From f38015f470a5fe323cfc71f711f11677dcc80599 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 3 Aug 2024 11:39:59 -0400 Subject: [PATCH 001/548] codegen: NFC refactoring to use Align type --- src/ccall.cpp | 10 +++++----- src/cgutils.cpp | 41 ++++++++++++++++++++--------------------- src/codegen.cpp | 17 ++++++++++------- src/intrinsics.cpp | 12 ++++++------ 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 3c2857608c163..d913ed4e27e7d 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -554,8 +554,8 @@ static Value *julia_to_native( // pass the address of an alloca'd thing, not a box // since those are immutable. Value *slot = emit_static_alloca(ctx, to); - unsigned align = julia_alignment(jlto); - cast(slot)->setAlignment(Align(align)); + Align align(julia_alignment(jlto)); + cast(slot)->setAlignment(align); setName(ctx.emission_context, slot, "native_convert_buffer"); if (!jvinfo.ispointer()) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); @@ -2230,7 +2230,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( Value *strct = emit_allocobj(ctx, (jl_datatype_t*)rt, true); setName(ctx.emission_context, strct, "ccall_ret_box"); MDNode *tbaa = jl_is_mutable(rt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; - int boxalign = julia_alignment(rt); + Align boxalign(julia_alignment(rt)); // copy the data from the return value to the new struct const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); auto resultTy = result->getType(); @@ -2240,8 +2240,8 @@ jl_cgval_t function_sig_t::emit_a_ccall( // When this happens, cast through memory. auto slot = emit_static_alloca(ctx, resultTy); setName(ctx.emission_context, slot, "type_pun_slot"); - slot->setAlignment(Align(boxalign)); - ctx.builder.CreateAlignedStore(result, slot, Align(boxalign)); + slot->setAlignment(boxalign); + ctx.builder.CreateAlignedStore(result, slot, boxalign); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); emit_memcpy(ctx, strct, ai, slot, ai, rtsz, boxalign, boxalign); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 613d7ae719448..297fcb164b112 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -315,7 +315,7 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V) } static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt); -static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile=false); +static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, Align alignment, bool isVolatile=false); static bool type_is_permalloc(jl_value_t *typ) { @@ -1006,11 +1006,10 @@ static Value *data_pointer(jl_codectx_t &ctx, const jl_cgval_t &x) } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, uint64_t sz, unsigned align_dst, unsigned align_src, bool is_volatile) + jl_aliasinfo_t const &src_ai, uint64_t sz, Align align_dst, Align align_src, bool is_volatile) { if (sz == 0) return; - assert(align_dst && "align must be specified"); #if JL_LLVM_VERSION < 170000 // If the types are small and simple, use load and store directly. // Going through memcpy can cause LLVM (e.g. SROA) to create bitcasts between float and int @@ -1053,7 +1052,7 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const if (isa(dst) && !dst->hasName()) setName(ctx.emission_context, dst, "memcpy_refined_dst"); auto val = src_ai.decorateInst(ctx.builder.CreateAlignedLoad(directel, src, MaybeAlign(align_src), is_volatile)); - dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, Align(align_dst), is_volatile)); + dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, align_dst, is_volatile)); ++SkippedMemcpys; return; } @@ -1072,12 +1071,12 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const // above problem won't be as serious. auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, Align(align_dst), src, Align(align_src), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, align_dst, src, align_src, sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, Value *sz, unsigned align_dst, unsigned align_src, bool is_volatile) + jl_aliasinfo_t const &src_ai, Value *sz, Align align_dst, Align align_src, bool is_volatile) { if (auto const_sz = dyn_cast(sz)) { emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, const_sz->getZExtValue(), align_dst, align_src, is_volatile); @@ -1086,20 +1085,20 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const ++EmittedMemcpys; auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, MaybeAlign(align_dst), src, MaybeAlign(align_src), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, align_dst, src, align_src, sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) + jl_aliasinfo_t const &src_ai, T1 &&sz, Align align_dst, Align align_src, bool is_volatile=false) { emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, sz, align_dst, align_src, is_volatile); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, const jl_cgval_t &src, - T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) + T1 &&sz, Align align_dst, Align align_src, bool is_volatile=false) { auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, src.tbaa); emit_memcpy_llvm(ctx, dst, dst_ai, data_pointer(ctx, src), src_ai, sz, align_dst, align_src, is_volatile); @@ -1999,7 +1998,7 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j else if (!alignment) alignment = julia_alignment(jltype); if (intcast && Order == AtomicOrdering::NotAtomic) { - emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, alignment, intcast->getAlign().value()); + emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, Align(alignment), intcast->getAlign()); } else { if (!isboxed && jl_is_genericmemoryref_type(jltype)) { @@ -2176,7 +2175,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, } else { assert(Order == AtomicOrdering::NotAtomic && !isboxed && rhs.typ == jltype); - emit_unbox_store(ctx, rhs, ptr, tbaa, alignment); + emit_unbox_store(ctx, rhs, ptr, tbaa, Align(alignment)); } } else if (isswapfield) { @@ -2329,7 +2328,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, } else { assert(!isboxed && rhs.typ == jltype); - emit_unbox_store(ctx, rhs, ptr, tbaa, alignment); + emit_unbox_store(ctx, rhs, ptr, tbaa, Align(alignment)); } ctx.builder.CreateBr(DoneBB); instr = load; @@ -2668,7 +2667,7 @@ static jl_cgval_t emit_unionload(jl_codectx_t &ctx, Value *addr, Value *ptindex, if (al > 1) lv->setAlignment(Align(al)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - emit_memcpy(ctx, lv, ai, addr, ai, fsz, al, al); + emit_memcpy(ctx, lv, ai, addr, ai, fsz, Align(al), Align(al)); addr = lv; } return mark_julia_slot(fsz > 0 ? addr : nullptr, jfty, tindex, tbaa); @@ -3104,11 +3103,11 @@ static Value *emit_genericmemoryowner(jl_codectx_t &ctx, Value *t) static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt, bool fully_initialized); static void init_bits_value(jl_codectx_t &ctx, Value *newv, Value *v, MDNode *tbaa, - unsigned alignment = sizeof(void*)) // min alignment in julia's gc is pointer-aligned + Align alignment = Align(sizeof(void*))) // min alignment in julia's gc is pointer-aligned { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); // newv should already be tagged - ai.decorateInst(ctx.builder.CreateAlignedStore(v, newv, Align(alignment))); + ai.decorateInst(ctx.builder.CreateAlignedStore(v, newv, alignment)); } static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, MDNode *tbaa) @@ -3116,7 +3115,7 @@ static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, // newv should already be tagged if (v.ispointer()) { unsigned align = std::max(julia_alignment(v.typ), (unsigned)sizeof(void*)); - emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), align, julia_alignment(v.typ)); + emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), Align(align), Align(julia_alignment(v.typ))); } else { init_bits_value(ctx, newv, v.V, tbaa); @@ -3582,7 +3581,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con if (jl_is_pointerfree(typ)) { unsigned alignment = julia_alignment(typ); if (!src.ispointer() || src.constant) { - emit_unbox_store(ctx, src, dest, tbaa_dst, alignment, isVolatile); + emit_unbox_store(ctx, src, dest, tbaa_dst, Align(alignment), isVolatile); } else { Value *src_ptr = data_pointer(ctx, src); @@ -3592,7 +3591,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con // if (skip) src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); auto f = [&] { (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, Align(alignment), Align(alignment), isVolatile); return nullptr; }; if (skip) @@ -3627,7 +3626,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con return; } else { emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, Align(alignment), Align(alignment), isVolatile); } } ctx.builder.CreateBr(postBB); @@ -3653,7 +3652,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con Value *datatype = emit_typeof(ctx, src, false, false); Value *copy_bytes = emit_datatype_size(ctx, datatype); (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), data_pointer(ctx, src), - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, 1, 1, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, Align(1), Align(1), isVolatile); return nullptr; }; if (skip) @@ -4046,7 +4045,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } else if (init_as_value) { fval = emit_unbox(ctx, fty, fval_info, jtype); } else { - emit_unbox_store(ctx, fval_info, dest, ctx.tbaa().tbaa_stack, jl_field_align(sty, i)); + emit_unbox_store(ctx, fval_info, dest, ctx.tbaa().tbaa_stack, Align(jl_field_align(sty, i))); } } if (init_as_value) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 875beb7c287dc..d8d7a1fd23814 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5652,7 +5652,7 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va else { const DataLayout &DL = jl_Module->getDataLayout(); uint64_t sz = DL.getTypeStoreSize(T); - emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign().value(), varslot->getAlign().value()); + emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign(), varslot->getAlign()); } Value *tindex = NULL; if (vi.pTIndex) @@ -5756,8 +5756,9 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu // This check should probably mostly catch the relevant situations. if (vi.value.V != rval_info.V) { Value *copy_bytes = ConstantInt::get(getInt32Ty(ctx.builder.getContext()), jl_datatype_size(vi.value.typ)); + Align alignment(julia_alignment(rval_info.typ)); emit_memcpy(ctx, vi.value.V, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), rval_info, copy_bytes, - julia_alignment(rval_info.typ), julia_alignment(rval_info.typ), vi.isVolatile); + alignment, alignment, vi.isVolatile); } } else { @@ -6868,8 +6869,9 @@ static void emit_cfunc_invalidate( root1 = ctx.builder.CreateConstInBoundsGEP2_32(get_returnroots_type(ctx, return_roots), root1, 0, 0); ctx.builder.CreateStore(gf_ret, root1); } + Align alignment(julia_alignment(rettype)); emit_memcpy(ctx, &*gf_thunk->arg_begin(), jl_aliasinfo_t::fromTBAA(ctx, nullptr), gf_ret, - jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), julia_alignment(rettype), julia_alignment(rettype)); + jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), Align(alignment), Align(alignment)); ctx.builder.CreateRetVoid(); break; } @@ -8729,7 +8731,7 @@ static jl_llvm_functions_t jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); ctx.world_age_at_entry = closure_world.V; // The tls world in a OC is the world of the closure - emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr.value()); + emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); // Load closure env Value *envaddr = ctx.builder.CreateInBoundsGEP( @@ -9272,8 +9274,9 @@ static jl_llvm_functions_t } if (returninfo.cc == jl_returninfo_t::SRet) { assert(jl_is_concrete_type(jlrettype)); + Align alignment(julia_alignment(jlrettype)); emit_memcpy(ctx, sret, jl_aliasinfo_t::fromTBAA(ctx, nullptr), retvalinfo, - jl_datatype_size(jlrettype), julia_alignment(jlrettype), julia_alignment(jlrettype)); + jl_datatype_size(jlrettype), alignment, alignment); } else { // must be jl_returninfo_t::Union emit_unionmove(ctx, sret, nullptr, retvalinfo, /*skip*/isboxed_union); @@ -9511,7 +9514,7 @@ static jl_llvm_functions_t // load of val) if the runtime type of val isn't phiType Value *isvalid = emit_isa_and_defined(ctx, val, phiType); emit_guarded_test(ctx, isvalid, nullptr, [&] { - emit_unbox_store(ctx, update_julia_type(ctx, val, phiType), dest, ctx.tbaa().tbaa_stack, julia_alignment(phiType)); + emit_unbox_store(ctx, update_julia_type(ctx, val, phiType), dest, ctx.tbaa().tbaa_stack, Align(julia_alignment(phiType))); return nullptr; }); } @@ -9538,7 +9541,7 @@ static jl_llvm_functions_t if (VN) V = Constant::getNullValue(ctx.types().T_prjlvalue); if (dest) - emit_unbox_store(ctx, val, dest, ctx.tbaa().tbaa_stack, julia_alignment(val.typ)); + emit_unbox_store(ctx, val, dest, ctx.tbaa().tbaa_stack, Align(julia_alignment(val.typ))); RTindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), tindex); } } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index ad89abf6ca1a2..23d272ea75e7f 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -405,7 +405,7 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { assert(DL.getTypeSizeInBits(ty) == DL.getTypeSizeInBits(to)); - AllocaInst *cast = ctx.builder.CreateAlloca(ty); + AllocaInst *cast = emit_static_alloca(ctx, ty); setName(ctx.emission_context, cast, "coercion"); ctx.builder.CreateStore(unboxed, cast); unboxed = ctx.builder.CreateLoad(to, cast); @@ -498,7 +498,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va } // emit code to store a raw value into a destination -static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile) +static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest, MDNode *tbaa_dest, Align alignment, bool isVolatile) { if (x.isghost) { // this can happen when a branch yielding a different type ends @@ -510,7 +510,7 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest if (!x.ispointer()) { // already unboxed, but sometimes need conversion (e.g. f32 -> i32) assert(x.V); Value *unboxed = zext_struct(ctx, x.V); - StoreInst *store = ctx.builder.CreateAlignedStore(unboxed, dest, Align(alignment)); + StoreInst *store = ctx.builder.CreateAlignedStore(unboxed, dest, alignment); store->setVolatile(isVolatile); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest); ai.decorateInst(store); @@ -518,7 +518,7 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest } Value *src = data_pointer(ctx, x); - emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), alignment, julia_alignment(x.typ), isVolatile); + emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), Align(alignment), Align(julia_alignment(x.typ)), isVolatile); } static jl_datatype_t *staticeval_bitstype(const jl_cgval_t &targ) @@ -770,7 +770,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, ArrayRef argv) thePtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); setName(ctx.emission_context, thePtr, "pointerref_src"); MDNode *tbaa = best_tbaa(ctx.tbaa(), ety); - emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, sizeof(jl_value_t*), align_nb); + emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, Align(sizeof(jl_value_t*)), Align(align_nb)); return mark_julia_type(ctx, strct, true, ety); } else { @@ -847,7 +847,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) setName(ctx.emission_context, im1, "pointerset_offset"); auto gep = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); setName(ctx.emission_context, gep, "pointerset_ptr"); - emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, align_nb, julia_alignment(ety)); + emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, Align(align_nb), Align(julia_alignment(ety))); } else { bool isboxed; From e1e5a46b002a3979f6b217f1ecd7cede83bf45fd Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 3 Aug 2024 11:43:12 -0400 Subject: [PATCH 002/548] codegen: update type of x after type-assert Later code likes to see that the type is consistent with the cgval and the unbox. --- src/intrinsics.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 23d272ea75e7f..4bfe3f184d24b 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -799,7 +799,7 @@ static jl_cgval_t emit_runtime_pointerset(jl_codectx_t &ctx, ArrayRef argv) { const jl_cgval_t &e = argv[0]; - const jl_cgval_t &x = argv[1]; + jl_cgval_t x = argv[1]; const jl_cgval_t &i = argv[2]; const jl_cgval_t &align = argv[3]; @@ -822,6 +822,9 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) return jl_cgval_t(); } emit_typecheck(ctx, x, ety, "pointerset"); + x = update_julia_type(ctx, x, ety); + if (x.typ == jl_bottom_type) + return jl_cgval_t(); Value *idx = emit_unbox(ctx, ctx.types().T_size, i, (jl_value_t*)jl_long_type); Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); @@ -992,7 +995,7 @@ static jl_cgval_t emit_atomic_pointerop(jl_codectx_t &ctx, intrinsic f, ArrayRef bool ismodifyfield = f == atomic_pointermodify; const jl_cgval_t undefval; const jl_cgval_t &e = argv[0]; - const jl_cgval_t &x = isreplacefield || ismodifyfield ? argv[2] : argv[1]; + jl_cgval_t x = isreplacefield || ismodifyfield ? argv[2] : argv[1]; const jl_cgval_t &y = isreplacefield || ismodifyfield ? argv[1] : undefval; const jl_cgval_t &ord = isreplacefield || ismodifyfield ? argv[3] : argv[2]; const jl_cgval_t &failord = isreplacefield ? argv[4] : undefval; @@ -1034,8 +1037,12 @@ static jl_cgval_t emit_atomic_pointerop(jl_codectx_t &ctx, intrinsic f, ArrayRef emit_error(ctx, msg); return jl_cgval_t(); } - if (!ismodifyfield) + if (!ismodifyfield) { emit_typecheck(ctx, x, ety, std::string(jl_intrinsic_name((int)f))); + x = update_julia_type(ctx, x, ety); + if (x.typ == jl_bottom_type) + return jl_cgval_t(); + } size_t nb = jl_datatype_size(ety); if ((nb & (nb - 1)) != 0 || nb > MAX_POINTERATOMIC_SIZE) { From 8bfef8f1b60155020abc9bdab8aef9788eb458ba Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 1 Aug 2024 17:11:07 +0000 Subject: [PATCH 003/548] codegen: take gc roots (and alloca alignment) more seriously Due to limitations in the LLVM implementation, we are forced to emit fairly bad code here. But we need to make sure it is still correct with respect to GC rooting. The PR 50833c84d454ef989797e035294ba27b3cca79b7 also changed the meaning of haspadding without changing all of the existing users to use the new equivalent flag (haspadding || !isbitsegal), incurring additional breakage here as well and needing more tests for that. Fixes #54720 --- src/abi_aarch64.cpp | 8 ++-- src/abi_arm.cpp | 2 +- src/abi_ppc64le.cpp | 2 +- src/cgutils.cpp | 97 ++++++++++++++++++++++++++++----------------- src/codegen.cpp | 3 ++ src/datatype.c | 14 +++---- test/atomics.jl | 6 +++ 7 files changed, 83 insertions(+), 49 deletions(-) diff --git a/src/abi_aarch64.cpp b/src/abi_aarch64.cpp index 7c31b6606139a..0a193ee132556 100644 --- a/src/abi_aarch64.cpp +++ b/src/abi_aarch64.cpp @@ -16,7 +16,7 @@ struct ABI_AArch64Layout : AbiLayout { Type *get_llvm_vectype(jl_datatype_t *dt, LLVMContext &ctx) const { // Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt) - // `!dt->name->mutabl && dt->pointerfree && !dt->haspadding && dt->nfields > 0` + // `!dt->name->mutabl && dt->pointerfree && !dt->haspadding && dt->isbitsegal && dt->nfields > 0` if (dt->layout == NULL || jl_is_layout_opaque(dt->layout)) return nullptr; size_t nfields = dt->layout->nfields; @@ -62,7 +62,7 @@ Type *get_llvm_vectype(jl_datatype_t *dt, LLVMContext &ctx) const Type *get_llvm_fptype(jl_datatype_t *dt, LLVMContext &ctx) const { // Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt) - // `!dt->name->mutabl && dt->pointerfree && !dt->haspadding && dt->nfields == 0` + // `!dt->name->mutabl && dt->pointerfree && !dt->haspadding && dt->isbitsegal && dt->nfields == 0` Type *lltype; // Check size first since it's cheaper. switch (jl_datatype_size(dt)) { @@ -88,7 +88,7 @@ Type *get_llvm_fptype(jl_datatype_t *dt, LLVMContext &ctx) const Type *get_llvm_fp_or_vectype(jl_datatype_t *dt, LLVMContext &ctx) const { // Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt) - if (dt->name->mutabl || dt->layout->npointers || dt->layout->flags.haspadding) + if (dt->name->mutabl || dt->layout->npointers || !dt->layout->flags.isbitsegal || dt->layout->flags.haspadding) return nullptr; return dt->layout->nfields ? get_llvm_vectype(dt, ctx) : get_llvm_fptype(dt, ctx); } @@ -184,7 +184,7 @@ Type *isHFAorHVA(jl_datatype_t *dt, size_t &nele, LLVMContext &ctx) const // uniquely addressable members. // Maximum HFA and HVA size is 64 bytes (4 x fp128 or 16bytes vector) size_t dsz = jl_datatype_size(dt); - if (dsz > 64 || !dt->layout || dt->layout->npointers || dt->layout->flags.haspadding) + if (dsz > 64 || !dt->layout || dt->layout->npointers || !dt->layout->flags.isbitsegal || dt->layout->flags.haspadding) return NULL; nele = 0; ElementType eltype; diff --git a/src/abi_arm.cpp b/src/abi_arm.cpp index 68f980d7b40da..8839a37da6e13 100644 --- a/src/abi_arm.cpp +++ b/src/abi_arm.cpp @@ -82,7 +82,7 @@ size_t isLegalHA(jl_datatype_t *dt, Type *&base, LLVMContext &ctx) const if (jl_is_structtype(dt)) { // Fast path checks before descending the type hierarchy // (4 x 128b vector == 64B max size) - if (jl_datatype_size(dt) > 64 || dt->layout->npointers || dt->layout->flags.haspadding) + if (jl_datatype_size(dt) > 64 || dt->layout->npointers || !dt->layout->flags.isbitsegal || dt->layout->flags.haspadding) return 0; base = NULL; diff --git a/src/abi_ppc64le.cpp b/src/abi_ppc64le.cpp index 1f10817cfeeee..f02e1022ddc2d 100644 --- a/src/abi_ppc64le.cpp +++ b/src/abi_ppc64le.cpp @@ -44,7 +44,7 @@ struct ABI_PPC64leLayout : AbiLayout { // count the homogeneous floating aggregate size (saturating at max count of 8) unsigned isHFA(jl_datatype_t *ty, jl_datatype_t **ty0, bool *hva) const { - if (jl_datatype_size(ty) > 128 || ty->layout->npointers || ty->layout->flags.haspadding) + if (jl_datatype_size(ty) > 128 || ty->layout->npointers || !ty->layout->flags.isbitsegal || ty->layout->flags.haspadding) return 9; size_t i, l = ty->layout->nfields; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 297fcb164b112..08d51f52b613b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2119,12 +2119,15 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, FailOrder = AtomicOrdering::Monotonic; unsigned nb = isboxed ? sizeof(void*) : jl_datatype_size(jltype); AllocaInst *intcast = nullptr; + Type *intcast_eltyp = nullptr; + bool tracked_pointers = isboxed || CountTrackedPointers(elty).count > 0; if (!isboxed && Order != AtomicOrdering::NotAtomic && !elty->isIntOrPtrTy()) { + intcast_eltyp = elty; + elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); if (!issetfield) { intcast = emit_static_alloca(ctx, elty); setName(ctx.emission_context, intcast, "atomic_store_box"); } - elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); } Type *realelty = elty; if (Order != AtomicOrdering::NotAtomic && isa(elty)) { @@ -2133,14 +2136,20 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb2); } Value *r = nullptr; - if (issetfield || isswapfield || isreplacefield || issetfieldonce) { - if (isboxed) + if (issetfield || isswapfield || isreplacefield || issetfieldonce) { // e.g. !ismodifyfield + assert(isboxed || rhs.typ == jltype); + if (isboxed) { r = boxed(ctx, rhs); - else if (aliasscope || Order != AtomicOrdering::NotAtomic || CountTrackedPointers(realelty).count) { + } + else if (intcast) { + emit_unbox_store(ctx, rhs, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + r = ctx.builder.CreateLoad(realelty, intcast); + } + else if (aliasscope || Order != AtomicOrdering::NotAtomic || tracked_pointers) { r = emit_unbox(ctx, realelty, rhs, jltype); - if (realelty != elty) - r = ctx.builder.CreateZExt(r, elty); } + if (realelty != elty) + r = ctx.builder.CreateZExt(r, elty); } if (isboxed) alignment = sizeof(void*); @@ -2222,7 +2231,14 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, Current->addIncoming(instr, SkipBB); ctx.builder.SetInsertPoint(BB); } - Compare = emit_unbox(ctx, realelty, cmp, jltype); + cmp = update_julia_type(ctx, cmp, jltype); + if (intcast) { + emit_unbox_store(ctx, cmp, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + Compare = ctx.builder.CreateLoad(realelty, intcast); + } + else { + Compare = emit_unbox(ctx, realelty, cmp, jltype); + } if (realelty != elty) Compare = ctx.builder.CreateZExt(Compare, elty); } @@ -2269,16 +2285,17 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (realelty != elty) realCompare = ctx.builder.CreateTrunc(realCompare, realelty); if (intcast) { + assert(!isboxed); ctx.builder.CreateStore(realCompare, intcast); - if (maybe_null_if_boxed) - realCompare = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + if (tracked_pointers) + realCompare = ctx.builder.CreateLoad(intcast_eltyp, intcast); } - if (maybe_null_if_boxed) { - Value *first_ptr = isboxed ? Compare : extract_first_ptr(ctx, Compare); - if (first_ptr) - null_load_check(ctx, first_ptr, mod, var); + if (maybe_null_if_boxed && tracked_pointers) { + Value *first_ptr = isboxed ? realCompare : extract_first_ptr(ctx, realCompare); + assert(first_ptr); + null_load_check(ctx, first_ptr, mod, var); } - if (intcast) + if (intcast && !tracked_pointers) oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); else oldval = mark_julia_type(ctx, realCompare, isboxed, jltype); @@ -2286,11 +2303,18 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (isboxed) { r = boxed(ctx, rhs); } - else if (Order != AtomicOrdering::NotAtomic || CountTrackedPointers(realelty).count) { + else if (intcast) { + emit_unbox_store(ctx, rhs, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); + r = ctx.builder.CreateLoad(realelty, intcast); + if (!tracked_pointers) // oldval is a slot, so put the oldval back + ctx.builder.CreateStore(realCompare, intcast); + } + else if (Order != AtomicOrdering::NotAtomic) { + assert(!tracked_pointers); r = emit_unbox(ctx, realelty, rhs, jltype); - if (realelty != elty) - r = ctx.builder.CreateZExt(r, elty); } + if (realelty != elty) + r = ctx.builder.CreateZExt(r, elty); if (needlock) emit_lockstate_value(ctx, needlock, true); cmp = oldval; @@ -2356,9 +2380,10 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, realinstr = ctx.builder.CreateTrunc(realinstr, realelty); if (intcast) { ctx.builder.CreateStore(realinstr, intcast); + // n.b. this oldval is only used for emit_f_is in this branch, so we know a priori that it does not need a gc-root oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); if (maybe_null_if_boxed) - realinstr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + realinstr = ctx.builder.CreateLoad(intcast_eltyp, intcast); } else { oldval = mark_julia_type(ctx, realinstr, isboxed, jltype); @@ -2398,20 +2423,23 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ctx.builder.SetInsertPoint(DoneBB); if (needlock) emit_lockstate_value(ctx, needlock, false); - if (parent != NULL) { + if (parent != NULL && r && tracked_pointers && (!isboxed || !type_is_permalloc(rhs.typ))) { if (isreplacefield || issetfieldonce) { - // TODO: avoid this branch if we aren't making a write barrier BasicBlock *BB = BasicBlock::Create(ctx.builder.getContext(), "xchg_wb", ctx.f); DoneBB = BasicBlock::Create(ctx.builder.getContext(), "done_xchg_wb", ctx.f); ctx.builder.CreateCondBr(Success, BB, DoneBB); ctx.builder.SetInsertPoint(BB); } - if (r) { - if (!isboxed) - emit_write_multibarrier(ctx, parent, r, rhs.typ); - else if (!type_is_permalloc(rhs.typ)) - emit_write_barrier(ctx, parent, r); + if (realelty != elty) + r = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, r, realelty)); + if (intcast) { + ctx.builder.CreateStore(r, intcast); + r = ctx.builder.CreateLoad(intcast_eltyp, intcast); } + if (!isboxed) + emit_write_multibarrier(ctx, parent, r, rhs.typ); + else if (!type_is_permalloc(rhs.typ)) + emit_write_barrier(ctx, parent, r); if (isreplacefield || issetfieldonce) { ctx.builder.CreateBr(DoneBB); ctx.builder.SetInsertPoint(DoneBB); @@ -2430,21 +2458,18 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, instr = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, instr, realelty)); if (intcast) { ctx.builder.CreateStore(instr, intcast); - instr = nullptr; + if (tracked_pointers) + instr = ctx.builder.CreateLoad(intcast_eltyp, intcast); } - if (maybe_null_if_boxed) { - if (intcast) - instr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + if (maybe_null_if_boxed && tracked_pointers) { Value *first_ptr = isboxed ? instr : extract_first_ptr(ctx, instr); - if (first_ptr) - null_load_check(ctx, first_ptr, mod, var); - if (intcast && !first_ptr) - instr = nullptr; + assert(first_ptr); + null_load_check(ctx, first_ptr, mod, var); } - if (instr) - oldval = mark_julia_type(ctx, instr, isboxed, jltype); - else + if (intcast && !tracked_pointers) oldval = mark_julia_slot(intcast, jltype, NULL, ctx.tbaa().tbaa_stack); + else + oldval = mark_julia_type(ctx, instr, isboxed, jltype); if (isreplacefield) { Success = ctx.builder.CreateZExt(Success, getInt8Ty(ctx.builder.getContext())); const jl_cgval_t argv[2] = {oldval, mark_julia_type(ctx, Success, false, jl_bool_type)}; diff --git a/src/codegen.cpp b/src/codegen.cpp index d8d7a1fd23814..ef18d9bd1b673 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3374,11 +3374,14 @@ static size_t emit_masked_bits_compare(callback &emit_desc, jl_datatype_t *aty, size_t padding_bytes = 0; size_t nfields = jl_datatype_nfields(aty); size_t total_size = jl_datatype_size(aty); + assert(aty->layout->flags.isbitsegal); for (size_t i = 0; i < nfields; ++i) { size_t offset = jl_field_offset(aty, i); size_t fend = i == nfields - 1 ? total_size : jl_field_offset(aty, i + 1); size_t fsz = jl_field_size(aty, i); jl_datatype_t *fty = (jl_datatype_t*)jl_field_type(aty, i); + assert(jl_is_datatype(fty)); // union fields should never reach here + assert(fty->layout->flags.isbitsegal); if (jl_field_isptr(aty, i) || !fty->layout->flags.haspadding) { // The field has no internal padding data_bytes += fsz; diff --git a/src/datatype.c b/src/datatype.c index e7ee15a63f56e..e88977dd67400 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1250,7 +1250,7 @@ JL_DLLEXPORT int jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_value_t *y /* pre- } else if (nb == 1) { uint8_t *y8 = (uint8_t*)y; - assert(!dt->layout->flags.haspadding); + assert(dt->layout->flags.isbitsegal && !dt->layout->flags.haspadding); if (dt == et) { *y8 = *(uint8_t*)expected; uint8_t z8 = *(uint8_t*)src; @@ -1263,7 +1263,7 @@ JL_DLLEXPORT int jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_value_t *y /* pre- } else if (nb == 2) { uint16_t *y16 = (uint16_t*)y; - assert(!dt->layout->flags.haspadding); + assert(dt->layout->flags.isbitsegal && !dt->layout->flags.haspadding); if (dt == et) { *y16 = *(uint16_t*)expected; uint16_t z16 = *(uint16_t*)src; @@ -1281,7 +1281,7 @@ JL_DLLEXPORT int jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_value_t *y /* pre- uint32_t z32 = zext_read32(src, nb); while (1) { success = jl_atomic_cmpswap((_Atomic(uint32_t)*)dst, y32, z32); - if (success || !dt->layout->flags.haspadding || !jl_egal__bits(y, expected, dt)) + if (success || (dt->layout->flags.isbitsegal && !dt->layout->flags.haspadding) || !jl_egal__bits(y, expected, dt)) break; } } @@ -1298,7 +1298,7 @@ JL_DLLEXPORT int jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_value_t *y /* pre- uint64_t z64 = zext_read64(src, nb); while (1) { success = jl_atomic_cmpswap((_Atomic(uint64_t)*)dst, y64, z64); - if (success || !dt->layout->flags.haspadding || !jl_egal__bits(y, expected, dt)) + if (success || (dt->layout->flags.isbitsegal && !dt->layout->flags.haspadding) || !jl_egal__bits(y, expected, dt)) break; } } @@ -1316,7 +1316,7 @@ JL_DLLEXPORT int jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_value_t *y /* pre- jl_uint128_t z128 = zext_read128(src, nb); while (1) { success = jl_atomic_cmpswap((_Atomic(jl_uint128_t)*)dst, y128, z128); - if (success || !dt->layout->flags.haspadding || !jl_egal__bits(y, expected, dt)) + if (success || (dt->layout->flags.isbitsegal && !dt->layout->flags.haspadding) || !jl_egal__bits(y, expected, dt)) break; } } @@ -2010,7 +2010,7 @@ inline jl_value_t *modify_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_ else { char *px = lock(p, parent, needlock, isatomic); int success = memcmp(px, (char*)r, fsz) == 0; - if (!success && ((jl_datatype_t*)rty)->layout->flags.haspadding) + if (!success && (!((jl_datatype_t*)rty)->layout->flags.isbitsegal || ((jl_datatype_t*)rty)->layout->flags.haspadding)) success = jl_egal__bits((jl_value_t*)px, r, (jl_datatype_t*)rty); if (success) { if (isunion) { @@ -2125,7 +2125,7 @@ inline jl_value_t *replace_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value success = (rty == jl_typeof(expected)); if (success) { success = memcmp((char*)r, (char*)expected, rsz) == 0; - if (!success && ((jl_datatype_t*)rty)->layout->flags.haspadding) + if (!success && (!((jl_datatype_t*)rty)->layout->flags.isbitsegal || ((jl_datatype_t*)rty)->layout->flags.haspadding)) success = jl_egal__bits(r, expected, (jl_datatype_t*)rty); } *((uint8_t*)r + fsz) = success ? 1 : 0; diff --git a/test/atomics.jl b/test/atomics.jl index 3df9e7d0f63c0..165c8dc4e2dfc 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -129,6 +129,7 @@ test_field_operators(ARefxy{Any}(123_10, 123_20)) test_field_operators(ARefxy{Union{Nothing,Int}}(123_10, nothing)) test_field_operators(ARefxy{Complex{Int32}}(123_10, 123_20)) test_field_operators(ARefxy{Complex{Int128}}(123_10, 123_20)) +test_field_operators(ARefxy{Complex{Real}}(123_10, 123_20)) test_field_operators(ARefxy{PadIntA}(123_10, 123_20)) test_field_operators(ARefxy{PadIntB}(123_10, 123_20)) #FIXME: test_field_operators(ARefxy{Int24}(123_10, 123_20)) @@ -317,6 +318,7 @@ test_field_orderings(ARefxy{Any}(true, false), true, false) test_field_orderings(ARefxy{Union{Nothing,Missing}}(nothing, missing), nothing, missing) test_field_orderings(ARefxy{Union{Nothing,Int}}(nothing, 123_1), nothing, 123_1) test_field_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_field_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) test_field_orderings(10.0, 20.0) test_field_orderings(NaN, Inf) @@ -568,6 +570,7 @@ test_global_operators(Any) test_global_operators(Union{Nothing,Int}) test_global_operators(Complex{Int32}) test_global_operators(Complex{Int128}) +test_global_operators(Complex{Real}) test_global_operators(PadIntA) test_global_operators(PadIntB) #FIXME: test_global_operators(Int24) @@ -691,6 +694,7 @@ test_global_orderings(Any, true, false) test_global_orderings(Union{Nothing,Missing}, nothing, missing) test_global_orderings(Union{Nothing,Int}, nothing, 123_1) test_global_orderings(Complex{Int128}, Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_global_orderings(Complex{Real}, Complex{Real}(10, 30), Complex{Real}(20, 40)) test_global_orderings(Float64, 10.0, 20.0) test_global_orderings(Float64, NaN, Inf) @@ -844,6 +848,7 @@ test_memory_operators(Any) test_memory_operators(Union{Nothing,Int}) test_memory_operators(Complex{Int32}) test_memory_operators(Complex{Int128}) +test_memory_operators(Complex{Real}) test_memory_operators(PadIntA) test_memory_operators(PadIntB) #FIXME: test_memory_operators(Int24) @@ -1031,6 +1036,7 @@ test_memory_orderings(Any, true, false) test_memory_orderings(Union{Nothing,Missing}, nothing, missing) test_memory_orderings(Union{Nothing,Int}, nothing, 123_1) test_memory_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) +test_memory_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) test_memory_orderings(10.0, 20.0) test_memory_orderings(NaN, Inf) From 2a56b7876be5d9364f2b187828e82f0cef0609d0 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Sun, 4 Aug 2024 10:05:44 +0100 Subject: [PATCH 004/548] Bump LLVM to v18 (#54848) Co-authored-by: Gabriel Baraldi --- deps/checksums/clang | 216 +++++++------- deps/checksums/lld | 216 +++++++------- deps/checksums/llvm | 436 ++++++++++++++--------------- deps/clang.version | 2 +- deps/lld.version | 2 +- deps/llvm-tools.version | 4 +- deps/llvm.version | 12 +- src/aotcompile.cpp | 70 ++--- src/ccall.cpp | 2 +- src/clangsa/GCChecker.cpp | 12 +- src/codegen.cpp | 6 +- src/coverage.cpp | 2 +- src/datatype.c | 4 + src/disasm.cpp | 4 + src/jitlayers.cpp | 45 ++- src/jitlayers.h | 5 + src/julia.h | 18 +- src/julia_internal.h | 22 ++ src/llvm-cpufeatures.cpp | 2 +- src/llvm-multiversioning.cpp | 6 +- src/llvm-simdloop.cpp | 2 +- src/llvm_api.cpp | 12 +- src/pipeline.cpp | 15 +- src/processor_arm.cpp | 2 +- stdlib/LLD_jll/Project.toml | 4 +- stdlib/libLLVM_jll/Project.toml | 2 +- test/llvmcall.jl | 6 +- test/llvmpasses/pipeline-prints.ll | 6 +- 28 files changed, 594 insertions(+), 541 deletions(-) diff --git a/deps/checksums/clang b/deps/checksums/clang index ee3dc2125ea30..7dc297db9c05b 100644 --- a/deps/checksums/clang +++ b/deps/checksums/clang @@ -1,108 +1,108 @@ -Clang.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/5a9351db0940c66e9646e0f3d6f37e1a -Clang.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/bf344cfe91795cfc4419ea9ec50df99237b64c57e0b81655a957b15ecc5b16f0134daf189f18fe34274df8de679d407b36f82e3723e80428afb456215a5b9a20 -Clang.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/md5/6c7461a52e07a1e3ecf9911784bb26cb -Clang.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/sha512/ce63de6405cd7c34d640afb259de8056db175e55bece923ce53c39b88dccc2885de70f4c598b3282102754b0c7cf6ac602e827968b6509fd7affa20ecf07d1a5 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/5446f22e4aba17482c057ee79beb2086 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/372be10dd8752821c751e571a5a9fc4af328285dcad6f2213f6e3d54f819405c26f1a1cb1e712d4bffebe3a42ca0736903d59ba70602a8ddd96b9e6fdfb9bacf -Clang.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/ca146aa3731ef24300c8398ddfb7ffd4 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/2459729a3af81fb962c7491ff16209fe6b65f4ef2f8323857b7c548da506d227a42a9b3301b8c8465cff66bbc9acbff2ac3e86d1a8560b9cb701b133317cd730 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/68c478b00a6cb1ecf700b54c86acc584 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/669af6e27ee67ea1be7f70cc328193d6139161264e1f6ef85c31c62523997246050d8b8aa241b257a191a9566df1f3ea620641c676908b817d5dfe7519ba0c8e -Clang.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/a299ea50c4862dcd0832cc483aa9c172 -Clang.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/e42e2e09be2872d2e1c57a46099b92847873dd4ebc87801cbd5fc171bbb236ef8bb815c4fd481d4a804fd6604fcb3cee7ee6fa66e712cc7a2fc2434d0649932a -Clang.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/804ec7eeb6b2fd8fb6ad9537bebc0f3e -Clang.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/36cda1267528e54658b28d31ed4218ecb11c2a05e161faf80030a899d2cb1d1ed145bdf19f7565853277230428d521a2b33759df09e0e799226b44d477c2de46 -Clang.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/4fd80844867e14a1a245c2ed911942bc -Clang.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/acc6a4d240b54682425f40d8c5b6e3578d818b03430696c3c90cde8ad8474de5692468379cfc7f4d826bd44a6fb38cdc036df9d31833d09477fb69ae569e160c -Clang.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/1774c2ed22a44aab72d7cf58c8a85ab0 -Clang.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/686b2bd8af2f47b03b25e23ac2d2954e5b9ccdcd942fcc6ccb9b96240a1fe414ac73d88f173dfdf93f45eb609e99299041a6a976a90b7afc6e49b698b6403a94 -Clang.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/e5e0d42647b5b50102f68e76823be11f -Clang.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/72b112ab6714d67c33088c978420f996a45c6fd1900ab1725908792a5b8a0f0407ecf53817761059fa4bebd81a443680c8cd1c25633a2eb702986a23072d7b70 -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/9601966aed6cdf5af9b6da24bf088ef8 -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/c70448ed703f74c0f82c43ba35bf3a1f4f02832e42bea795b5ae0caae1e3b8aa0fdd914b3a6753a68b9f0a7d3a5156e56fad35bd6fff15bc5566f74db26ce3ca -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/3aa7b1d67c252aab4cfb85a665ecf194 -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/7575858507e07b94001200c4d4e9875a2a4c8b97074655a1c1c03edd8cd2bbe853cca3a518d36d6f3243649839aa53d4dbe2e637eaf9b93873b2dd82266d4e17 -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/7729c1ebdde07122fd0f9ae63a14c34f -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/04fe96e57a5351c3265441b73919900f1d19220747aa8ac061ef15531ee3f6bd62a70a4405c615c3b14975250b039d81fabde3b351c6e2118e684ca479eacbaa -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/37536e1d18b6b0a948b83ebcee530af0 -Clang.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/d94c3bd7afbca9dc81bb10a4d0e041165e63a2ac9dfcc1483bd55da1091c664074e9a26c9972da23adca3f711ffd22ba517546f0f0e58b717f3043973def0e97 -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/ded5fe5d4de1c3bfd0fc75494c44cb11 -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/85f6df75ed2b67fe9dcdea745ac1e659c0335c17c54b03c964d6996e926303fbf14c1c2ed6b369ffa38bde827c9894c32de82afa562673dad3268c43985dc7ce -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/e4eac3b4bfa0e3b825cd23f88738bd3a -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/c0552d1c9dbfe73b07f3ea2907d879c1142e8f6db6530297ee977c2b23858a9f55674f635a4746262e7595af2ca41a752a6abb4944e6707f4daa3a8c0715df83 -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/4b5f66f39069204ba7281740115b7ef7 -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/4c789017ec2bd98e808d198a8baefb2405222efb6e93eb5d1b8944dbd1e587afc41ebaf506b0aed5def5eb4c815ef4c822e0e0477b4aaac35fc03f9850853be3 -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/95310b525b635b8c6550226e2b9fe02b -Clang.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/faa62d41124b92d829658397eaada3ee8ce41f2e36e7f9954bd3fdd882a1232bf3431f36c9b7e65c17ae4f228da9ac37e1db0b1ae43a8540765186a230c65bab -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/5886d8e09d76ed74c2293c0af8be413f -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/ca66bb3bd39b7643cf0dcb551a6bb7a3293f4c99f8d4ae1fc16eb66a0f0da0ef10acae52169b2522dc2fdebc1f550d2d36b87bb25d9b1c9df0a8f0d5089c7642 -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/7b08c562565e408d716898bf37e44eda -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/e9051454cc15a7879d90e6b36eeaf4c956e328be7823a1fa37cb98197c0fb4dddb9aaa8cf7aedd35e0affa9e6876b79f9a1160da1ec4d26ea7c775db58293dd2 -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/dc816bd807c9d131e088c30caad9d326 -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/5700dcef831b52abf64cc9120352918c302301e19ecf6ac64aa2cfb6270f7b2c82fe3f0f1d3281539081db7d520e2301995d992b9e8234cf64d7ec88126f4bc8 -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/971caa23440c190b085300f4cd67b080 -Clang.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/5ce854fee306c14ef7964e3ab9831e816c6eefab637221b71be2187f42e7b089c1dd9e92754ab5ee3198bb3c0e84da9a2cc15c2d6afcb086f61b897cfc320ab3 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/5ad3e0a391db624713263226259b55f0 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/ed4cf7c241bbaca9f887cfb81caca687e3f30d01922e05c72b435c6333b334fa4f708193b8c85de9777f9912fcd8a55b1d7a6cd1aef00b913f0d0f1439e9ed6f -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/5d487035d147bc48655e5538f08afbdf -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/f26d96241b9b18609c1d4148e30048d73faed24fe3f623a5d2bc6aaa59a644cc97201acfbde2eed4daecc79602e6d13256e112c8b821b6d865d071db957268a2 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/fdcf70b4514c3d63498a3fa46a2525c2 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/d481c807405302eabc612075e22acc9f7d1cbdbb17de23b6a129dfba60c265eacf20cc2d48d5b4087979b1184a783bcd0bf6ed326060e5ef05119556f21a5a95 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/a8a04c92d74dcd22f980956d2b7ccb71 -Clang.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/af3ad3efc2941b98ca4ec1340e24beb1c1f1c5d2248da3000af3f2e7184df013b55127040cfd03a63acd461acdb4f1afcc6b11f1ad11502aa86f737629c185a2 -Clang.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/15d02f2f91fcdd52d641991d58b15c9b -Clang.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/e1903be2164fb3002a93829a295d2a413c14faa2a0fad2297763a6cbd63ec0bcc37689cbec4c0f0bd0f4eb4cddc716f78d57c95f7ed29145ffed3b7c50a98d04 -Clang.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/cfaab26c1c8409362a267484c2ccfbc1 -Clang.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/2f9bb137df4666f9c2947a74a4a06489d477b5093c3b0acae11d6c1213c467e258aaa360183f8b18ca28778773a5170f5dd19ea3622294f0d715a5909c6d06ed -Clang.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/6bb27685277eae5289f782657925c33a -Clang.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/35bd7b862b2a2aa372be7f0bb01aa65dc58f71d2218833ca39f832a25c3162b3282c9806f3cdc4a9a2a7bc11167a6daec30b688b979f1a38f49fa412e4628648 -Clang.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/f44f9fdebc843c5947d1777e53c4189f -Clang.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/09d1f9a29bdd04e598622cca11e0a82ef6a68f0ac9d4e6fada548482fff6219cef5714bdd3d02d1c0944de14ef14991ee0eab9b4f54c4efacae330a9e4ac47dc -Clang.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/0d7c790218fe40d1f87850197a5d08df -Clang.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/fac636c5ede5baf7d5c706f348b8992d4bf0042cb34decb83964385d9877b7555db998fc79cd7f032d80b3572275f13fbc36ccf5a76e573221266e1ee339ec76 -Clang.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/5d4f5b78645f75b93e76a75efdad721f -Clang.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/a013697f1da103a1202a1728ebfc61ec0d08e705e0caa253cd14430abfa3d47a7b43930d3d9d70d995dbb1e5f78eebd02ef8cbfd9b028b447a3b7953d437a60d -Clang.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/8d9b46562cefc0ce9b7dfd6022cb914c -Clang.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/95d8cd716bfbff69d336987a3ff0f65e28f48544679cf6bd165319cd5382f0eb9d5be119917a5b309e7544e43ac7c52f1370d159e67f18ff2eda06cf7bad31f5 -Clang.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/86bf7e43fa750d620495eb73c938273c -Clang.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/1a014fa2ec455fee7be9413fa1db901360e5728bcfffb7bb76fd3b30b00120883c91f4ebfcfe048e5f372bdcc18a2a45744ddb1e8c7e303d5952af49e386caff -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/c125dafc105894bb0bb821bb7b28ce3a -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/6da75d2fdc18ba95dd3db9181401a4ac0b7f8e465ad872f95f2e9db49701cc56da7c13f6ca69b01e15832f9bf23cd698ca5dcb28dcf775edef6bd5728ca669ca -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/4b9d2090af04573a35c0d80833f9212d -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/7e9231e286f15b4d90318a732d1fae3130a1d6714f6cf868f5d3472068b719818e4d2a63dfbb2056e1f3e7f2a25250c4de1f0629e459b36f7d8e1e42286470c0 -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/7762d01fc07748336997bee900003653 -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/c7334dfaa5317b89a58beb0b397a2784036c98fa3434f67efcbfd1ee9375a195ebfedbfcc2f7ddde00a518e2a175cd821e11b887a913499c10d60940c7cdbe43 -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/4d4388d2e621d71e390579d0684776d6 -Clang.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/b5dee74f399ed7422fc1dbb3321b8c216fe434ca57440c4ee51293b2478ef007df9f8d1031e714496014309ac8eabee4c7c884272181c0713253f43e1bcab3e6 -Clang.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/927779c5fe29a5beb2d4bf0668a0fae2 -Clang.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/3c16687184eaf180b5a6861a4b96ddf0d099769bbfb21265ed434575c537b10a30803924f05aa53ec0684cce8b8ae31f3082124130d4a4ae31b717bfc01e7442 -Clang.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/md5/5c08fb48aa15c5ee9667a3e177f19851 -Clang.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/sha512/aa773bb698145fb4040c34d787d959c0db71c90da5a5e5bf6799d287fd7925049169fa1a681742e12f81a32156b1d958c2f1d92295888cf50ccd4b84fd798625 -Clang.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/302b59a86820fa43b0f62c8788f4129f -Clang.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/543fad08f4d22a8ee5a25c29b81f8e6df729e19bd31d94cbb48d5cd9bf451fffdad692209d9a0a98583bd1cb22d3a783ecc140c10c65da58ebb1b727311aeea1 -Clang.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/5e904bd57f12999826ef39bc37382c76 -Clang.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/adb8061bc2debde834722b7965d4dc8ee5ea5a5fd5459eb84f872e13cb40a38d2e923f5dbe06cff5138e6cf065d9ce91e52ac395a586f0ac9c6f4f2fe1e4f0d1 -Clang.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/806b042515bb8294bacf37dd804dd303 -Clang.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/dabb1c73f477ca97ae2d84846ed937f084ceb9abcdd614a0fdbcab7b9d863ed8544ba25fc25f99df40254c964eb4abf89ed61bf4a61f680607e8d096f71a837f -Clang.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/1e92606453daf98bbb5879aef9a0b97f -Clang.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/f262e08e2b36dace3f7cae645709627368a221bc7d3a081e501f5a95797ef36e9c63071f17d722ee546e0993502c171049f285e22f665e3998e1a408e3adcdf0 -Clang.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/d5519cf744be4d311c5a6ce97cd26d6e -Clang.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/5ef23e8b5c0a828974dfa578eae68255e01c461f7d4bf10ec43f0bb4d2fe3b88c649dde8fe534550c602ceb835f7e736aa7d0b642c7ed21aa725c06c3bd4890f -Clang.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/22831802bfc779bac31c3b5fd5b613e5 -Clang.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/94be9e0c6c077409f8eadbdfd8ddc83901bf36f095563e630ba02a86234f30d67d4bff6df2cc33e1c52e494f231f33538ce1f8a25a2d0e187596c638121ed948 -Clang.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/71a77a339451b9d49858ecbb11119efd -Clang.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/49c3aa8c8580969750ea6d61fd69e98d1daf47b9578cf3372febd2df79542e22940a24d23ce16dee20e4bbc4becb9340f820d3d45f879fbc5209f3f9699ffe2f -Clang.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/6a6d68aaf9ba085c02ca1218cdce0246 -Clang.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/fcfbc12cb248021b4f8b9bcd7a21cf695b0bbb3983564a9602e6a23f83ef1b5d884927ae7b46ab5e3752a18d5346fc0b52a160ea18af1c9ee28870b470b4591f -Clang.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/md5/e451f3326c665c8dbdb41ffa2b6362e7 -Clang.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/sha512/d2b2f812dcd0d9d7602f37bbb629a8573be8e1d97e40efc51fe4676d6fbe69c21aa1943b6fc7172e788d3b3d2fd9d02fc3279838fad70434caf3a9e427006336 -Clang.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/md5/995abd90c834cadde6f272e097ae51e0 -Clang.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/sha512/f8f2d4ef5e2fac7d5e3b06ba76f7f54791820e15f0ab1bbd182a5e70709fc29085c73f5709cb45267671a849dd965e01683c6ced91281ef9d64f4750cf5d6151 -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/26143a5824a6564f69510f227acb6b1c -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/1ce7c9cc3c9d04934f06a32d67f5c23f68cb26b917cf81c8e9844ae20eab4709110a4142d21b62b205c714363df463e63c2563011f432e2e0206731841798ea0 -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/06dbf7651fe7d8b021fc1ab6beb125c3 -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/daddc731c54c13c0b2d665bb4360a400fec3246f6d756d5401a241cf6c9dcd2fb1df2f55c3559551ef9536d40067e9ae31753947756ef6210696b87856f831c2 -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/722fa85d203c5da1b4e28a1510bfa27a -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/cf8d09192ad248c6603de813b22bcb61e72994d0d39cfc4260d6f6e1ebe69386313f924c5e3de3021ce2041bc41d8a022623bae5c8979fcf81649c85ee25c9f1 -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/85b8d93bdc92b4014d45f5dff6ba626e -Clang.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/b1d66f9bcbaa3dc571fa8a1ca79f39f79ce4c7941bdd1a1fb7df2aae2c90960b9ffd7899237da1379e1898c18e2ffcc63eeefd20ba64550aca82167474981494 +Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/ce3e582bcf2f92fdaf778339e8c51910 +Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/4f977e8f0912f52b9b4054089a53a05f60bf7ae352c39b2541e68fecf3c21969d6d1b85e40d71d61040b65f7c60a2c33c8d259734bc1d2ddf77392fc425025cb +Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/1eda08774c2f9975de32bdce4ffc72bd +Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/c76ec1de9a25f4f8bd309336830cc07e1113b941ced12cb46976b24aebd4ab3d261c943dbc9cdfb34a01f27073af6f598dded31a4e03c62f229cd2e7d5982af6 +Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/2817b0eeb83eff4e1f580729e02564ab +Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/88242559299836c7a7b7d3a216353fc6880a587a839793ed71d6d053318d6e2071ff218587a082f2b5dd9fb2b0952b4c60e62030d707435607303708bb1e6d81 +Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d3f92998b7cc35a507cb1071baae8b02 +Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/be22296623f604927e2e815a1cc149addda6d567270a50b2cdf77fe5b09f74313210a1ca7b1b3194592da23490ba1ccfdab9f520ce7219989e646f12208e418a +Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/716300acfdee4415f1afa3b5571b102b +Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/b97efb3c461ea7d2736a3a8bb6b6b5c99f02df9a095f11291319c629d44f1fb934b124d38af6be3e5cc7103c6f85793d7f185c607383461de5d0c846560a1d1b +Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/034f44b2fc61791234d9580402002fb2 +Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/0b4ff55afcec0b1e8fbd09fab57de8b44d5ded360d3b53132c7a7df8d3a3b83a495bf6e0c706784e678c6de46be3a72e8bfe562c7f8dfad90b82880849625e35 +Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/54211070d63a2afac6350d06442cb145 +Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/a58f8afe9a20f202cf3956f758dc13a10be240d78877a02cd006d7e972751ed65623eef7e92a7256d9ed9157d6e277302f93b58f583d86d386ed4945f3c7d875 +Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/7084567b3637fe64088fdce357a255de +Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/77ae83e159a814a7117cc859a0b2aa7a5d41f983d45b7eb1ce2fd2e93f8733ee067ac8c9fad9d5af90f852b8802043ef39c29b44430b2594892e57b61ccb680b +Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/9e294d16a6e1c2c76c03f32cbbbfbe23 +Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/b8f83542b51f5cf953f6baed185550394744a8466307ee08525bf18a651fcecd7daafb98e75a0866b0e9a95a524e8940be7ae1878ba80d856182dcb7f7d2254e +Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/70a41c2ffd55d2d87a7b8728287eb9fd +Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/44bb3dea7227ee991b2666c43a88613d5b5d382eb560b5ad1f1184d38680c85a2ef961bac6ad71c2b920702c1ec6e09296198e7ff5e2929f4ba7839e55896e3f +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/95ee1406f8575898eb52e2c86ae18992 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4da66e4d397491836b3e539258844346fe50bff41e6c0628cbb5c0eac76147bd91d1720cec1523452efdb063adf6ef8792dc278244e1f8e194ef60a180442c56 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/6c4e4e892b54ce81d73a8598728083e3 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/53d08fd8b6782867cfa6ce001b14a2fde38bc9ffc85c7e148aebf59dd9c1c535b54eaea816c39fcff42abc456c1047ed13d688917302bcc5a281abe368bd29bb +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5acc5853111bcd529eeb06ea31b329e5 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b1794f7cdfba838a7e43de8f66700ae44fd16d8f06300e8ab955044ae9bc96110c5ea72691841cd3787cdc93dfb91c6b257702c20390689a8d1b45a994db2fd8 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/c4de50252e557fb126360001ddae6a97 +Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/9343a7272c76d5341bb49273ff8d43bed09ad99b2879ec51cfb8946174181b286af82d85e2d3a13a375c7e7859e51e4a4f06031a6a3fe7e540700cfc6a795741 +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/af301478b20e56cb7fa1160cda2573a2 +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/8822c58df101c239221fead6fb523e677da04a065b42849a2e6ffff03dfd81e07f162a9bbdd29490ad9c0e0a33d362eec46608b9e6e42dfb4889da1c22191c91 +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/901d2808599d5ac5ac7b5ca4bc39833d +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/820756cad00b1fe927801a253bd3077709c2b067ae79f9e1812f3cc9e85a0b7ac2ce1534031b7c6f7bda3364b7173c1c508e7c7d316920fb9bb901c16c1b18c7 +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/d1f368604084e907c382aaf00efe452c +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/523b25f6b79e222eb65b5f4cd8f23b0d2c8b25b29af0df88efe45546ea57c7dabd88baef454fa0b76342d8d364739107271f25d3504380fdec5c9d225fcc2521 +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/e57c116b2ad1cf32307eb4e600ac80be +Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/63366b983c7aac9fe1246b25432b2200c8316f569f6930eb12de3c867f448ffccb8756d418f92eae7751d4c9ce6c42cee38237e429b81530819684fd5150c93a +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/645929ce42276db10ab79184a60cd6e3 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/65555ed26d9bd670b8363e5dad949822c2bf0e141a5418e1dc30c3f8a4733dd050620e40be2e7552c2551ecb30d4ef3e8f74cb240f1d441a9720a25f5a3bcaa7 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/8424c6c6318dfa7bebeac33917b29453 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6cf90c253f6b22358c2389a2347af2febd010117b22de0cc91ad713b8c8224627398004567c96b673650212eb5bd40bb97b9a637d46ddfeb3c72388d83445017 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/ea8151dc1dc32befe579c7f9d7f13898 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/ed518423e9ec35afd7983471cf9ff1e971b840f637f34e0f62a1f6c7379ea59d4dafbeb9a311d39761733ecc98c0318ce3d8883298f8998e9c741441c7c9616b +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/70ed39b13bcb0435fee63bc30ae25a39 +Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/b2afa383346875514c62129c2991b3604c4fd3d507ecf4fc4244dec81d08b30218f5aa03dc4977185c2c9fb2d08848ddd373e448883ab472e5221ae5bf285c99 +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/e6798835128f663f0c837aed4463e34b +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c99856e16bd42ff967479e2c89690ea41268f1d1f868e2628482eafdfa53a0d69ed7c21ecc68ff0859eef07d9fe02f4844fad5f13df26cee6cea3a4254446096 +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/92c1bd54b0474244e35c51952966a55b +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/2d7c3b60ba8b11cf903bc5ea720193852027cbe61ea0c8d6fac70be8f97691da3d36663aac6e61b68185dd83b42d09ad61dea973d9390271210d690295e4902c +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/c495d594f8ce1f701d1bab54d0b60521 +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/0261bf45403daccf236723383341dc791e9cb3b291bde97812378d85aed785f083d5deea3bf806480a04ef1b972b00dccfd0537e43532a066c64733b817c3d77 +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/41541de24d625271bdd5fad867b8eb0c +Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/595226ad7ef75ab8ae03adb456b4ee9e884e9554c720b6c4ecbc38c75d446ddba7898be94630673074f09f40c6dc3e18fea9cee5a91b8b0e4727d20a180f670c +Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/8bd8ca0436611e78882939067f6277f7 +Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/27c7b06e93fb0fb516b1b240e0df6c95e8bad6aea04d637ba065c6fafd087bfa94d9136afd39273c8d82d9c467395dcbd7b16f6a4b829acb0c0d4a5677676a5b +Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/424bfbd7b69ddf7b1199afaacde3e028 +Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/9c48d949309aef6ee39371ff39a4f12c31bf3f25ddd288b317b2a17a803db73850cba2886598a1d10c4c154d511a4b79958d1acc012e92491a63f3925c522873 +Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b0b3e045ad64ecdc9848898f30d5f34 +Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6c0f4bdabbbc94fc9e1fedc138b0bce99d383e380ae7222fb70f5935f17701d549f6486956c8a21731061e4bf60bbc52794f6ce6858b4d2adb89bf80f88795c0 +Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3b7a461ebf957756aeb2a2455b0a298c +Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/74641a3636dd58c69415b19f0cb1de444215e22cfa9f0268fd549b5c53b206811d8beecdeb9692285613468d9a0569e836d225fb8361218438346914f6282839 +Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/5e7b9ad5fc3af3bfdf262687cd248dfa +Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/c54835fdf8e3e442b7c774d445c2f13c5dd8b3224f4ae165e72cc893ee5453d0112a9ca6d543b17f2c02a89471e2cff7cf022dc4c8188a5df25d101dd0f954b9 +Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/3204bd8074d42920a6707cc8624c0dfe +Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/74b26c4556ca18645cc15647d8abdbd46fb94c75169934af885e5773a880c066b2ff221402fdb4a53417b2c97ce589783f7fae6a8d56ee89cc1f70577b02b2a1 +Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/7922c04964e0c1a5b44e95480290930d +Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/4f0d675c1b85dc3e5007a62a7cfea412ca432d1276a259db3ed5a1bf0f33d6c555f16010de717a62e0e065e7c1dbaa66c281815eb9629d2b6c720b152820e582 +Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/e023eba0ea0a327f53013d5e4d50d0cb +Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/9fbdebce9c7375a20d1cd10e39a0c26b131af686cb5771034a6afc6cab08855e0cada2add616c01394424383333950d0dde9c55a9477fa139cf0ca3fc438b229 +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/a6c7d64ede931fb19e066a1c191e2f6d +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/1a085a4ea1efb910f2b529f3c0e51be4a5e31debbefd00ceefeddc352b36bea6d0de5a06ea7d509098d16416b536ffed3da8485feefad7a2f11b1bc148a0c8c2 +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/692af94ca3e5c3d229cbb459e266aadf +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/b27f05cfb0ada89cefc5a6f6527583b6b43d03525954d5b1ad1c807712efdb8750ea558a230b587a0c0d9e77c54d9f8978cc2f3884653808c7409eab1b32a055 +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/3b59b6aa4b18b5dbbc632811f2ffa270 +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f8c4b593f969c723ff1931c4875ed52497d83d74b94121890e10c9fcca5f6bddc5067555dee9949e61e426586ae3e568375fc44f318a07b70571ee34fdf7032c +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bc4be32ad57b13c3dabc80684a176ba7 +Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/19a8346547b6c6adc2a9156e4b913b20137593752efa3648ad532b08de67cf015bba1eb023204755f48904c3381a3665c6c54fc8233c50e887a22ceebc652303 +Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/05f37d069c7d59ec245d961d0928cb37 +Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/3b0956fe770fd9230319bfcaefab4922f9aee3df3e8516edf81cb7d322132ee9ab899af4464c75b1042aa99e3bcb07ede6de5646bba2a57995fc2eb32d4d0861 +Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/0304434211ff4101a148fcc0c96455d4 +Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/a033dc589fc95e63547b7ca82964116bec33ad6e78ac131934d4bb16988756d36c24d74761ca93b0e47dada1f3d2a63071cb3721ddb9af457cbeb164fe5f0f54 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/4e5d1064d90f24d57d63f08b61baaab5 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/cbfbe8b6f2be80e59b69d25d6af901ccb4807b12180208b69afa7223dd7d5249255265bc319c9402a1b0d1f0995940e3e72d7ecf1009f60d83021f8d35626a46 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/22fead15b4c45398ca869821d04ce015 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/2ee7a7d3f293f7b63c89bbe3b541722c502a840883804ffe272848f4ac99b7a8ed350ebe92ec434dfdf03d1f4a5531c1367859f4a4603c98325abe5a0ad71177 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/46dd01b10377cc3d45c6a42cac0a07e5 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/957677ce4251938d0c5e066448762b38a21bcce5ed424072ccd58085167d61b7e45a88fe32375f6bbd43dfb579b65a9afc09a886a650fc634a8fb9c81f27c9e3 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bd9a61ea186a39162201341f0739fe84 +Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7a06d2a9ef20e88daa00d627d482ebbb6bf7223219d8b2a24aa60ac9eda24649d206b093d5bdb88b65c1e2b0d1ba0ad7dd927697e2bbac65bc9b42f9d14ad0d9 +Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/60c98c6cc7d4446fb52b7585bc8709f3 +Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/4d55464b4499a45f774e1000a8b015326d114103a3d348fb263367e5506ca6659444ea6ee2767712903757e83939cd446aff6fe2351438b644f0057053422b58 +Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/90a512d1881c4af1f1abfd5e90e37356 +Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/62d6d855aebd49f132d6470c7b0d5a0b965c6489b025046c1ea73fc53336030d6c5b4c867523a9206821f7fcf62fdb37ef0b7ff4b5eb04d07f40b65edd2c8e0f +Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/c9eb9acb605d774db9636b82bf2e5f41 +Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/96e1440b3b0378edf8907d4cf779b1c53d63f6d00fa798efe1b6aaa289135aba8fd00a8d6f55d9678136e9e07d0c189293aec64f46e66788b938e1f8e1fc2199 +Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/5837070450c81d44395468d8e3671dc7 +Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/0e8b674c0360f9586f03c7f5d0ffd5bc73dcde1e88eddf7d6360c1461adb8efffb104d8f454116a6a6cdc909973d0876745590b21009a9de56e12ce6e1c2e8fc +Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/5c198d35df5cf6435f4f5ac91a78be01 +Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/9ba0a532f499933320145834aec2b57a70410bf67af649ed675f00aebfd59de7c80e6f5d19e7ad57029a573090e63c5eba4b42b498a374810b48c8668b50dcaa +Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/8ac88c856d946e29d1121426de44e6bc +Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/94af63ad3fb17d9c07f5256e2d474effc0e3d5ef66f4a9f3ffeb9bdd8f1577c35e4d0aceb8b4746ab857d8f164141790ed494b7f687e644e040d2f3820f9e1fe +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b4be546ff44019cf46d3250dd9a4321f +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/7ce5e4d68e18021392355359f59931219eeec3be4edd01f7a18b7bee499b589414bcea73820ee38dbc3b5ab12d912a93374b4a616b10ba491f5d41b6b33f3d9e +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/4616c348320d8704215d58c7268de6d7 +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/b4c21147ed21d41321e04b092d47f99338c6ac7d50b8328ceb8ae26d6382955cbcd655dddd39f0de3d3c36a5fda7084a33272aad9f6cd9585c87fee68be73a68 +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/bf9cf2efb938b68ac7e1560c464f9051 +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/ca29438393d393912571a96ce59bdaadcacbb329342c42a0de0e8d8ab52f69d4e6966822c0743d99b1a277c8715c1f72ddd490b781b45bd691df2c137ed42a1d +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/94138893eaaa99f37354317bc13cf7e0 +Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/1b03d8d4e407372875667f25f74abdaac9be0b81c6229dc1c4c1714589efde6b1f8c76302a2545b103ee4f9812fa78f9e06e5d5bb5bc3903ce579328899faa2f diff --git a/deps/checksums/lld b/deps/checksums/lld index 9238ed622c27f..cdcae063f68ff 100644 --- a/deps/checksums/lld +++ b/deps/checksums/lld @@ -1,108 +1,108 @@ -LLD.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/3144fe910aa5fa308a2a2ca86820541f -LLD.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/6b60bac8ac870c6e0f2f615ee92599c863e388bb9a654ce7dc6b037e6f7ba77b4401f88471dcdb2c8418775a833a10b010bd932a61c4264b032f5bf42642559f -LLD.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/md5/f65548e0c2c455550635d2821822e97f -LLD.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/sha512/52862c78a5bd6a33848ce33c79eabad854a5cb86487ff755160f3a7c89ceafcc24773495ced5d7d25e952b7a7147969a890de6806845996a0dcb3ecd8c1ce1cf -LLD.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/e4b46d1b3397fbc876db8f0a15745f3c -LLD.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/831ae6748e0c18e4be6a732dee56bfc3b84383e7c607828f72ba798db0bc1f61e9379edb904cfb992455ab5ecc6d4ea7dae4bd8eba481a857afe6439fdb333ac -LLD.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/16d60350522a797fac1fc3ba47609d7c -LLD.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/6d457e4de4a896bc4d8742a9c7a09c334f9f0fee1fd5e93133128889c326cb3891d7b7f774a01d1936717639bc5e84b7a3d6d39866cd6e689de78cecb5934f80 -LLD.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/f55f1eca81cc38584c94a8db9d53b53f -LLD.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/70abc3cfdf7c94ba3cbc26aaee3328e28a5e19136bd659b619a6240d64d50029f94ae36e5ca4359caf1db79e13e6283cfd7b806e96fc3245586970edaf678a0b -LLD.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/e7370d074ce08d8de4aa6a34ba7f4efb -LLD.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/5491fdf2d478abacad107a7de626d0f70388568b8f50732a91f30a93bc79a683d7acfb37a2ee9dda39f860fd9af144b322400fa0152f52041fec13a4ac01f511 -LLD.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/f75f229860bbaaf61d8ab8d89745d5b2 -LLD.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/046bd2cbf12a3d381821f87800635957bcb0bf37d55bf4a8046ca39fc652c2b98ba0f8497216f6c580b738db319932e8610c6215b020e993bffd129f730a4d9d -LLD.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/21c591ea3725c02c5cc1ba861b435966 -LLD.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/600f590486acf10dbde10dcbfa728bd53152ee7134bbb68cc15f724017f8b5e50102a286ae7a836c197c974d34078ad8e8988cf97ef032ab97d9aeab96ae9994 -LLD.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/89770e2c5fbd1f36775081f37e8374db -LLD.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/650e5fcac1f00c2ed576d9232df46325dfa2e283976ec84a7cc12c8127d532e3b4d758a736e5ca1efb5a09833f0369ab44b277469fb49d50ee14ddd9ebcf6a8d -LLD.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/4e9b983fadd5fec3a2b76698fd24bbb4 -LLD.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/058bcf4a9cee81287d1f19e9bfe5a8d16ad7955fdf175ad0f7be6fb6fcb9177674b08a8fdc98b4365324e3c754c4b957aec24daa8c135974a2f2737a6054454b -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/3cf9661e272471213ed27d70a28245d5 -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/4c2b7d02bfc73e2c36469b249bbdb9e6e695b47a464a71d5eca495fbd21fae35bbb260262771246f995ccb83ba1d89a5946d76cfb49de327f659d467ef3b2d93 -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/432800b0bd1fa328c929f24765cc32cd -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/338053e5aa25b4cffb10ab2064b5e1e75ca628cfe006933bc125421406a00a1b84386a4ad027fca75211bba9860cdcf0d1c1e992752603ada97442d97cf17f20 -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/067465123a0343a6c9d8d9cec1a6c3ee -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/895bc632e732086ac7676e9596c7a6ebc5f807fb49bd2cb542252aba76aa4faac8e7bc423681e3dd1556bac5fe5754a5e09490e2f97e40e0551263d021987290 -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/27f10a51c07df6550e9945913b8f40be -LLD.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/fc8fb0dba3aefa19098c3065cc0e83edabf9a3052691c6b3fac229c0b0bd605fa7062ad4f117220e51a6f6c15a0a6385cbdc8a2d8a0f46f96cd637fa134229de -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/5d5719e989de5cffc156955875e8ccc8 -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/71ef0800633546b4945d460f51ee9e042bfcf4d0caecbd658033199ac82bcade9efe9749998396d42345c914621f658b3d7285c849f554d369acba8c7c75ed2a -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/416ed355013ac094d39cc8bd6d573917 -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/a8d74edbf881b4f4212f22c01bc37e82dcbbe0e9399823604ed1ee78ab33a5cbac5e13933348878cd7cbac0077547af27cce80becbc5a2ebc6329306c86f45ba -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/68a609cb681b1fa2d7e8ad87ca31020e -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/6cba7cec86f914790194652fff53996458db01d26b322d2e76321273d425605d706d631141f2ae675bbc44712f3761fa2a90874de13a280fc2cdcc98eec6e0a3 -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/a3e3ae95dc0227f6a1c596b42fd5d6dd -LLD.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/e8a7e21ef3726353d15d1406cb76ce2e79de7d59244ee9c2dba6f2ca9255472dea28d9aee68d4d806f0a291b9293a3e3548f791c06b3461234efa36eac1ed356 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/1e43637c4e6ce02d7b2de3713c028067 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/820eee9280781fffe5bab8464294199389de9a6000cbdb2e3f8ae3d2c474ee176442b128436cc6edb414eda06ebbccebc4277b3d0b6b4a7a0d4d232e18593eb8 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/a61866ddb1da3130bc2c58426aee6b05 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/a193e68d3ffd1794d0107d4f003ba0ad5f5e3e72fcad1b6652c5487cbad57d9357311c8f14a8a40c2c7384042d768c86ba17f0ee5fbc3439435224273ed2cd3e -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/e72ad283df0a290f1eab61a2037031ad -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/99d04f3f27fde19d1e61821dbc3d1748e6687899e46206c99442fa9b7086f8ade1199c67f16cfc5be40bbfe37da602508441a5269ea741186a79ea1d221a58c6 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/bd08e53b82b168fbab8d97e9460ab9b0 -LLD.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/408557a99ba6c794eb8de0b0dcca04662a079f6af2e7a663075b8f18eb422006235650eadf32c3abde662f12f87019cd73c5ae759207dc11101d3f1c3b8e2d11 -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/df3bb4b61144f94e9ca0ffad461fa69f -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/e9ca992cd148026fccfe47b1e8df1bb8b60e7e44341d664a3447c06030dccf2a95ffd281b9c7346524cf87daf4e72ef7defcc1233a3b5adc228adb5b20d5d911 -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/3b6879510e198560d65d29f6cf238b5b -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/75a8e70ec5071fd41031ca96e1560f4471b6e0c45ac361d10f11e41c9e63ed41e850170f5b97cf305d0e28ac695b8a86d8d777c7a3582f6aaa7953c00a666fef -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/03eca3b921064050532b59049d2da878 -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/b7ee2af9440fdabe72005b84e05d05c28f2736135df63859f8d6ef7a10f622122d3f2d0a393ddcb39bde21ea8fbcba4a99a46b5568e42dbff2a505a5cda79e94 -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/cf9ce84e5631259776238a11c3528901 -LLD.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/b64f0b0e2b3180b569528479265f15ba2e44523dc7103af3daf82ef6b9087c2859bc378d51abf44ba10c6e10a9aac4498b43797d59ef3423de1f652beaf8b6a9 -LLD.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/a85df5a842c1ad1e9db959fe8fcc39fc -LLD.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/9f037dad3faead898854235ed48804607e19a68e4a34a28e1ea09258bda9b06c1be88de4595bb50b43234e438353db00f47dacfa0754d88e8fce01b637808f47 -LLD.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/46191b2a25020871c8c183d6f62ad477 -LLD.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/aefc623430f79050ef367edc394c72a09bfb4ec39c6ae4e31775d378d1169432695be1fef5bd93737502465363571c695f7a0a7bbcc78d78c8689a0a6b0e280a -LLD.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/5f034672f90e88a30ced0ffa0880e8af -LLD.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/fc8a6fe2f4e1a6ccca3744a4cc51be62ad2c849e9ae942e9c0452aada0303e95b376b55c951e8ffc22b65e391bbb182c977c979a8c50b50c1426cf84ca8073e4 -LLD.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/157f2faca0641168becea6b1493e4c36 -LLD.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/6981bebe07fba76f18e38f30aafcbf20325dd5a3f158ad732ce1d4f153839eb45966e707e0cdd008e8e739454f10f8dba0a372b0e67f7e753ed3e84ec47d4003 -LLD.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/de902268f693954466d62a154a2ac490 -LLD.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/57eb929a0b21d047bf2a1da287fe2038fc9cd26bab43b013ad8794abaeb387a5be0eb7d4f9ece52171609d9b74f1aa743c6cdbdbc1eb78b425e42a1ffc862133 -LLD.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/d11bbc63ca2635836f5b95283f6f93ac -LLD.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/e8b9c2383f71be7daa2afbb0a6dfd6487c832673b3272b2759e139299491374c2c3e8ff6db71c526dc0e71901fcf81fcf77db4fcb9351dc1dab0315389cb19b7 -LLD.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/8ad1aaa3ce1bd9b3aee584b6494ba6b7 -LLD.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/411d563c9856cde0834b4f035551cf1b0cb0ed853365f312889c8d85ff6e52f5470e7e9af5146061516439ad109945caf651209153db1f6671a4cb31b69abfa1 -LLD.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/aeb833592bda8c92781a9417daac1c7f -LLD.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/79134510ab99d8478b80a9a14dafaa818320b9f823c024260b6f8a82ea7ed406424f35fc9882e4590a98565098a949f8ad01fe03aea2b8146aa22827a7dd710a -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/db979fa556737823f4629c1d19d45adb -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/1790dd2098a07a31d3e3057257da0bb9d54dd71ee713569f9d15d35895feb143e22435eb1581d72922ff09ac5b733e0a3053aaeb2f31483e4441d7ee12bdffb9 -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/63811a8ee9ec2915aafbff884e8ceef5 -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/b8f6400246382ab9288bbc0b4a50cbb9264a8c0e2e3e9695580577df8460b7de015a0628ac92bc54ffa14bc7c03c86ee1e52d032d423120d4c5c731b8ff07ae8 -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/29e840a97982441b492bb6e9e30b369e -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/9a7477279f129c4c500694e78b0f587285e79adcad167da212e68c46a22c8456ef41c721d8332c7f101958cbc3ac055414fdec935e0980fe2d0d507b1bed736e -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/97bfb5e23744e57e243a9b0d3fe4252b -LLD.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/394db3828de239afa47f6896f0014900146bf8e5ecb90014601aab86fa95dba434a78a8590ebc588d3a22b93ff6481587c7c3458dda751114a7a4b41e93a9a72 -LLD.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/9dd9fdabdb07a2d25270cd936e8ceb47 -LLD.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/ffd59d483f969dee28e32166a8fe091a5ecfbb56d6c17d44c732f6f8608b14d29b4e3874d93ec2dc8563b9a4dabd61418836e6dd389aa0c38a779f527becf48a -LLD.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/md5/574c37b28031d6332075b7ce3e4b8520 -LLD.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/sha512/f09afd7098a56ef282ef364c59a2f91db51165d9ffbcbe63f70f68999c7bf67d6ee445dfde789be79c81e173db346702d983e03fe1ca51d2a2aa3cfd9b9e9e00 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/7bfb0b6c0ce4b271f3c0e7cfca20ce79 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/66daf7d38f206d428e27227bc1218bb8fe32abdc50246ba6e49ec1494c48b5217697a69e3bff51b3510a4383e2ee743a8a0ad11aedbaa895ce8a56016b5d7693 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/f2666afb8179d10cabe3bf9e739a0e2f -LLD.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/fa59a23c4b24c444173454da85e77ae4a9aa73ab91b64efe7a6aabfe21c92e4909ec61b7b848d4b0190eb5e4ebaf0d55f8fc0d92cedc6ede76add230b8e6caa2 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/292e0354262b845ab359adf298aecc6e -LLD.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/654331656feb9c4fc27a5a06c838ffaa10ee7f74ee7eb98496e9d8d0882ac2416cb8662b5ac70968d6e8277ff675200a19c56989c120caa83170707c409e0cf1 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/c0b864d0d7a830627cf0feab502eec81 -LLD.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/72404a586c46b62b0317080b925ff5fd2ea99212df50fa6631bdd2f01bf805bbc8afb2e49bde16a4b8ee7896af4d58237326cb83aa064e98509e6f4f0fff46b1 -LLD.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/942a23af6c179c2715f2bba2048fa125 -LLD.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/101a6ada3ed831dbb2f580185de732b90b81ce1b6ba544fc1ace40857fb471e61901a782f467a2077b2e34952997b8d04c841aa4e9f190e1488ce37c5f6ed32d -LLD.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/2f0aa6b5220213d549a2430a439094da -LLD.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/180f0a520fc9089ce39ae8f032d7b415442c2ce6bc9a44bc427ae98000be55d0eba6278db1e89d45e3c79c907a75264bc8185cea088d002aa9557fe1b047b42b -LLD.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/6206795db1db9f9a86346ace421fa778 -LLD.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/b86c57b2561cd8fbd4eb0158b0e3f4064cbc55936546da6dad794818eb93f51d80fac1dd094b2281ed6125416a205010e2edb801fc347db8d02d93fbc336d878 -LLD.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/e07b22829c2e8d4efdf8c41584a3cc67 -LLD.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/4695f44b9a1b21684575f58dc66644907f7fd5250db42da2cfa55d90a4d5dbafc9cf37698750f8faac45ec38dff54eb382288052b7c95e82bfc60a10136ae5d2 -LLD.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/md5/3e183403117e11f6c4b9059fb32e4acf -LLD.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/sha512/34f6fb23cc234dee826a303a8ce3bf17ddf945c2ee9a75fca4f6c625379901d5fbc4d5d9006b63d95d982de852fa1594877cdbc9451b0ca04ecac72371d1647b -LLD.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/md5/40b50d3ba65160eb842bc43241eca5e7 -LLD.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/sha512/8cb54d63bcfa0ead6b8f8583e3e30ed676df4e8042b8a40f94aa443b1de211cab71ba4ab36ae804469876511641aeb5cd29e1843adda9e09e7b7e30a936c12cf -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/d999ad0a4c62fe11037ceed768cf8cb7 -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/3c43e7eabe62f5a71d9b2411def56d5357a23ae50b639bb117eb795101f10ee896e886040db0f57c527873f07d68b49c8eb6f64a362419ba4d6ff9fbd2ecd9e3 -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/c2a7d97bc3b45591329284f55a821c26 -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/d53f16798e8d19359ee6c86e9f55135838b2db78d2e69a2b0d01c92087b9bf1195d7cdcc9e2eb5c29debe02048af6b2d7dd83c0207600143c64b5dd8be647ecb -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/0a6eb0fb8f794e4ab1ffa0ca94e69968 -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/9d1b5de37206ce411db00587a0d9dbb3d57c186ef84d2d60d20dc0c7718621bdf01dbf090ac1d2e63eec595e55fc39d9787d038766dbc0b4c49708e1b16bf09e -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/9b2f4c2988b177ac0928219406d5aa21 -LLD.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/199bac9c28bb919eb1caef1eeeb5a935183a134be3def03f401794a2241b05d62468ee9ba12836d07bbcac508304c50c4c7f34d108fcb505a69a46a0eb89c6d3 +LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/64c9a9f1758b9b292e0a3ef37f16ea41 +LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/cc740aaeb6ed29c56b2881e1488606338e4bd0e049ca4a5b8312b1d9129b778224570336698347e4562d632db9049e0e91ecce34ef68acb23a8bbf62455a81cc +LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/1a8e11dba5cb574cde42de2b9703ff79 +LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/290300229576bb9155fe6bd24c0ee21beb41d0f2a46b208ab5a657b0199a7376c1f4cb07204c8ee1e6d202efe30ca040a6fff63c69b174120de3eb9866e344f4 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/cea134f347bae257cf5f55b6388cef81 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/16b59143e929791b0c3e56cfb4970d8b3c87adf6e847fa9e2aac17c4ff2aa311ba2c7511c1b0ae2f39d9aa92f87ad4d99c042fe35bec391ac865fedb72bd3b1e +LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/5f903bab0e38fa608e8965acce6f020e +LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/01e5f6a32958e04174c545f57c6c3b1bc88ccfd5ab18dcb9d67b92b55ebc7655a03bf963c4eaf7e5c3792d4691427a89db372e7534c6c8f965f8a715a32d9284 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/241a55374fd067f3736a2bb929e47015 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f1fedea4e6b5f6f3bbf4d705034d6c51b06f011c2ecec1ae49c5b7bd123891eee8b991462d60be7fffd58f7c773afe910a06ec0b55b37eed9b4d09b9fdbd5068 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/ff018c7448a7589935333e46739ee2c4 +LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b646c6a945b8f42b396164a8e87fc2e54b1ad05681f438dfba83fdd3712a60167aaffcb0300bc42d904eb4bd34c002a71642b59540ca01e64d6fecc6daaacdd8 +LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/e6ee9423a82322b9233cafb1c92eed2d +LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/c915582a9ce2dfa8721741fb1ed19b719ba40f0092c2d29ebd68829ee558cef0b044a5e40985cff88e89129cbeed052d85fa5c6b6d87f9b3a68a6e89079ab4f3 +LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/cc55112e2db358cf26d7bae3211d8e4f +LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/0ecb43045419020eea911f1767dae23a6b1e81bb155ec493e911a9412e45f7ec71461aea2e6fe346e641747139cae43d9435ccecaa7fd6a234e4d69bb06606ed +LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/498b2909f80b20588135466d5211bc80 +LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/120fff24e85cf970670b20b5f4509475a3ae0d7621f8f67d018f3a7625548d736a3abc89f015966b1329c6b0a08a1db832e035ee3bae384e2c5864b73a856600 +LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/1bcd298d5292f8e51f19b97fa4b27ab0 +LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/695c42557f9ee53b2e10bbf74653fbad4d02124b962a1f50cf719d2821607dfbb9c1bf638dbbc9e0e544f3020a9ef4a82decd13f886cc41ddf47c07a5e40eaa1 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/2323ff933feaf3754b442bee48a63607 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/47b8e490b89e04fb8886dae438e3ddcd53c4e98045de2b0def3988671827528c8e9ae296411464c0f17cc64bd3956644673f47a3817237f27e1c3ed16ac8ef01 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/37cf8528666064a434296f2e0039e9c6 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/ea1504a859509f8a16030db7a65f42f0e78d67adf5946497f2178bf25456c0f2583af72c636881a4bdd1210dc0d377bdf300ef55aef5db8c56995424a1886059 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/1c341f2b161e2320d3d1a74685887f54 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/4f6fc099293deb1a2cf729ea7edd6e17fea0dc8b9fae9acfe34e00b1f5c798933df9538c805c8d28c6279eb38f9ebae2a1aeb1a2f23087352c6eeb3b27b63ddc +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/e306d59c71b0958c77108e650fac2612 +LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/79fd7cec0e169a9555ec9b0acc3248991e2e37a1d5bb422808ffcfd4f47e79321560b7985c82dfe070fb0b5ded5c160d83e358399c6e7608eeb62cd4a1406f88 +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c1d080f1aebb58778d730578fb113290 +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1f420da1897bd0a61413321aaaf032e8ed38d59e6dfe3313ca3a6ee6582ae6c566e3761ca8fcd1f5a964337ba8a9b3e73dc55ad68aca139beeb45e43d51e862b +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/6f4e0c7d2fe9ac254650dcd2842dafa8 +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/bbc71b334250e5e6429766d88595adbb671a206630987ec2a27e05711ff0f844487dffc1c136052ec11394e9d5c51c70d1b75d5348f97d3bf7fab463291e9dc8 +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/76925b9a7bc249b2227390c479c54f8d +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/20643ecb79732e3ae9666116dbd0763c18b808afa78e6a14998aadc7265cccd6efd28670592db61d3d27b8d3023be4c5f3df41fff9e1b38d61abf76829090b4f +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/399b9aac140d9050088fdb187ed4645f +LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/8bab65965670fe392e78d0b9dc78c92cdcf202898f6d5a3174eb89ca5cb95b995675c8a7d81bbc4e95e490ad1a43d9d29d7907b7006789c0143a1d8f24cccaeb +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/026a4f5ae9eb3ac05e5e8fa894d77a5b +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4bca8bd558619260cddf4e2f4593cbb2a0691b5ccc6d1dea6dfcc5a2b5f51d7d1a76c35e481244e211e2eacf32bd628df5ad0e6c75e5185bb1d9b569f6acbfd3 +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f898ceabcba052b7e6713a2b2c208a92 +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/92be1910f795390be5f15ba5b2c220a3209a5f7ac04fca3f5229486628bcf5d2f20cf6e4dda8b41d6beaaff42a68a9ddb95fdacc6eae33b9183b581e9a194895 +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/e366058cf69a4367945bdba9523f2a0b +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/45a786e8d0162bd5bd01c029691d2928d3744ef4a7a1efc2e39755dee2f9a9ae23ee725f0454ca601cb9c082a342209e9129df851314b5757c74767b13508fc4 +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/665a8502170729c86ea95a7ea2fcce0f +LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/c1a2a85c9ce14af8c91bc9a599393c52c0b8a585057366b1ceeed34c5db44641ecd0c9b377bee80cb4951fc7102fbb4f21fd050126bfa5bb4e582ffefee17035 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b90b2130262f63f5189cc8e4a65e4433 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c1cbfd38c82d676c3fdbec486691334cf7bf4115d9ef2665012b71725c28545a49f34edf5689ea0352822c811c24c89cc152d1fccd1586b17ae8e6b2503641df +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/2d5360da4b2c9ffcea5d0a646a7c114b +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/73323e0937fe4423883480294c8df44744acde4f47380e35535cbe69c855c0e35e86a1eced3085ae0285f284f47af5ef237f4650bf2b6a8b9d5308efce88fa02 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/a9b9a65938a7701aaac6fa706481c867 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/fe8243aa131ad8be54f0fca5754c2e68ec39049004ec8feed499731c5228a7a46e303ba866b9f9a55e5318c73d8a46d964673e111f6c60e5ae1628c568d7d894 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/0d9592a287c9231ae2db65000be2cea2 +LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/4ee192dd33f518d2735a829ac8f822b5672b39e8c2254987aea6e5f2f0056213bd85d84c4050d52ba9ac8c35762521c324fe2d6e18db0396e7142af9cb61a561 +LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/d487598dec9969485dcf785fc0968bd4 +LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d3117739919696b9b0c9ae398f1b1e9db8bd3e2e27839f62b3551c22ae2517f8abb69e57e23d125002bb466889b7352e69c1e9dfd9abf1c5433f274e928b341 +LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/943434b08dffb54e8cf04ae7bee34923 +LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/77b7bbc5d988cf36ecd10609e091cf24dea134cd32c7ee96dec7bfe1a4942553b6205653edc16c8454261f621966daeb267f42562172bab4cec9693ad733d867 +LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cb9e371947ad415de048636ed78ca48f +LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/c00b696fa146e8c29b37f15f78ab3325db9b3f5b3514e615f145b4eb7c9c8788662cfb6255b7dead596cad8c576b378f7459c2c85d462b597ba5e21adbac0536 +LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/485f061ee8425f042e4dd3042388bf8a +LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/845a47a36c61b305bb70b1249f6fb7c4e8f740acff90d3e850ab2e887f7d959ae263431a02305bf7587e4194463f9932769d500a19709bc479eb6e6168325421 +LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/f234526410e779188f3d22da438a4926 +LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/12e2c9fc5385ff142bf82956268230fb01a6f1a1fdb3a6c1e13afd341dd2eea970b707168d5f45960dc9ebbf6d6598af0ceba371172f624ae823ea1eef4e9031 +LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/e68cab4aec1abcfce12a13e3d1f67dac +LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/67755b34ebe04f4d28be3be2a37df46b5e900f38dc4908875f66671fbb740cf033f2fd9af5116635f55025f330f7b1a478cd4900db9d00e4699b591a16269100 +LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/4a71aef80b75b2ea1a5b7f8521afcf5f +LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9deb3e9615ae15dba8c744b22416243304d30f100c8d17538fcedbc18787147505f74ecc2f933fc54101527847503142cfe84a46a95ca3c57987996e3b8583f1 +LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/9b28ee75d05cbaabff57fd45cc0d1cf7 +LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/bfd3d6cfd4a5a2fbfe940f64d47a86a598360e90619f8175a2d1306f0894610f13fc44ba099ad59d2989beabf491df08a5611bcef3fd61b6391ea0230b11a432 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7962fc6f08531f0dcfa44bd667f31582 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/2c936064685f12ed6764c187192023118e97dcbff6ca1656f0304a40772b4ecf55ee0296b3c2a00760f5bb437162e2b737635fdd59b889d35756d697fc7e6b72 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3eb4d78af670d446f696449a5e71e3ba +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/315dc76799f3e443fdb5ebbecf96a08070f8251930a26995de892b8e67bd35bbb365f2cc5fd93bc7cbcbc9edd08280ee8d2a36b28a704866dd3fdddae4969455 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/e73cadd0354897bd5bb611cc1c027858 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6f444a4ea22e7108ab75686ce9cd78c0db0a677e39e8434896fb1ec90b9dc013abf7de1024d329a9726dabf229a8a68c27a11f211092e676715d282efb7b8504 +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/aeb310f106f31126dbe53459e36d33bd +LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/cd18c115415dd92bc7fbb5c29cacc5848b1f3851c3a526ff9c0813ad46824df0a4f13a66b1e6641ed11b44b5b937390619f01666fe6d5a047f1772f0ad03c941 +LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/300dc28f7af6aaa69cec9a214a57fdb8 +LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/dcb40c5934118c204968cb963a3fae91179eb1e31f5397975ca98c8a7aaecaf2a7f81847bc426fd306bb76970794502ed4f94d8f461b96ea90362130f44520e7 +LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/e1f23fef82fbfcbc28899677f12658b3 +LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/b6b585060832d53827376ac6c00cc8bd5dfbf091c38c87020f6de42adc86dbe4fc33ec2c17f4433176c79a509681d694ed1502b179efcee2c4dd4c10a26e87a2 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/5dc96eef71dc28611bc998ef966371c6 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/781993c75bb07db96d02b5a7e779116864730a9bb941b64420a435956a7ecd501b5b2673f1854c09ece5f0c73559d5723c271d6352be57ddae6801a695629362 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/8a1fe0ccf7699ab7a7a514b620112a70 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/d002083045d3eb7c749f2e97527c1228cd317a8138ff254228e43594a6cabee47fa363785466ebc2874cc438457640ff08a836eec7334afac451506ea7bbed03 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/331be92bd3d76bb8e86991b7832ad41a +LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7b1c6df53311a17a92a41cb67ec476f947949c4ca5d15a643badaf9f01e76a186abbb6e156f95ad1605d83250df4e081164986a6b7fcb3238076b0ec5a3bb565 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/97c7f5267ad6927f699a25ce44f55a70 +LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7b847c6026fd7daeb17a4459b852562ce6664b2f406664be672bcc384dd5a79b9505561fc61dd8fb78a903a2ed4978f322cccad19f5a3966bac856e877c11ef7 +LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/c86da6a396fcdddbd26cfd71c0f70458 +LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d5b75b43167080b8ea456e516c9ace02ee6066ce715a56f0b42cb8045b965b1cf8d4ebc0786c23be4544693ff858816a6257b0638ec11e077df32ead62f7efb +LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/d72e175272ed893688d18e868120c575 +LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/9a46eeca8c7a8be65ed487a74227534e08a257e404814c44730f12a5bebc8cd160998cfd5ed30189aa606ddbe602e1b1788e465e4a210103c6726a7fd230abc3 +LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0206fdaa9582ae3bddaed1b6fd7a8cb5 +LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/584a67f603f656ca5d27aa0ef2e425ad385612aff06cdc1d534b5944939a09246c93954fc153b8a89acff721e657a8903af9a640abc252d4e452f348781bca98 +LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/0dd14af342467eac2e13cad4acbc881f +LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/918f2c66898f828414009fa6ee273da5bd654e4b787ebb4d703f0be27e388b46870d68bd58c4f45638d276c61c1bfe2f3c67fbf34dfb5578158d072f82d927de +LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/b373e1bf2a24f34496754438e563a5e9 +LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/a739f29e332be74cbcc544903d08bbcc12c3e9f5c3d02d130ef1c69426ead2c74b14f98ac79e88ba29fb2e2dc3b28b7d81c9d42f2e27e0ce9442f6a0e81bb8f0 +LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/1fdbf6aa0751788611054f7e98024104 +LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/2015b8e84642b2434d1089908354b47b080d5683f1c7eb2c09de09abb3674e7119ce4ecfa858bf8129fd9e9075bb45f2e53a997421f2457aa9b5c4a9d7edfec8 +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/85bd5a9e312e83a09fa5b7fd6abfba76 +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/0a5cba5c65abc72361a780f64e64c463797aefe52994699d8d785437b773530e49a5fc2746e36bc5a31aabf4eb4343870aa448f8fa2b119ede3e1c4ea228cc9d +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/ab07ed76a796d86cb6ac2ae4fc563eab +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/485117c7e1daca401c1cfe77324e8f5961f6f33ed2a3c907f4c4a2bf9c45c14d929959cf8e4d9cca9ad112a3ce6a851e336cd793bd5ee284c87b9fe487700ecb +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/852449a26af61d8554fb1b4c22c4384a +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/a080d2da5ff4b832822e099f150f0c15b985d54678a9508711f7f435d6ceec68eba12b5f8c25db0b4841dc5c5edb003b74b4fef391292b9407d7bda73d35c4f5 +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/eb999bcb67f789b6443dbfe907bc61e4 +LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/811f72ce250184ccdfa30aa992884f1bdd0a791fa125f089037bf1af45b844d76807c5662a904ec9312b79efc565fd0957f195a70a39248eed99ff53f3284cba diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 22d86ec2e009d..122aeb9a53337 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -1,111 +1,111 @@ -LLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/db3b242aac30d823cd911ae3424b3d7e -LLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/5efe8bad36e748ca1883b23f5eb6da416ebea77f42232e2de9268d9aa92af868b2a20b3c9062f77e734a58ae607b4dfb134e2f8c7879652cc643802379296196 -LLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/md5/640e9260e4b4235b9497720bced5330e -LLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/sha512/b7d193821ce8de809ec4c3d035ea1cc9b889e2c22ba0d263d25b8c01474a6a353abc9c9b8bf8feef686f00cd6055f06f20cb3f5b85299cc9764fe75c7b3fb21c -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/898954f176232716590f3c885af8c824 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/8251dcc31ca7d2c827ad73668d9e422f777a219d444e9e00c0a0e16b895ffaceb266701938f630d4f354e3ded736061be69d3398f3810dc0df5b803a508bebc1 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/860b7b3b26e3fa5ff2d1c77d087cbbc0 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/4c4dcb47f952b87ea506e6c1cb07e1b978dee4aef14381e78ea389de728d49b351c401b079790a1d1f8145853840d019d02a906a39f3c1edb705c8cf663728f8 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/9cece7e656a511fc8f47fdca094a2695 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/9f918e9a4b552911b58ff43e829acc8355904fd7f050a25ba9e08437b8f7585d48bcd842c79bef756d2afe83fd0a690331b5cb51fd55a2bc4bab7e3c615987e7 -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/66fe8bb24feb2779828432e0d996381f -LLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/090adde3f779fd56815992971a532dda793d3ce4462497daff66c065822834821eb6a0b9575f94241a760cffe4cfdce8b7e8a2b1acdefcbb62fa9db55748ef71 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/b3a1eab6dff60e4c9dc770ef1d6d108f -LLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/eacc5a3fc376359e27ff795f63aaca0d1cb3c57ca0dcb27e9a43887dbcb6a2923b1e9895ef0c0fdd3545b7edb5cac8598515f464c27f5eda25dc65425f5e0c50 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/047ea0fe65332c30dda16799e7999185 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/c62949b9e4ec97f372d3d9fe7bceae923d54db5afbc0902d74b7ebac66dcc1de7d7ba12b10b5028aa1489166cf06b30fc2610ad84bee9cac95b08bc7b7af7f05 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/0fe165f62c2fee6dc0fd3167c6da9ecb -LLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/9bbdf63b00ca88afea787069da2c64ea6a9478c2f929bb540fec5d2664c19249ea63a5b69d2e703605ff5bde28a029e5bcde1470e859abdb1f0cc8da960065d3 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/0f07d3c14e1bdc435a9c69431b354c51 -LLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/f3f572dd805bfb14713a6f4ce79e91c3c9b2fd7008ce9f4ab1e2a55f0e88b192f48beb6ad3954b531261ecdcfbe8d26cbf931057e43e472ea244beae87cfbfbf -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/e7b10dd01bcf10e9c3100c4edb5b127a -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/a86a213de97ad709fa2b0afd7a2103f58c5292bd9e1f939dd79fe02574065642b67823a0b05f9cdb054c5b6546b3051873c67c93bad57c0f53b8327285ef97f7 -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/480cfee009635bfb78c164fa10745e36 -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/414af42a442224f780eb6e6455483a6a210250d28040138998ef01c57c63176aa77626ba8b9985ad9ca9a397fac5c1a8abbe0e173a585d218de8b036cec96f67 -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/1b6e3e6fcf6b20e824aea1b3b510f989 -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/7370730b97875e1f6b8b3576d68d0d4a3112ff7be66c8f04901f905938cdb495b67f4676f03c1e6f0e4f9612e2849d7a9f02a932bb66354d007c2e4cfb8fad99 -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/24bad94fd9ce261880ff589829dbeabc -LLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/cd52c2c036cdc7d986731b76324c0d241fea9d9deb3a94963b5efa01a1649042c0ec062e2cfc6c27bc747637ace09106238d07aab0df84c448755a61571e6a68 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/78273dd81559e0812eb497c91bec8279 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/1b28a9f2300d4777406d125eee8fd8217dab7ee67db38334cb0f51a749f27baa370d6229c4396f12a63dae210c0d772b83d79feeeb6dc17f19184832eb15859f -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/3a88701757a155ba35cf911b1ad6528b -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/aca7a33d28e1ddeac19d9c99fd2ab81268a23d2523df7d300c8d1999481902f7aced3e4615d40bc7257d38d10867a8440d9811db81300db1784b2515c3bc8fa5 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/3be9234f6718d0684a40fe089af08503 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/9acd3d69ea99f4e660978d830ead8253f8623d6fb66aad0c79db211f975f42b58254b7f47e284e751c94609934c6895b4a2e662fa0e8ac8835cce1b60e653ff4 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/583608958fe5159e105f349f73bf9a40 -LLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/cc574e3e9f457194b4ca01e63a6a55d46f72780833b496f570dc5b7c1d85d751f02093da95061162a8d66b0118a1cde677751b32e53261ac0272d540925fcf0e -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/3318c8aced1cd7dfae6cc5722c7ff590 -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/a202552fcd12c6379cda41a235be1eab46b7ee2c28b4cd9c9ebc581a5ad2eed3f0c54d7ed861c9045272ec04d598707877b6b831c5109b12b4d2de80ac53bd2f -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/a5718bc977c437d3268a8e4681d2adce -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/45d053db2325ff637edcebb60167e948f42b7a6e8e6dbb14b2b0785459e06667ee42c1e01b13ed9ef09b51fefb0bbdd861ad59a3320f76986fec60129f937cbe -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/53f2205312d55810fdbc1fec961d3560 -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/bb80a8003bdaef7da016138f99e8c53223642a9490c01b55ded0d02f6dd69c8460df11bfde7cbbc5ba1b3c331e2d2e6a8c6f7ca83aba20112bc0f56ee907802c -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/737492de94fb15e377789aafd66e817b -LLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/2d31da6d69deb859513ae6db6e3fc5c2bf3703648301b1c520154b6f3aa0eca12c8290026643384e40b261755d8c3061c02d685dc53f5c14104498657f52d167 -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/99a0eac6aabafb1edf3bff32c283dcbc -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/f65a01d75c3330299584f8eec1d25f54d89162e5359a7dfe0f73de76aaa371751d3e5008a0dedf5de55ac0f29613f0f2ee1d9dbb7f69871dd0e85e047b44b28e -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/b8947735e7e7f9687c9fd97c72681e9e -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/a6c699b0994a132867071f6cabffdd6bfdb51eb4fe7e5762e836d420aa7d61a5165d899037df2470e9cdb04bd64c4c153ff83c49e30f42a120fd5b5090ea4fe6 -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/fc3143ac4c492cc2041d73556a84897b -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/3e7f3281b0fee548a4135893be999456c39aaba04644f4bb55247d514257b3700d36fed376fb1ff7b3ef5546a43f791b202a2c1ddf6f924a63730c56254eeb8c -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/38f9a63bd7b6275e4cec5fb05d2ac163 -LLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/b7a131e2d1838cc3dfeca08fa8f6b329ca714e909d35bc6429952e690743a321b8f2e242f358adfe719302742015306e3cdc1ac0ce5be613a36447e53292f9d3 -LLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/ef301dbee02ae00dfb6f3cdd9e67c714 -LLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/944849a1261b3ac3a5141fad55397a0de423a73f57ec6b8d725ef09b3233fa6bd6e76be4c8c4e77c80e3b60bb3ab135d8adb30760fec1d21eb699aa1868f9a09 -LLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/1777a48a9188895ed07163df4594a688 -LLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/e5226a9b3f87c63b32e69e43f96ca07c32b34742955c5e608be7843dc1c003b75e065ed5ab29c2a4144607df23d97852382d7fe7e25f98abaca2493200ee8cdb -LLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/e8b3d76ed47923cef55a71033f78ceeb -LLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/88825e210479ba2504892af2dd01a3f0a1488f05ec5df6b352fbe0d6d8bcc691fab0e3d3b31aad2b14eef860dd8ef5bf56e83647583e7c3fde847ec7a0a651cc -LLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/ad634b98e24aa296087aaf11e6d70828 -LLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/97bb219361efc3aa847fe53fc20669821466aff399ac47bb312af7d0899bc6af0533a0e4b490d74a0fbb3f5e3d0b91af23023109fc9a0138ef56630055e5792d -LLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/cbb4bed17e57832bd99affd1a01c5e9b -LLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/948dcfcb18eb1288f4d66f9d9a7c7b234956cb8bf87d6b52b7d639dac60360e4a3b9c808a908b2d298adebaa9b6e63d8df78c57a1b0ade613fe0da0921d07807 -LLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/c800bc4f0f0ddc5d2626512a949d9b79 -LLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/4e0c9091d580d8d8bce9d5ee63beccef52dbe4428d29fe5315891ce63a0115f4226029bdd68612d813829ef49f0c6db9b5681c83cd5bc70ded55541466296cca -LLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/4439c5a18ff9c3bda01aa989b5d7a1ec -LLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/5d17b2da2d2a218ecd866d9164527bf4762090effc908fb5b93b8f4125518e2925ca65b5e96210b3e2f77750f2a3e3c2edde8ccde3ab779561191ce7eab294d4 -LLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/9544f9d46eefbf593ad8b665a1cd73c6 -LLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/8baf49c6495221ac0d0a039bf7988fd4890cb750519b0e2e4b44e8dd504c417ff9635d66d43ecef75e3d0d397b1e47aad010fb91782286b1c26f7785667d58c8 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/8f46834c72ac617d41befe0abfec29a0 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/c131d322d45a95c74ad01bd35c094b5e30a47535c6cf5cd7c2751b99f8942c1cf0c27c6039b33050a4e140ef856d79c5878a81fb98db0751d43b6e463070dcc6 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/14f97547b230bb2774bd1bcbc9e44fcc -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/ff1d8ed1a479409b3bc611cef9d6429bb7f1118972340ecf7c5b8cf6db28a6cc79778e800523c655c334496d3460a93d6c0f42a1488dea58d8c2416afd0725d2 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/6cf4a661be00b3d007c67f7af80b9cc2 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/497e853d4b06c64a61de09a1216ac612ee0ae4a3bc8f01ee9b919a82c82706bc045f0a641c5a69547b68a36597c2be8f699f711f60d5af5fbf290421a4e76816 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/4e8db53272b73e5bf7c21413cebea308 -LLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/184c1cf3e36efdd9da14f6e3decd015268f1a0a9157b9c677877f600bcb4c9d20a7f37d6fb830b22012bb7cf05d7af86ff97dce046900c1a9a90c49b0a6c3bda -LLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/c06ecd1d4ab8e4fd73c6cba18bee9750 -LLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/eee12b4e551d21ad35f780ea8f3ace4b43efede66297e6941da78ffc768fb28c31a1f7e1ec5e11495d95d456ff2a929400984282d2f2230c55ebeea8005c352c -LLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/md5/72975355fb31f13f86be0afa188dc436 -LLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/sha512/9a82c8148db2c67c50255c70fbaa3c6cb60fb6bea700357a39ff5e59dc1fb615a13c647331b494244b88001b525d4cd61035705b2ce0e6590b8894797efcfbe1 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/04820df9e180b7008598eeebe49bb981 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/ad5fd5d96f22d67e3ada73b8f0deecff2df212a8e8762d4acdede62564f379496351659232fc5450d23b4e3b8a55af95f48725841233a6a2448d18d3fdc1adfb -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/59af89fcc77a0eecd7c9e8cf812f035a -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/a5c486b44cbfb41ef0623c067e42cfbc5216cf2c09f537b8b792aba814612ded170e6a99a11bdaf9f3790d4af06f7f058b6e7a66b2f8513a8522044a83007a11 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/b404077eea756a6c5c7bca8a54898680 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/70c562d603aa974b8143fe72ce000be60fbd975cbeee4ebd0bf29bb01bae2dcb8006e3774581d1e37fe221ea0432067d06a19e5fad963977990fdb5f1c0648e2 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/1437271560c205407a4a1d72fa3d0ce5 -LLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/463b2beede8289d4b6d3a39795fcf8e2c6007b321abdd7125cd56e2f960fb2b9c3f9e27e38cc23ccfa51ab0f9f42378f64ab8d5dd28a2ba452a3111efbd46c99 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/df1353b2f06a17506ef20e0315ac8396 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/79a696d86252b23dbc439284f8a2dbab5da77af2541680c569cfa11bc2b4c9ac03218b6a307841ae92aedbcad88573e800ad5fc8bb1c55b957a837a7e53da592 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/92b15d83ee43db8522be4b4d94b8aff2 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/ace2323208f16a9c53823c0ea8ad2b910d416d834e0731485008e4607ff8ed8cf566509172f8f65a8add0e7a7dd7391704246cc024eb3d40bee73f043481decb -LLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/69c1c0f5f416d00a3237bf62e6c14075 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/733cfafec656ac3ccb51dd444f5df028062bd1b3182463402105ae78f13675dd0dcdb79e1eacb5222d0942baaa85e487fffa01018c5c19f0f971ca96315f8073 -LLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/8192a6688c46ca92ca1706fec271856d -LLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/49d602a869ba34d560966f00bf575a04516476e9c147219ef7086842326bcf88377e2c601ae22fbbe56f2a12f40d4a9611259825bfcddf05608e73b2ceb363c9 -LLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/md5/64b59e6cf3c0d8cf09ca4d376b298134 -LLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/sha512/19a1a17ad7e95ffc75cecd28be4190ed527ca5ad948995e9f3491770e010c45ec79bec52d270abeab25c96846f604b694944f2eb4dc0697f43cf4e9f86f1efb9 -LLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/md5/3221c8c4773ada18489016d3167ce509 -LLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/sha512/514a6cfbca973e95cfbbb880323056fc6b51dd373a6de26e9628801f393e42011b4c21750d0276e562ed09fcc09e9751572bb9c4f362b8972411fca3a93c8f7b -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/03744874a951f038349d0179e752ee40 -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/83f34672472ed640a9e2be7eccc71fbec4d42397174dbc5abd34ffea754681c3779c07692d9eff30db299fa0c922594727f5d316ee2542baa34d081717e89ba8 -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/649d1106a8f95de6eaa7835f4eb66fe2 -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/4955cb0e2850542a1116fcec9e04e7f9f6633a806013ec49fc9e519e2d511cccd375410225eb8630d74b65fe0f2633ae7f46b194db8e9ec78cf7a2f12f6920a1 -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/d6a1fa15be0cde66ac32692561f2249e -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/48845cee32afe6ec3338b6abac77c773980093082d9dfedc1b9f03c31a5d940125e2c01ceaaf85d3bc06bb75e5871e24a51743b91ce1b5e8ec1c7f4eab4a4c2e -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/514a676afb3bf9d5a20c52ef3f1d4bec -LLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/ee2ba45c86b8a4cb5d2214b9fbc488176e2134a9c19806c160bff7e3bc796991051e04fc5d2829b151faa3dca6738f4468405b246f94bdf27cb5366b1043021f +LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/341e3f0b5c160100f5e12783b8f779c0 +LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/65b2c2091db1364adba4fe1e4ec6b9d6582432a0a0751cd0a3aa1c69798633b3aa5ff09d3de4e47d552d55d5ba81bc86662f1784cff2ed58e800452488cf9d50 +LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/249910dce0a9ee089711b71626972b26 +LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/1eba4ecfefb56a00390e5c528c467f921d64e9ca40f5fdb4d7fe0d7ee995f03d253887f7fe40ee285f03b12fa7a194543d18cade6af8a24bf47e56b06a78d901 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7bc3125dd810bcc44ea2d454b6caa683 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/86742a4476481b14145855ead8a5acc6397782f6d3445f900ac2de0570f1fcf53563cf5e1f3cb59886282083ce63756604f1ca2434e9e427cdc1bd1f68373581 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/4eae06d9e6272aef23afc191501810fd +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fb75927982b1428b05b765bd5ac017b2c15d89990b7e6cb582b9e1a3ec04d09801d25d5cc6c037a12c205edb7c0f7a2d33832a2d1de7920711e9720dc3ca3655 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cd86e18a63cd6e84a1493acf0df4e267 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/1dfefc4600368467ab90ccb527a9fdb012b9b7f485d932a0db8c4b1b81985fad931b74494b76ef2162e46280447d39a055b5681b33a17c564c50094de29aeb13 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c7cf7daa7c11827ae4f9fb2e16f3cce3 +LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/dabe2940606a671a8e3b4f28bb9e813d000650203c382372142457020f2ccd498534903aa99320afb7ff960a62d752ee6cb724e74745bc1bad1051e12cf78ab4 +LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/62e575b89fd92d9206abebc19b084abf +LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/7ac029567fd68fee82b7096e2fe278ee5cd2935494433b1faace036469c54bc471d614d0bb339750429dd88f3e723165d2dacaa627f73c3647c6f3b51a4a3034 +LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/5d39ef811bc78204ebfc7e98111469cf +LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/10fc9a64d63351e168bc79fa63bcaa6fd49c8483e5ecc40a66216192588367e9b47ec3ea2c047e88f39ea8f1caf8052726f4bc8858223f7744606156b4133970 +LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/f072fe487e5d1b717aec49a6244adf05 +LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/42b03a2562728ac86e751abab2e8233d583baf006e69b107d002a9258844ad53f62e6332eab3790364940d478c7ebab6d3e0e2194220e8436f40e6b75063d1a2 +LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/eabf0239298f13ff4893011e75828bdf +LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/34724d9c9a550c85d406021d7265e1848b002b8f212427eebff6e8f03ec6acc336efb0c2cd9d9e1c76329e7c84a84a9d852b8de5897550d957e0e9385129033d +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/1910b5daa31db6542f0c762901ab7d43 +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c43e8091e9946ba1d8849734a25b258df95b4759a79676565b624930d4a19805a78b66b1d193e528f95174d909d7895d4a4e49fe8ca298a24dc40d25c95900b1 +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/a5198b13dc75ad3454e05aa6cdaca48f +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/9ec8078a1a7246f1545fe074783d6b88ce9b50f62b0438ff5637f6dedf5bcac427cc252c350354b7063f79f4e31a19f699c168c15bc6547a207da497026c2827 +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/f569654ecdd8ec2a50986ccac8388c69 +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9b50e3be1577a753f0ce42704846bd126229d8dd9f28bfcbda58c4f18e4b9ca4ec6bb9b57de61b3b9af8157a2983aeffb8af782a073e5e19a8ccc261cbea9601 +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/496de8c9e2361f44ac6933480620d07f +LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/02a8ecfb6e81e0fe07fb0d616a84a590e23e944588c18348c32265bf6bf19196beec189a0bc40514e379e97a9c8bef83557260839800fabe9f8e39e96689713d +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/05bc7406fd0a703edbc912bb3230eb37 +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/898dd4c19dd0f22dcd1bd44264daa8dc64340c890c3368fac7451da1ac872a687d55b5eb50ae4e156c2dc4ece226ec05775daebafe9d8b53eb83b72d2986ff92 +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/d6ca30fc3a2796ebda2451f80846883d +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/d7dc96e1bbca38272b1ca78b3ff995fc30434937a58815c63d0a9b4a017964cfb269a1f3203ad8374870257152229941d420f098644375b5f4d1b88fe39e0dff +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/6eb1a197150ad6c165b82c5e0e0db102 +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/a159598c2bf351ea79d01e8a454a82bbd9823c080399520af3182e57259957ad07834b03c336e6225857da365e8ec1aa9f65b0ddd0821883ae817cb81f8e6dab +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/116d849cb2fb4b1c8c517397b2b04192 +LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/7b2596c76d2814fc30992ba78e5c8f93519442fa76004187de9830732b80bfc6c77f5d7aca042c20d8f868cd682bb6f71e3fa32940bc8c7b401753dc4ac2f331 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/27837dc854a173bd37a20f92383f6913 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1719205cba6de969e8724a99444bf958d5a7943ae90ee2dd11193f56ddfd4f0edf6d9af6da2e67787a64b91d994fee76bd8ffde36486c5229a980c2c4ef07e29 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f0016c21c045e205131ea22dc711acaf +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6d192b7e21c7ee3327d288b890f4c5dd03e5f53dcba6905a34cab96b7ad0ab6364f5271af88d95e60aab8f569a8840d17e16f27f6fcdafcaf537d5d4a651dca7 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/9a2bad4518966db29e37e7c88388e779 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b9a10af9dcbacf1f129d4e9b4cf562a6a4687252cc8a0fcd78f52d75c0c20be0ff32e67413a7902a628b04e7fac1091d35b64b145e33814899796009b6ed2853 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/77c4e24c1e44ce14bc6476954f294a15 +LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/d9d90a4ac788dbbc1b532623a380d4cca8813ecdf8b7b4a8cfff769499e50a1433bac618234bd0765d8a4f50aafb3fa724d16ac71baf75ae5a2b4396fa2bd017 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b29e36dcf5a0aa05734f1d6a0afd6944 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/ab46a835f9843c5b3427101bcd0c5d2b8acf79693aa9b8d4282d499f25df4ca248a81fc94ddd96c75d69d3c6b3814b225eed81bec32fbe9199bffdd605f7fec8 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/a411269f925cc968a0438562262e6d97 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/04f275603134b0ea0f23da377e4983765885f2b1954d5c617134af9f103470a5e50dfda18bcddb836852db2382f1c134db40df00b36c8bd00e7a9e6ff1a9e684 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/841921e33407e15eeeaa76354aa2b737 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/e1fb8b75e141cc90916c5c81c31ee91336911983c525f38eab86682ba69679dfbe1f10c9b673323632fc75f38cacc2af47a3d5d5d1031ec9a2a60cebd68d501b +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/7342a1d7b1d2c0fed7f5edf1c331ffa8 +LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/dae8ca11fa8d34f99ee19a95bcd108a65b9e6a6ddf2e5a9b126f2ba1b1cdff6b7ec21e9590d70b3785593435bb71e47703d9765811db814a90aa8a47940421ff +LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/10aac489dfa10a77427a82958f525da2 +LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/a87f721df4fc5f6e929a54d8e41e55fb366a051a610836923213bfa42a7f1593de880391131619653cc3571bb76a4c82e011852ee5a6005523957c9f0937e6ba +LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/7f231fd359f9297261c22f95d8f738c8 +LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fdd6441011609ef341108ff2d108c6f320d415b621a69922aeacc555c3d1ae6090a0f600f24e229a609b88ba9c1868900791a6590033b7dad333ad11f8a6365b +LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/c4523a485082044553e1a89049dc4734 +LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/db365e63bbb5189f7f348e2fd51e627ddfebf838ca9dfc6c0f8a7bbf6b8a2a03d78ea3ccdf08b0c2674f4cf5a0979506efa643554091ba751f16051bdf42ca9f +LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bcd10e4f3e5a4b00d52441e0094de1c9 +LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b17fae89a3dfaa9428cf48c9c0866477cc75edda6aa3800702227cc9e3d6ebaacbd60cccc96acb4ccde56a2de531dea5a436bac8e6c450a4674daae23b878037 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/2be8cf274b7667adf8d967a27abdede0 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/15f58c9a00aca5bf828708089912f128adfa3b719cc2fa8b9b4cd7ff7722d02375bc9a961b02d5c6a6c9ab637b626d78876741bd824353aab944e1c3b6719837 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/0dce4be3e8cead78cd3d12ca0796d560 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/cd60b39f2ccfca8ae0a497292819e9cc1893f6c3b2162fa9bb3136187351cfb1d6e4855141f1e9252bdee7e97ad61c0560566c2e9f73fe77a26b7f4ffadfdcdd +LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/f2548c8f4bf1edb488642245221829b2 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/1604986526156a40ea82f50ddd0465d06df9faf306835f1dbbdac7da7f97c60fe684cd6c64acd8833a9f8b1d16f80c123ceef94fc16f255f815b93f1d41251e4 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/1c268e3e93ab3a34b3c05322c2fb0dc9 +LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/f111ca82e196ea9507bb089b9d10990de1acb1a94778c40012ba6bfc16cf362369fb1f9dcc869ce14545439df21f432589ec004816a1ba0323c5edecc2b84211 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/b39ce0b0f143c3bef4dade99251003bc +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/62148e1e0a31d6b28effda0a5016d9335005b27ffdc5be1d184efcbb13f13e29eca52eca19cc6800d1d0421c0e67a36027e05d5fdc967dae686b5bfd112fb2b6 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/9475748210eb5b1947fe3aa6673b6c29 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54320295e59e5903db558b6be0220442dbaf7ea78e1612d54a35cbe014541b354ea708679da00851b962140b6da77301e27b656fd478666d3f0f710382c13a85 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6a533054ccfc3d1b0920eabcfb45ee03 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/3871620aeea2ccaf6e4b17a675c5504624fc6d8ed57bf4e5b66e0372b7124e4f3d1e0f10baa1018d5a1ac5bc4bf0e9d2143e84827712fda1f512fed24829f1b9 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3fc6d1b7d59b98823d6016f97835b7c5 +LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/745942235e40f2ab71a5eaef2768842823620d4a4dc7454a7512fb2bd95bc8a74323eec6a4b33edf1ef935151c18a20172f60fcca2fca1ff3a37b1e019ea4640 +LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/f069af39cbbb650e293093b5989324a8 +LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/34685eccd8c1cf7b72a52bf353de16bd0cac13959584217ce5d0995b52f506909955a7051ff7b29ab9d9c3f603af8f7db936f11e4bde83f5acf16415de62880b +LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/819a9695c365b9365b6cdba7cf9288b2 +LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/4280768862b19918e11b6a7ed09f150270e71cf4560b18b224b3591c460c9375777e73e41eda375271d719f23b211daf3ed51b3c87bf4ee4429344d14f1ed7a5 +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/28ae362155ce224cef605cee53e36d0b +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d90f25e57f92a9da68245ceb15316e3868bf657d7e744f37cce5ccb4945777ec82fc5d470ba4fc104fe7aaabfff7b0dc260838a45331e4360b0fd14c59a55666 +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d10ec63510dc1a043ee0a4e37b49eacd +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54c393208d1f51661e631cba62a21c0685fb58827067d5ea7c42fb3d6dd8c8db99d8ee1b3c304abc25510bcb0265d86ca03e1ce19be4faa252d97cfc8a1b52cb +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2c1e000206c9e7c6c8e7515eb8115e3e +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/12c0ead798e43448a30699b5386b3d88aac49aaef9bae283ea6d089a1c66df7293f4f220a2b5c3d96e73e556e37e745f38d81f5c68e09a86a2b19a6695eff460 +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/21d6c5d5e422412b88ffce50862efb29 +LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/5e8e17ba79134e9752c7fbd28b62e4616574a5e1dfcb0980160a3aad28a2f6cec4e48ed1acf73ca1f94d74397f7ee3eba53cb1280699e40c451295590ede3fe3 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/293fdc43431493f915a3e0a5b3c6d587 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/27e13a4334a3bfb3c91fd06abcc4eca7a347f4bffcbce40834302d153ef29756295121b42ac433c266668af1428ffa08ed12ce75f21fef44cd7ac1d8bdfd155a +LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2825dac8280d0563b7f521a9eb8c0563 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/7f4549ac7b63e58d8c149f6b22bd997545713477a1df3b32adf640f3951580df1645f08756d9ba80c479160cf5759e3f9372396655a35cdca14f4be4afc4ae22 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0c0da0eccec4a092fc0e9a915716ed6f +LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/e538e29c4d52d9aaf151670619702541fed8231ae4c7fb9431a425d10eea95433087034a37da8fe468bd27a1c882f6f8eb9549ef71964124db10e99f4b402ba5 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/6b4fd19277c978306441da3b58ab86a1 +LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/6216b3e1dc6aea979d8b5abc4cc0faf510e4e64441b1d18b4b36c45d65e874e9046e14eea67efb88f3219449ef048d34fcb751b15c59f8a299aa822b426d50ae +LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/b7956d25e0e5ced19df637b4fadaa532 +LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/ad632493095a8fc3638ff48514c9902215378532c1455cb19d70da9f2ae46fdd91ad4a8b5a3151bedd38dda9f07c21f9a25d8e095ded7ba843f9bbeb005e1bd4 +LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/392f0f0f61fb672002c7473c64a63ccc +LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/d620dcee0b20e3aa4b2fcb7ae835933b33b5e4c4b5d9102b885c70b1dcec535239eb5a3d6b56b51f7b049943a2c79950bcd4a4425610f7a1531f6c452eac03bb +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/0b41650067323bbe0c5edd5c060b517d +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/111a21a5b491a77c69ee724b37d15b0c7baea387bb6a36695a1c2dd5f6e2eedb0ed211513145d8a6ce4dd6329b2de67e9bfce1b03fbf911b906a33a39e573f9a +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/a9079da821bee8e4b5aebf47a46cd9f8 +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/7088945264d1ccead492e81636086390fad91b0e071e9f3a54ef903b619ac2a7bd38fa5e0e04ea1e299f3985e04838cd5b7a2dffd666b8e7dbbf3b419f74df88 +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/4ccb3d0eabf8253cbdc1192b04c78d4f +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9d817068dcc2b60c77fa639aa7632cbf071746e7dba62fe524c095f86e88b9323c3ab82ed5af0dc8b1af9c3e6f0da18be53d92e7c05e2d056c84e5a4e974b6d8 +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/a88f7a9f42d2cb5567c84d7fa2a2732d +LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/9b16cbf75e9971dd4950cd79aef85396a7d8522a572f1c8017af82725cb335674741af680e1dd10c731987a321d3afd5e3e85718d3c3fdd1c9de4803e72a66ac LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/md5/b95ad4844e649bf46db43683b55b9f4f LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/sha512/15e0996aebe6db91fe58121001aa7ea4b23685ead3c26b5d89afae34b535e34b4e801a971f4854d8e1a1fbc805cece06272470622eef863e225358113a127913 LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/md5/6d8783dc9b86c9884e0877f0d8ac4167 @@ -138,115 +138,115 @@ LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/54ac594b4c8e7f261034a8 LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/a43756afd92081e6dd7244d162862fc318b41ca110a5e8be6e4ee2d8fdfd8fb0f79961ae55e48913e055779791bd1c0ecd34fd59281fb66b3c4f24a1f44128f0 LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/md5/83cf8fc2a085a73b8af4245a82b7d32f LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/297a5c7b33bd3f57878871eccb3b9879ea5549639523a1b9db356b710cafb232906a74d668315340d60ba0c5087d3400f14ab92c3704e32e062e6b546abf7df6 -libLLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/1b99f43b611f8c72e187b767adf8abf6 -libLLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/e8a65c950e1d782ab6fca6da5b9ce434a66901d7c1efac0ad973cc376ea1c6d1ce7d68802d623b66d9fdd2801035383a11961cf544173a54e90b6cc4acc2ff88 -libLLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/md5/7a1171e54395fbf5742c0b3dcb1ad116 -libLLVM.v17.0.6+4.aarch64-apple-darwin-llvm_version+17.tar.gz/sha512/f173b4fd6090cfddb2fe43da5603d7eb627bd8cc1385b039a719b830fd82f230d0e1e7e9d8808c94cebb83c46145148c206e4dd43cb5cafb96d7995f116a170e -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/0564e41ee1ea590661214ed7747fba62 -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/8be4f721f5d6f264f4934423fa884e43ef6fda5f895786ecdad51bfdd3c02df575a84be87d69a367cdd4131576558096502b77063d766a3961be1f1b4ab8e205 -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/d64714dcc951dddf20423aa7831cff9b -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/41bfd5c44c27be4538a19da87bab09f35cb7212eb31205cac876c35721a15b6baba26ae52b5457f9db2e4f51e9600cf9677c68a6adb6385d1fc07fe7edec274b -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/f457fb97ed50a72dfc59a3e84177afa1 -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/a04ef467f8761843c6e621e5d996f9cbf06d11d46c6b7bf85cddd768ec7ee1a6374caf153afb8a5c017048266e6b5df6c5d90cdfd99f1ca0f2e096002e2c0d4b -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/4f6544a9c3e3e4929ccb5f53d5be47b8 -libLLVM.v17.0.6+4.aarch64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/cce7cc5eaceff2f02e67258fbade6aee130462d049d1948c37061018f9acccfb4bfbb0cf8f751da83c8f76fc504ecd4ea9afe16382e4d9f261c573a1daaef942 -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/137328448b1a47a3ef13cca0665356f7 -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/0e6ad105e6cf8ba21a7521ba213e3608679e414740cccc03fbc33385310b50637da9a8c1cb0ac3b54e0ed487e04e3e0557cdcac40d25211731645ee1c56aa238 -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/412efb53b9468fa5a64866c76a60d9ab -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/8c02a1d910aacf807f8920a930ea52ef423d70a6ddb18eaab32ec14e836e08b50d2444fde6832f5eff21bc14ca291d69a2968d6bc94e4039dc439b879378bd3f -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/56445a46ec24a4ccf796b42d83a5b43e -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/90549dfe474c8ed9b12b4589243d53508b2b682f71061d583b77a01131fd215341f8dd860609823c5a564c5dd61b317c844b9d050920e19a99daf3d024f937cc -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/48d268feb53cccc7b12700a172793525 -libLLVM.v17.0.6+4.aarch64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/a6fbfc44d5382de86548ecdf7d18a8487ad37c84efe79d67d9360872dac756e7e013972981c97758a7a9eb3857d70d79d3b8bc90aaf1638ddd8c456111507ec0 -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/17986258a97407361b4c4c3679932666 -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/71fc532446f5b20f2118a56b412690ed38c3bfc52d3e52079dd903b671bd4624e2125f377c183b86aea4ba57592ba9a1143b5a1327ce49b128bb7c81490305ab -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/dc7d7b2e6184a1ecaf433a2589b59940 -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/d14b4b583dcfd8709e17d7ac79ebb5cfb044480a85591b5e26b8fce9ecf49a13c4b562ad6e6d2081366db86e6b66caed2278ef167a97650104fb818b5b0e4776 -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/d6c4ac2506639735cd7bb4940404806a -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/2089a09a4ba75fdaf053c4a59b3a5cd333c6e043f9e5bab186d5d751aa84d616dede56799468768190b3b8120747e7e08d404b8c39e7329b3154ae134ebbcdd3 -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/20dadbec0c7ce386f718031adbc21b9a -libLLVM.v17.0.6+4.armv6l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/2a6f09158272ba3d2a332646db57a94c2b9481835a974ec87f4c9ff23b5e5dfd6030f71d98a43380fb5bde078d7fb586cd5afc75b4b4428ae80f6dd09dbd26b9 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/9508999ae1be9c15f87ac49eef8bae80 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/735e5cee32590cb1360ed760d49357b2fdc830f943e3c184ba9c213f620ee38b4b8d7dc378540fbefd0f500c61a131b36051834ce521bb6d75f0c6ba7e223606 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/0c56726d3c7b249621c7e5e71bb91660 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/843062aa01605beb40f2d5a484638fa4b896016cca242084ce74d4a5eb1c0b8fc91addd71be105a784e083b90b328d0c9fdfdfabb0d257bb91d5ada5f4f71988 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/3674c019b88c8324c78343cf45beb98d -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/043faeafccd4bfb5207078af3517832ca710add36eaebf8abde86e4801c1e964d3bad5547800ed5fc4722b90c2bdd606a11ca06ab277f1e48264a194b1cf85c1 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/bd94929dcafc7ef8d4ad1f788023afa2 -libLLVM.v17.0.6+4.armv6l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/26b40bad7ac61366e5609c6078d2ec34bc18ef89b25d0c251a6dd49e83df4a62338f49cae2729d245a1d367a9d7bde01a286eefbc71668097d83b4c98402fab6 -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/0a443c1b7289030b32e22dc137b4ff3e -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/6ad96be0acdbc90ac092130ff438a1bd0df409683df624e0fce4095bf393ca90c54c71c19b1dc1a28563a25ea06f35d7883f199533d3e52ab42bc373212aed9e -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/md5/5ada2da7581d128ec2dafed8ddd489d4 -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx03-llvm_version+17.tar.gz/sha512/6b9d41908dd882e7a874a46786f5bf67db1d63c2917a91119dddbbf01bd709ec5d2487c0f3703e669a7ef404fd1a5a7c8671e4ed2e3fd10a47e6c4f6c2b7f115 -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/86074d6b9a30cd8b6ffd8e1e1b3a6d62 -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/48c7d92a780841d333adda21984920ca66d47b54b152c4316dac05bbb6b8ea6007644cf93b4a4f8475a4cb5a228dd0d0cc17482d873c7d9c9d90213b64c3ccc8 -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/md5/df1fcdc1f7f385fe630bbc83f121943f -libLLVM.v17.0.6+4.armv7l-linux-gnueabihf-cxx11-llvm_version+17.tar.gz/sha512/8c1a8fd4665c871d50e68249453918f865429cb9d3fece6ee139f367d006d5cc21613681e84656f9cc4bc6e884b3de7c19c808fe9dc2a9c7ca8b1ea9aa594e6c -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/md5/07b634f82a8330440a2d5608cfa90c42 -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.asserts.tar.gz/sha512/51cbc0753da4612ffd8f3f50c2536e7cb26d6cc67c3b936f2f971cceb9735b9e587dcbe88dde32367c9b581fae639bbe8076b774b6135288f2b160719dd97735 -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/md5/26261c713ecceb1d7a076c784d76bc0f -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx03-llvm_version+17.tar.gz/sha512/64344d21f38858d4a10adadc169c03ff98d523937882c8244f297d5e835ec8186eb8ad20e54c4aa5bed597af35e7b25cb2499607c967bf404054084715d631f7 -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/md5/24ed4293f7fab2172ab21b96fb44817a -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.asserts.tar.gz/sha512/d3c612c24e4a3159699ba5e31d51c9068e977780f3ff2af49a1b084af707398e51756921eb0fec7103bf99e80b6beac4cff5c1bb32c72920ec0834be7929b33b -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/md5/6e53ecf916b54f97f0aa6fdabc49e9d4 -libLLVM.v17.0.6+4.armv7l-linux-musleabihf-cxx11-llvm_version+17.tar.gz/sha512/8ceff63d6ef095dc2db7d836871bb07ba8d36bd946938a21dcff9edc8531635df8af1ce0b39fee0fd644929ab14d127badc77697a559fdde2233d2a74ade6282 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/122aef0ec2c01128acd1b830faf9e279 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/b163e80b21a91b75f8c7a82a3cae705bf1dc6b9f4243ff0e5ebed535589ddea835b3071c36794ca8511b411c71b545a9c3eb75f0a530e682996922916e2bbf5e -libLLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/8410e6b787a1a39cbcdfefbc69ffc0a0 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/debda3ddf24fe3228ac3f90f809aa4c3479a4d46a61f5aafb609740d353acea80cc33086e5fc79303845a642c4171c7da79108a213728630e5045daf18e0d6e9 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/7d34ab481b9b538feb96f53c5c0d6305 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/2af919b8481c1531b9a2458dc80fddedf3bbc0eb9d70a4ba7b6b1ac5bbf1163b3c407a026fb95d5c580687ced6bfa7ab474efe91575c9d3d98e3801e1d64af99 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/26e92a8a602bad11d07e5e28dc597363 -libLLVM.v17.0.6+4.i686-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/f6ceb5ff769b7fe75e19ea10fb3ddef55517228526e6fb3d961faa8e776e7b3cf3d62536cf1f287a4d0d9054c9e7b92181b7e3dd8ecc1d0f79bdc585f2008d37 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/14025c82e278f11ce31fd115c6e8c344 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/161f80c5d1289a90cab305bc6dc6a54528e797e6a0be375afba819640507df76636885b9aa5378f2585a7441acad50566004f437ce1e872e50e8c7385fcf4621 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/47ebe4003cf9938930d992d12c52e6a5 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/0d3ca73be07d98bec4f283e84c4286249de7ee8f2b9cae7c1b0f44a96ef9d90fd16e3911c9fd49652e0fcd105cb2588d66994aa502e9b3a7cf22eed6f264c6b5 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/91d359c70756f364192ae99a3078773e -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/a2c36c4039e98b06472a4081621cca4352bf0050d915a3d28e93185f16e85fc38305079a94d13870422feb9e5c365219d9213fc64441d1f9f2dc176711447282 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/56857016759f1e7a7414d41b65756d20 -libLLVM.v17.0.6+4.i686-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/e136529b357eb9cf6659f8b0447bc63bce77e3f0b943e955c01571b69184fb0c326b08effdb9e08342556c3b8649603d94e8c9c265041482e2c103b582f102da -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/ba23feb343b22ea60d9e1ffa0d4093e8 -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/84b77313f0048e02b7e4d6185a47417e66ec6f32ba2a8e9029b688a386acd3c19c84b1bf351e2ab6ef7942101f1fd89402bd12bf4676d126cb1b283ce9272d0e -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/5bcdc7a767e191726714edc8ca6a416c -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/fee08e6b046128c83932160a376dec01666c10dcbc22584177c017ccefc7201147c74c12137213c8209db8f0ea04102628c47dbc9a51615db889afb0cd11abdc -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/526db12f2f2238cbf935f7a2bb7c2485 -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/38602341b0e5f1ab99af66cbee19c0587beeb7883c71ac9b3a7c5853a09fe1b4aef9afc6ec66fc53442e491c976f02dd5dbc739ee9974689af5f76396f2ad382 -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/6837b6aa15a9628614b046c18985dba0 -libLLVM.v17.0.6+4.powerpc64le-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/4fccea20fc1bf221b27193a85fb3b274c4479c6f9b5c8e77fd9666f053b051785e7b4bf512466a0e6df5c303316825523c634b3c81e7829824b3e6fa28b4f032 -libLLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/md5/0ba2cb738f9e3f1cbcd0774331ffb7fb -libLLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.asserts.tar.gz/sha512/5a6de296017d942e7ec108663fe238f7bcf2a0db54d9cc3c44f4b2fd2596f2d4641d5ee1ea183d0b6cfd3bf10a4d1196c21a503f89f8c1c3746023e5558c6049 -libLLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/md5/53cbd7116db0d09ef0e41802032b8d3f -libLLVM.v17.0.6+4.x86_64-apple-darwin-llvm_version+17.tar.gz/sha512/30cd95f1437fd05a73965e88d35e3c91d4281ba9a339d04a36d8450576e8f32eb1b7325b45b8c979ca63b319af582c49f867a7507248dd1f3567226c9fe29c6e -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/md5/d9d5f588ff478450645c99a6fcbc59df -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.asserts.tar.gz/sha512/b0320e945024bd3519edd48dbfac8735a6f6041c199bd78db345f27ada53bc12917184350a9448b12d4f2ebd594e0e1aacc12c7796807abfe47997f38db21c9e -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/md5/8e79c58d7ee18853107954d97d19afac -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx03-llvm_version+17.tar.gz/sha512/eafed326babfd8ab56dc75c999b7526729723d20a343c98125a532ad35bc3ef1cecacc8366679190dfb96b7be6617adba614011e87d925032c5dfe96172b9228 -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/md5/da04c26d8cfd0dc3487bdb28c5641673 -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.asserts.tar.gz/sha512/5b61115f20383e0c0c7274e24091d6e8ac29961aba5ba5a81c4f8d1226b969674d72642627125fac696b6dfbf64cbad7aab1f090bca217b8df4f50148c20442c -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/md5/0283786c2b0901e9e5893179c29c6cb3 -libLLVM.v17.0.6+4.x86_64-linux-gnu-cxx11-llvm_version+17.tar.gz/sha512/f47ebfc5acd36940ea64d5fe5d3bd69aee430c911c58b453a2355b55690b488adc032437bd10f893afce1da5f8777ca5446426dd506b8b5fc9fb6f76fbf9f6f9 -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/md5/aa8af3906a48929dfd6c04a83d909eac -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.asserts.tar.gz/sha512/ef9954203c7f3ed81e044c44ca80374485b91551267a5b74bc42c4fddf82ebdd7f4136dcd22b05d70bb66ae47d4ed49079f5e83f38f0a7b9141158d631f96c9e -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/md5/26cd3622c65ceff6a74191395bcec31b -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx03-llvm_version+17.tar.gz/sha512/033cb0f0ddc9027afb5dace0ecb39b3be9f13badda715fea1f8f04ab969f0a7b25544849abe851f4aac2576f4d99c9be8595296e8d1b7cc4accfd4cc3c882b3a -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/md5/a2fc72f59c1cdd2042b855c833e23c1b -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.asserts.tar.gz/sha512/0fc74b666b91d667fc112f38b970bca4cedc3083fa832907d9daddbf7cf99fee89ea42829eda609bd96a1bc9d80adaf32b047232a71c5957b87fef60cdd4c046 -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/md5/91d0f5839e7e744eb8048793c3236a89 -libLLVM.v17.0.6+4.x86_64-linux-musl-cxx11-llvm_version+17.tar.gz/sha512/da9ef48726b6d4e2012831bc246e3e6d2401af7ddc7636add6c96239351a36c3c5ae2fa71937b047ba0f63eb0377692ae85357c2be0a73ab6e5e710193266bed -libLLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/md5/6942c1fc5ba689e7058100a6b0fce16f -libLLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.asserts.tar.gz/sha512/8919f5b643aaa6c6c761d92b4e3d6d28165e18edd75baf2ed1dc32b27c1b2da55f71b6dd5ba7d114d021993eb4db415e8ae264ff014a12dcfad78543c510dea3 -libLLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/md5/1a91e2eb0b4d4f6d691d552e78060661 -libLLVM.v17.0.6+4.x86_64-unknown-freebsd-llvm_version+17.tar.gz/sha512/f1a1e2c1ef51bfe489660e2a1b1c997f550cddb8bf09634cbdfc6c17bb0a1d6096ad94fe92e02cc5bf61e6b4bbf4d3a91704e9c15e672f5f3ab4a9766257d395 -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/md5/c9d70905fe2dfde034855eab75d10339 -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.asserts.tar.gz/sha512/58e653a04f75078767de6d4a65086eca681f27d4c579fee518ae974d47252699bc217a150c5e688f69d7444670a3812ad0edebab2886a5c4ce501d2570e38cda -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/md5/c7342d2bc6f0004c78b8e768da06d332 -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx03-llvm_version+17.tar.gz/sha512/c1736f186925c7c21a97570b0346ca235d20651f7329ecd4142737473ce67b98a36434c02b384e3b698990238b6118f164601af5d91895bbfcab69397bc6b85f -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/md5/a90df960291596424b7291a68f632404 -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.asserts.tar.gz/sha512/ee002ab1b08078d02b5f1624ad2b6590d1937ee4c7d0c78b03b6dab8c7906d46f5373b8d5fbb76460ed8782ed85c84b12ac4b139a84bc73d2c17064930a668c6 -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/md5/e1928993bffba68c277f7bf6e1f537ad -libLLVM.v17.0.6+4.x86_64-w64-mingw32-cxx11-llvm_version+17.tar.gz/sha512/c18ee72b3187b1bb5184e5047164338d2a52eec626db44a09a77c23889db8a39e417020f3c50feea0f180aef39f2f23fff4d3324fa7030e4feef6a4033fc4c70 -llvm-julia-17.0.6-4.tar.gz/md5/3c69462bf7ba6219955dbc9e7e0c52ab -llvm-julia-17.0.6-4.tar.gz/sha512/aa96b3d01d3c2c86b79712a13f1abaee8dc95b63c8c7733588c2d5709bb72e2e835909af5a907c77b5d99d69ec69f97cf567d706d11d5f54d4c6b8536fc7762f +libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/2ea6046caf5a3d519ab1c3309a2eea31 +libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/079720b30c61ded8499eefdb314477d58bd121e9f326d98696ee39b2ed91f806d5f67e68b6fbef8613a992175fe34694e5efe83e87ef3bfbed67d6b7fc41ebf9 +libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/62c49bc7767d1ff114dc6b6a996449ae +libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/c708472b325cd73b94e10003bf3267b0ecbf3627072302fb22e78336974f2c7855c8597420efc954bca30aee17cec55277aa0c95a01cfff38d5d77df50c807f7 +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/766a2de98d275877bb676ff1f23e972f +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/3b353ea038fafefc13ccb4a81c7242d569c206362605be374fb312cb495f385796d052c3a7e08c7fe6ecaa3018e2a7e3dfa43d71a8c3a94987f7dc7aa378fd22 +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0684a6b210b799a8a0f45a286f3dfcc5 +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4221e2d74117bd7e89aba2945030c1507e51999b236814fd23036565364c328392e87032daf1b9fe274ed89fcf9a6dcd203f0f1c8602c2a08d3fcfa189a5fefe +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b460256e923637e5107d67859eb60ba +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7d3f2736afe4022842529b1355cf9914b7a1c7b1e261f814a4523ad30a0cf0189056d5117a06720bbb7a844a435bb632ddbda2daadbf7e01c0120452cd13e6a3 +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c2b13a6a296adbb4be91dd3bb5be0877 +libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9086937e718125afd535b0066ee08a3523161a94fa7ef3c9a3e86bfe760f251b6ea7b035888e61a0e7f192ed25c9bd0f4dc153df86e08569e7067a7a30ba48c5 +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/758d33fe0b2b3d0371708614365450e8 +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/79a662f72ba1b89b373d1d143ee880a12cb128211e79182e7befe8b3e50298b594de2ce489ca8bcdeadb17fceee811622f8bfcbc3e232cefdaf9927177469eec +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2dcbb811be8985bfed3c8b37733c0d40 +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/17f6fbd96ed5029f360c101cedad127881e14b42498d66f717448d99ca1909057ae79169d934e08157edcc7467db4b3941bdda26a2e9f42645963eec51f27e29 +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/bd3b904b5f9464aaaf87c41b899c8ca5 +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/fa99e8025419a18f548f658ea589771c2803480c3cb3a25cfb75e26ed0993b7b37bba204d7cba1475319a71159813b2b58a3b3327ba24d264cf80ef24263628d +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/b4f9038d5c3c13207111ee1a9a918cba +libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/e8b97bee30f597cc06d31175e12f0c2035aef0054e8abdb431f31b1e9d440d561bd9bc6637a403441aa7f3e1d2a46c600734e17e3b7ed0ae899c92df91758780 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/06d8e634b4a6914efc18b7962df52021 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/cf6aeed1eaf652e5830e34dd2ba88abc33668953281146106bbfdbc92f5f225645f00ff5b4a0eb902baf904362ab4eb32192fa50ee5b2672e8b031fe2550f9a8 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/53e83804b63e6ae4d0f1c97abcbbd1c8 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/45b3ee9b105ef2ef106fa8ac7b8e902cd1d6bf3c9bfb57edeca9e14f1654714d23fb086b369a9fd3cbb828c04fee4cfe80d2b2a2bfaa852d3ac65c0d213d8c62 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/91b6cf00564053d385e30b34e5b8778e +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9111f3f02b49bf78340c9b0c5c1325a1ca09b62c83aefece1121573dcc21dce095060351f18997971e5cfbaab346cb12c75cdc0fbe8fa92aca2e8a68b5f5f577 +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/f6c91b71dfd73c7301a4e3de48e072de +libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/581d7e1e4d85aeaf082fa31555074471705e391de0771bf66665807afb5192c79c481ca30e73a25f4e2d48d4d325f0198e39bcbfaed2c9bc7477ee917667f5ce +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/ce41ee46959e5e3a17b6c99293afedb7 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/73d8c5af750ea9deef822aec58d8697243ca154bc4435ac0b0ab8c90fc97750e91fa55f8de7b8283eb1ab19951cda3e3c4c60834bcf13730163e593126a8eb57 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/67ed5b654852dad400aef17fb542703f +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/07f70c57e27eea37f520f6f0a954b54d2506530d5eb5a74e5a8526ba8ef55a948073c49037544b602d03d0aa482704292eac943f0a83421386ccbfbf22ee8510 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8bd88d49ce21e5b63af6f77782eed4 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/cef1c561ae388b2baa08e39dc195989cb795d8a2747f5f11e0dc9d9e107b9e99dbba465335376beff2e1b326512f6afc962775e0b246f3edcfadf509235cabd8 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/5fbf26d20b2ce3f61edc9a9ca2eb5284 +libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/2c564c95d648458b9a0f0c963246cf5564c625107682f680390b6db5fde0e2b15a964fd3fd23734b5b2bb135db1fc698812d61b3f275710593f4defaee4a9c23 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c81bc29a75acf4f806f3eb13bf890604 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c8c922a0a4fefd549f1c2ba396a3cab9cf7738aa82e7ccf7ca29c090260e2d73ec45d6f2b07173d584f6074b10fa04052114deef6ecb6f53ea87f1924074137a +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/1fcb40ba1a427105b4e7d13a6c11dc78 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/392c9ee85ba7ab6697bb8979c7f443d1d25f7ac9178e96a886401cfc68d75a43ce98bf3038a7ba70a9a990f65e604d38e043472cec3badb25fbd1b38cfbb7162 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/427a19eaf69725d11bb33f48de9cb205 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/542e209b10c13d8dca867247a7414f84adb832f40051fcbdf0dcb09bc9664a77248e1b0ea1687805847dd9f5a05b86475dd76aba427c9a1bc83f8502444c60bd +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/ab34bfa2950014936edd13a7b5db8170 +libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/6376b25d0278e5c97581480fb4d54371b09a08be88f4cc39d2c7b3875f1189cef60c1be6bea5e12b0cf306cef8b394bc7d00f8b0fd95d749bd1b4eb318af7e15 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/cb6300fe87fd7cb9840f3bc44af26878 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/a7984cd90fef55559142fc05d91b0da1f37f77f25214e93ff7641b7c3958f08dc7c082611915dbfda4bbbaa392656ac8604d4f75369777dacfb78baee2f99b16 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/b8a4e8ef43340e9cbdf5e4479c6a5a56 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/fc249f2b666c8a8129e05ea08c773cbeb7af6d37791f271461eedd99adcfc5082e8609ed096d8a46edd1e73505352712a41e0ddc247a371f78227aab01fbe0f3 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5864689df3298be4b1b4df1ae0412d3a +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/8f32f73e366c3a6993fa8d6b8cd1a9391611b0644cd4a77a4f7a235c037fdb75308d99b5a23ada6e4a73ed5fbd8f929a981d6bf317d79d52396220c221619303 +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/6bf798476c4e94716cc47a95580104ad +libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/9dbd27a000dd3c3dda9047d366a667c4b179cc61582525adb0f8227e8055413ce46efcbc1530305400239656e2f1016fb8833fb7f4734714078e035d388f3531 +libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/66e2889f86ae6bc1977419e6d9be729e +libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d0cac798c4979b4d818d36596b173e523cba3f41ff7ab1e2111f6a75c3e819e563e207a547328f005c5a93c7f8f88c17bf43c1139b5c2690df4f1d719f82920a +libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0534b72d6d33c8573f79dce8a2a5a6e6 +libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/6beaf1b45eec8b46fbf92f692f53e6df40bf48e50589aeb5ef99240a5a3ec9089ffb350dda6df24530937d613bf6d2cc4da76e92921ea00def9d2d38ac5bbeba +libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2cf9a1ca20472179ce4a9eb3a949457b +libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/cebae06ccee12a14d20d3056ce0519b1e774e3c9d9200a783262fcc40aee6d7aabfb08714bf53b88e03d8b09a96d3cda248a70c16188f8c707b291642998262a +libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/4712f6a46e0ff407ece958a7701511b9 +libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9a0a2dfa2076b93027f766277a6890cf94d67c131697f74945e92cf13ae64e84c09d3dd744498986fb22ad5e5465300aa9c8ae6632fcf919a0932515edfcc1e6 +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b944ae477232ef10d213b4c7743280fb +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/25ff757620baaf6fbacb375b103dc0dd9af6a23c3d3bca567c182a6357a367ca125d7b6c66927d7db23816865b6ec783157352fba08532336de467be80efcb9c +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/52345a44b3ac74b3cdf93852bbc63710 +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/3e5b449b0f1bab302c45f9ee9f04d2cfbb01ce24e86096aa610fdf360ad65828f1b73734beb28b3d3c249ba8ef657d2663c5492940504f47c973038733b15248 +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/36e058b96771b4cf77e29b800227fa03 +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/98873cb2963c4469b0f69ad1d9d9e27056aabfb46a2642dfa3507b7fe2f0b0fc41c3991a2543125291783699e39fcbcac0bd6e92fa8f0df97609a85c340fd25b +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/3b3823fbafabea289a769958f633dcdb +libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/91a9c1ad6f37cb1186ba3392935fb55d49e0f8d6afc768cf881886f9b1d8b0a2b0ecf0c81a8e32e36d32cac04c065ac852bdb95ba5ff6780c00a763583a02973 +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/bbf060d61b294b86f7e3dde381b00b8a +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/632372d41f6e400a10fae27c6cd06a5a344cfb5902cad7928cb4133f14f36f0a3373e69e73ce9baf52f518340593c3a5a16173ef59a1878e6300e9975aeaa157 +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3d730b713e01cdb5a7a5a46028afd41b +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/052ab4fa7ac3b2c430601753ab078cdc9fd6db7f65ee0b76bb05473f4c5b99ec8919ad9d347425f1928cf619548e992c86ba97f9994218f50bca617e43d2f0d9 +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/bf9dcb92ba8c031ae62ed4434fd5447f +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/e53be14dd02a2cef8eccafb9301d29c51d652c635703529c1444947002993f6639083eb8bef13af21c9796717ce4b3129dcdcbe2751a1173d39e321db8f6e3c7 +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/b5cab0fc7c6643c6dd161f1e553ef1a0 +libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/4032634449e2669479761c4323096b152f8df4948e3a97eea10f0b400fbf2a00d1edda59b74a714b62c4e204b113d8ecda78d828c3344ebe8bd750d14b3c4c7d +libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/054e06d882173ede2886c510e8519c80 +libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/eb97ec25354badcac1b8a4a09fd9e04cfbb7d35493c54cff82af9ffa4c2dc5070c9232a86e900d6eb9acb03f1c572fcde8d2a865477bf6c9fbfc139763a9dd1c +libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/f1c23200365b659f0dc07cc6d0a32c60 +libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/fad13fef7e7584b3f756fce9125950e788e79608cf5d0c023cb8f8a4e79001afefa8060f7866875e4861a268b3020e50305e66bf472360c1d92fce12d7a81ba9 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/69564913bae176a167d24d3291ef7af7 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/b8eeb86b66d767218e59671bdd597623238eea72319913c2ac5e116faec3f4c13739a24f3b95338ed857ec29e714dc0308e4ddbfe359332b3c27ad5235052342 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/bc9d5637fe30f21d2231a98371e798e4 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4efbc2823322abe80d0134d35926767bd9cab717cde9308726a6a8891e5a707476138888c695ed399e3dddb57baf17abbc43a0a338cea2e5c0f472ab427c12e3 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/8492ff91e6dbd1a66edd8aaf0390a582 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6443bd2fa9c5beecc2b002c26595f2cf3a8e2ea5eb49aa4c00f7252a6623fe0f8c01824941ebe5475460641285c4e56a5203056c1b93a78250b7e48fb5ac9e00 +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/6918c9978fd8b5887c66eee76950478d +libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/d455a4f433bf3ea1b5100b9d45199bc785e4b6fbc7659bf06cbde6ada471134e7d4243d3a3a1f71d579126ef8371d70e59f174e124b3ff8d4842e9ee83e2dea4 +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/075f87d106dd95c8e9c6e7e157b5e9db +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8132379d8f44a21082c7a90f58a7dffb0c6ee725efd58a959d4023787411b080d72913bb1e89a35072f97aaf1ca512ab1d027b37eaed819e3c053d7a0cf64269 +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/4cfc2838a77f05883f82e50b3723dcfe +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/20079c81cd6a4020b087485be1ab4928b3bd3e1a53728cc98137a35b969484278093bc75a9e51ddfd8331556577c5fb3109d74dc2eccffa93b5390e0fabff2b1 +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8cbf00631bd4540b7335a86302a1fe +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/51ba9a4b74b740905cee4baf7f4e5f3620ed81e0746f49cd352d874ebedab95277c5031123f880c9239b7dbf505b10f6531f79c8a6b0482a652b8324f4137cf5 +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/11010cc2d58b1a8c6a6e7bc24df0c0db +libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/a6bdd9a2a2fa9a572e74ced69c3ce9d1b84cde18155ec9bc7dfbaba411ee6c43d229e6fb333eff66fb63b632b485b46b7cb1657c0c49d9d9bb849fa13f0bbc7b +libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/8afe26d16d9fdb0fe6c0248c51b4f053 +libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/32a92685f417c1887aef3cd8a9cadccc4de3e560ba8fc42e8db721f273a3451927b24dc4a2c2e83446e32a84d47f714fc3c22ce71989f2e97c5ca23a1783b8d6 +libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/59d8d911907127ff56f5eafcd8663300 +libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/9b0bf6f9d8d32ccbec349c249b79fd0fa3b4949c04b69c9d408f19dfa3b4f00e5cfa51b798234721f72f2793161d6af6491856e10e6a507976b0da6ed7a8065b +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b0d9a7eca92d40ecbfa47461d52659e2 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/dc4a91e164d88ff51b4a642b556d5767156f28d1efafa533f5d7c619e05535e2000afb2ea47469a90f5a19f970e8f0522f35d59ec250e2f9b42ce22fadb9ffd3 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/92a60309ad33391415c6703edbbd5423 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/2fe90ac804d94bcf0d4058a8b8f0c274e405ffee7df0175f5e7ccd5014b29a813af48152870e1af0a79df8d3eec3118c233bc4f5b3f8439fd9792931140ee944 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/0964df17cb98d2d869a33468477f9901 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/2c062acd62175d32dda773e9116608ced814a64ab06ea73f89958437178e2603b268638e88162fb81c22e5947cf4cc925b1af10c6f9320be22c92b279b278992 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/7dfb8e61e972c66f1d754cb979bc0309 +libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/d462b6fe7aea75f6fee6c5c2f24576569b5deac8027fb88240e16c55a54d68b7dcb06b3ec4ab514616fb88549fc2f10fb1d587a641d6f29fa66273904bb9cfd8 +llvm-julia-18.1.7-2.tar.gz/md5/5c0ae4abc4ce31a86d5d6d4ecabc2683 +llvm-julia-18.1.7-2.tar.gz/sha512/b4d1dde929a8670eec1a9b25abe23fbc926a922e61b60ed99b52b440cd07cb026e7f746878292db4cd0cb422d9b87ecc4ee4b2b141f8e9411855d18da51facb9 llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 diff --git a/deps/clang.version b/deps/clang.version index 76ddb503b3c8c..fcd55b72de5ff 100644 --- a/deps/clang.version +++ b/deps/clang.version @@ -3,4 +3,4 @@ ## jll artifact # Clang (paired with LLVM, only here as a JLL download) CLANG_JLL_NAME := Clang -CLANG_JLL_VER := 17.0.6+4 +CLANG_JLL_VER := 18.1.7+2 diff --git a/deps/lld.version b/deps/lld.version index 431c1b7a75032..3ca9960164e27 100644 --- a/deps/lld.version +++ b/deps/lld.version @@ -2,4 +2,4 @@ ## jll artifact LLD_JLL_NAME := LLD -LLD_JLL_VER := 17.0.6+4 +LLD_JLL_VER := 18.1.7+2 diff --git a/deps/llvm-tools.version b/deps/llvm-tools.version index 3609c54ddc98f..1fcc8944dc769 100644 --- a/deps/llvm-tools.version +++ b/deps/llvm-tools.version @@ -3,5 +3,5 @@ ## jll artifact # LLVM_tools (downloads LLVM_jll to get things like `lit` and `opt`) LLVM_TOOLS_JLL_NAME := LLVM -LLVM_TOOLS_JLL_VER := 17.0.6+4 -LLVM_TOOLS_ASSERT_JLL_VER := 17.0.6+4 +LLVM_TOOLS_JLL_VER := 18.1.7+2 +LLVM_TOOLS_ASSERT_JLL_VER := 18.1.7+2 diff --git a/deps/llvm.version b/deps/llvm.version index c02a52008fe25..8e4180ef5a277 100644 --- a/deps/llvm.version +++ b/deps/llvm.version @@ -2,14 +2,14 @@ ## jll artifact LLVM_JLL_NAME := libLLVM -LLVM_ASSERT_JLL_VER := 17.0.6+4 +LLVM_ASSERT_JLL_VER := 18.1.7+2 ## source build # Version number of LLVM -LLVM_VER := 17.0.6 +LLVM_VER := 18.1.7 # Git branch name in `LLVM_GIT_URL` repository -LLVM_BRANCH=julia-17.0.6-4 +LLVM_BRANCH=julia-18.1.7-2 # Git ref in `LLVM_GIT_URL` repository -LLVM_SHA1=julia-17.0.6-4 +LLVM_SHA1=julia-18.1.7-2 ## Following options are used to automatically fetch patchset from Julia's fork. This is ## useful if you want to build an external LLVM while still applying Julia's patches. @@ -18,6 +18,6 @@ LLVM_APPLY_JULIA_PATCHES := 0 # GitHub repository to use for fetching the Julia patches to apply to LLVM source code. LLVM_JULIA_DIFF_GITHUB_REPO := https://github.com/llvm/llvm-project # Base GitHub ref for generating the diff. -LLVM_BASE_REF := llvm:llvmorg-17.0.6 +LLVM_BASE_REF := llvm:llvmorg-18.1.7 # Julia fork's GitHub ref for generating the diff. -LLVM_JULIA_REF := JuliaLang:julia-17.0.6-4 +LLVM_JULIA_REF := JuliaLang:julia-18.1.7-2 diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 815e305d14376..48afa360c0037 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -19,24 +19,9 @@ // analysis passes #include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -1164,7 +1149,11 @@ static AOTOutputs add_output_impl(Module &M, TargetMachine &SourceTM, ShardTimer raw_svector_ostream OS(out.obj); legacy::PassManager emitter; addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); +#if JL_LLVM_VERSION >= 180000 + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CodeGenFileType::ObjectFile, false)) +#else if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_ObjectFile, false)) +#endif jl_safe_printf("ERROR: target does not support generation of object files\n"); emitter.run(M); timers.obj.stopTimer(); @@ -1175,7 +1164,11 @@ static AOTOutputs add_output_impl(Module &M, TargetMachine &SourceTM, ShardTimer raw_svector_ostream OS(out.asm_); legacy::PassManager emitter; addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); +#if JL_LLVM_VERSION >= 180000 + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CodeGenFileType::AssemblyFile, false)) +#else if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_AssemblyFile, false)) +#endif jl_safe_printf("ERROR: target does not support generation of assembly files\n"); emitter.run(M); timers.asm_.stopTimer(); @@ -1632,7 +1625,11 @@ void jl_dump_native_impl(void *native_code, jl_ExecutionEngine->getTargetOptions(), RelocModel, CMModel, +#if JL_LLVM_VERSION >= 180000 + CodeGenOptLevel::Aggressive // -O3 TODO: respect command -O0 flag? +#else CodeGenOpt::Aggressive // -O3 TODO: respect command -O0 flag? +#endif )); fixupTM(*SourceTM); auto DL = jl_create_datalayout(*SourceTM); @@ -1892,26 +1889,31 @@ void jl_dump_native_impl(void *native_code, JL_TIMING(NATIVE_AOT, NATIVE_Write); object::Archive::Kind Kind = getDefaultForHost(TheTriple); +#if JL_LLVM_VERSION >= 180000 +#define WritingMode SymtabWritingMode::NormalSymtab +#else +#define WritingMode true +#endif #define WRITE_ARCHIVE(fname, field, prefix, suffix) \ - if (fname) {\ - SmallVector archive; \ - SmallVector filenames; \ - SmallVector buffers; \ - for (size_t i = 0; i < threads; i++) { \ - filenames.push_back((StringRef("text") + prefix + "#" + Twine(i) + suffix).str()); \ - buffers.push_back(StringRef(data_outputs[i].field.data(), data_outputs[i].field.size())); \ - } \ - filenames.push_back("metadata" prefix suffix); \ - buffers.push_back(StringRef(metadata_outputs[0].field.data(), metadata_outputs[0].field.size())); \ - if (z) { \ - filenames.push_back("sysimg" prefix suffix); \ - buffers.push_back(StringRef(sysimg_outputs[0].field.data(), sysimg_outputs[0].field.size())); \ - } \ - for (size_t i = 0; i < filenames.size(); i++) { \ - archive.push_back(NewArchiveMember(MemoryBufferRef(buffers[i], filenames[i]))); \ - } \ - handleAllErrors(writeArchive(fname, archive, true, Kind, true, false), reportWriterError); \ - } + if (fname) {\ + SmallVector archive; \ + SmallVector filenames; \ + SmallVector buffers; \ + for (size_t i = 0; i < threads; i++) { \ + filenames.push_back((StringRef("text") + prefix + "#" + Twine(i) + suffix).str()); \ + buffers.push_back(StringRef(data_outputs[i].field.data(), data_outputs[i].field.size())); \ + } \ + filenames.push_back("metadata" prefix suffix); \ + buffers.push_back(StringRef(metadata_outputs[0].field.data(), metadata_outputs[0].field.size())); \ + if (z) { \ + filenames.push_back("sysimg" prefix suffix); \ + buffers.push_back(StringRef(sysimg_outputs[0].field.data(), sysimg_outputs[0].field.size())); \ + } \ + for (size_t i = 0; i < filenames.size(); i++) { \ + archive.push_back(NewArchiveMember(MemoryBufferRef(buffers[i], filenames[i]))); \ + } \ + handleAllErrors(writeArchive(fname, archive, WritingMode, Kind, true, false), reportWriterError); \ + } WRITE_ARCHIVE(unopt_bc_fname, unopt, "_unopt", ".bc"); WRITE_ARCHIVE(bc_fname, opt, "_opt", ".bc"); diff --git a/src/ccall.cpp b/src/ccall.cpp index 3c2857608c163..db7bcb3a408d0 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -2108,7 +2108,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (!isa(llvmf) || cast(llvmf)->isIntrinsic() || cast(llvmf)->getFunctionType() != functype) llvmf = NULL; } - else if (f_name.startswith("llvm.")) { + else if (f_name.starts_with("llvm.")) { // compute and verify auto-mangling for intrinsic name auto ID = Function::lookupIntrinsicID(f_name); if (ID != Intrinsic::not_intrinsic) { diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 9caff014c7703..ecaeb460ebf91 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -767,7 +767,7 @@ bool GCChecker::isFDAnnotatedNotSafepoint(const clang::FunctionDecl *FD, const S SourceLocation Loc = FD->getLocation(); StringRef Name = SM.getFilename(Loc); Name = llvm::sys::path::filename(Name); - if (Name.startswith("llvm-")) + if (Name.starts_with("llvm-")) return true; return false; } @@ -911,9 +911,9 @@ bool GCChecker::isSafepoint(const CallEvent &Call, CheckerContext &C) const { if (FD->getBuiltinID() != 0 || FD->isTrivial()) isCalleeSafepoint = false; else if (FD->getDeclName().isIdentifier() && - (FD->getName().startswith("uv_") || - FD->getName().startswith("unw_") || - FD->getName().startswith("_U")) && + (FD->getName().starts_with("uv_") || + FD->getName().starts_with("unw_") || + FD->getName().starts_with("_U")) && FD->getName() != "uv_run") isCalleeSafepoint = false; else @@ -1050,13 +1050,13 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call, // global roots. StringRef FDName = FD->getDeclName().isIdentifier() ? FD->getName() : ""; - if (FDName.startswith("jl_box_") || FDName.startswith("ijl_box_")) { + if (FDName.starts_with("jl_box_") || FDName.starts_with("ijl_box_")) { SVal Arg = Call.getArgSVal(0); if (auto CI = Arg.getAs()) { const llvm::APSInt &Value = CI->getValue(); bool GloballyRooted = false; const int64_t NBOX_C = 1024; - if (FDName.startswith("jl_box_u") || FDName.startswith("ijl_box_u")) { + if (FDName.starts_with("jl_box_u") || FDName.starts_with("ijl_box_u")) { if (Value < NBOX_C) { GloballyRooted = true; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 875beb7c287dc..3a363dd3fd0c1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2180,7 +2180,7 @@ static GlobalVariable *get_pointer_to_constant(jl_codegen_params_t &emission_con gv = get_gv(gvname); } } - assert(gv->getName().startswith(name.str())); + assert(gv->getName().starts_with(name.str())); assert(val == gv->getInitializer()); return gv; } @@ -8846,7 +8846,7 @@ static jl_llvm_functions_t !jl_is_submodule(mod, jl_core_module)); }; auto in_tracked_path = [] (StringRef file) { // falls within an explicitly set file or directory - return jl_options.tracked_path != NULL && file.startswith(jl_options.tracked_path); + return jl_options.tracked_path != NULL && file.starts_with(jl_options.tracked_path); }; bool mod_is_user_mod = in_user_mod(ctx.module); bool mod_is_tracked = in_tracked_path(ctx.file); @@ -9638,7 +9638,7 @@ static jl_llvm_functions_t // make sure that anything we attempt to call has some inlining info, just in case optimization messed up // (except if we know that it is an intrinsic used in our prologue, which should never have its own debug subprogram) Function *F = call->getCalledFunction(); - if (!in_prologue || !F || !(F->isIntrinsic() || F->getName().startswith("julia.") || &I == restTuple)) { + if (!in_prologue || !F || !(F->isIntrinsic() || F->getName().starts_with("julia.") || &I == restTuple)) { I.setDebugLoc(topdebugloc); } } diff --git a/src/coverage.cpp b/src/coverage.cpp index c061276e66fd9..685370198ff13 100644 --- a/src/coverage.cpp +++ b/src/coverage.cpp @@ -207,7 +207,7 @@ extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) { if (output) { StringRef output_pattern(output); - if (output_pattern.endswith(".info")) + if (output_pattern.ends_with(".info")) write_lcov_data(coverageData, jl_format_filename(output_pattern.str().c_str())); } else { diff --git a/src/datatype.c b/src/datatype.c index e7ee15a63f56e..8a7b256db4bef 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -936,6 +936,10 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); uint32_t nbytes = (nbits + 7) / 8; uint32_t alignm = next_power_of_two(nbytes); +# if defined(_CPU_X86_) && !defined(_OS_WINDOWS_) + if (alignm == 8) + alignm = 4; +# endif if (alignm > MAX_ALIGN) alignm = MAX_ALIGN; // memoize isprimitivetype, since it is much easier than checking diff --git a/src/disasm.cpp b/src/disasm.cpp index b24c374607113..b71503c3f7a77 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -1224,7 +1224,11 @@ jl_value_t *jl_dump_function_asm_impl(jl_llvmf_dump_t* dump, char emit_mc, const addTargetPasses(&PM, TM->getTargetTriple(), TM->getTargetIRAnalysis()); if (emit_mc) { raw_svector_ostream obj_OS(ObjBufferSV); +#if JL_LLVM_VERSION >= 180000 + if (TM->addPassesToEmitFile(PM, obj_OS, nullptr, CodeGenFileType::ObjectFile, false, nullptr)) +#else if (TM->addPassesToEmitFile(PM, obj_OS, nullptr, CGFT_ObjectFile, false, nullptr)) +#endif return jl_an_empty_string; TSM->withModuleDo([&](Module &m) { PM.run(m); }); } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 487e6a0462500..442103c91be0f 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -3,7 +3,7 @@ #include "llvm-version.h" #include "platform.h" #include -#include +#include #include "llvm/IR/Mangler.h" #include @@ -42,7 +42,11 @@ using namespace llvm; #include "julia_assert.h" #include "processor.h" +#if JL_LLVM_VERSION >= 180000 +# include +#else # include +#endif # include # include # include @@ -564,6 +568,19 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, return jl_an_empty_string; } +#if JL_LLVM_VERSION >= 180000 +CodeGenOptLevel CodeGenOptLevelFor(int optlevel) +{ +#ifdef DISABLE_OPT + return CodeGenOptLevel::None; +#else + return optlevel == 0 ? CodeGenOptLevel::None : + optlevel == 1 ? CodeGenOptLevel::Less : + optlevel == 2 ? CodeGenOptLevel::Default : + CodeGenOptLevel::Aggressive; +#endif +} +#else CodeGenOpt::Level CodeGenOptLevelFor(int optlevel) { #ifdef DISABLE_OPT @@ -575,6 +592,7 @@ CodeGenOpt::Level CodeGenOptLevelFor(int optlevel) CodeGenOpt::Aggressive; #endif } +#endif static auto countBasicBlocks(const Function &F) JL_NOTSAFEPOINT { @@ -589,7 +607,7 @@ static Expected validateExternRelocations(orc::ThreadSafe auto F = dyn_cast(&GO); if (!F) return false; - return F->isIntrinsic() || F->getName().startswith("julia."); + return F->isIntrinsic() || F->getName().starts_with("julia."); }; // validate the relocations for M (only for RuntimeDyld, JITLink performs its own symbol validation) auto Err = TSM.withModuleDo([isIntrinsicFunction](Module &M) JL_NOTSAFEPOINT { @@ -1157,7 +1175,7 @@ namespace { { if (*jl_ExecutionEngine->get_dump_llvm_opt_stream()) { for (auto &F : M.functions()) { - if (F.isDeclaration() || F.getName().startswith("jfptr_")) { + if (F.isDeclaration() || F.getName().starts_with("jfptr_")) { continue; } // Each function is printed as a YAML object with several attributes @@ -1210,7 +1228,7 @@ namespace { // Print LLVM function statistics _after_ optimization ios_printf(stream, " after: \n"); for (auto &F : M.functions()) { - if (F.isDeclaration() || F.getName().startswith("jfptr_")) { + if (F.isDeclaration() || F.getName().starts_with("jfptr_")) { continue; } Stat(F).dump(stream); @@ -1397,7 +1415,7 @@ struct JuliaOJIT::DLSymOptimizer { void operator()(Module &M) { for (auto &GV : M.globals()) { auto Name = GV.getName(); - if (Name.startswith("jlplt") && Name.endswith("got")) { + if (Name.starts_with("jlplt") && Name.ends_with("got")) { auto fname = GV.getAttribute("julia.fname").getValueAsString().str(); void *addr; if (GV.hasAttribute("julia.libname")) { @@ -1651,7 +1669,7 @@ JuliaOJIT::JuliaOJIT() DL.getGlobalPrefix(), [&](const orc::SymbolStringPtr &S) { const char *const atomic_prefix = "__atomic_"; - return (*S).startswith(atomic_prefix); + return (*S).starts_with(atomic_prefix); }))); } } @@ -2208,8 +2226,15 @@ static void jl_decorate_module(Module &M) { // Add special values used by debuginfo to build the UnwindData table registration for Win64 // This used to be GV, but with https://reviews.llvm.org/D100944 we no longer can emit GV into `.text` // TODO: The data is set in debuginfo.cpp but it should be okay to actually emit it here. - M.appendModuleInlineAsm("\ - .section .text \n\ + std::string inline_asm = "\ + .section "; + inline_asm += +#if JL_LLVM_VERSION >= 180000 + ".ltext,\"ax\",@progbits"; +#else + ".text"; +#endif + inline_asm += "\n\ .type __UnwindData,@object \n\ .p2align 2, 0x90 \n\ __UnwindData: \n\ @@ -2220,7 +2245,9 @@ static void jl_decorate_module(Module &M) { .p2align 2, 0x90 \n\ __catchjmp: \n\ .zero 12 \n\ - .size __catchjmp, 12"); + .size __catchjmp, 12"; + + M.appendModuleInlineAsm(inline_asm); } } diff --git a/src/jitlayers.h b/src/jitlayers.h index 101f5714abd11..107782e354d4a 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -26,6 +26,7 @@ #include "julia_internal.h" #include "platform.h" #include "llvm-codegen-shared.h" +#include "llvm-version.h" #include #include @@ -645,4 +646,8 @@ void optimizeDLSyms(Module &M); // NewPM #include "passes.h" +#if JL_LLVM_VERSION >= 180000 +CodeGenOptLevel CodeGenOptLevelFor(int optlevel) JL_NOTSAFEPOINT; +#else CodeGenOpt::Level CodeGenOptLevelFor(int optlevel) JL_NOTSAFEPOINT; +#endif diff --git a/src/julia.h b/src/julia.h index 29899f2985a4f..cbe60e78c2d24 100644 --- a/src/julia.h +++ b/src/julia.h @@ -27,22 +27,10 @@ #include #ifndef _OS_WINDOWS_ -# define jl_jmp_buf sigjmp_buf -# if defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_WASM_) -# define MAX_ALIGN 8 -# elif defined(_CPU_AARCH64_) -// int128 is 16 bytes aligned on aarch64 -# define MAX_ALIGN 16 -# elif defined(_P64) -// Generically we assume MAX_ALIGN is sizeof(void*) -# define MAX_ALIGN 8 -# else -# define MAX_ALIGN 4 -# endif + #define jl_jmp_buf sigjmp_buf #else -# include "win32_ucontext.h" -# define jl_jmp_buf jmp_buf -# define MAX_ALIGN 8 + #include "win32_ucontext.h" + #define jl_jmp_buf jmp_buf #endif // Define the largest size (bytes) of a properly aligned object that the diff --git a/src/julia_internal.h b/src/julia_internal.h index 2ec4c9da7452b..d4d1a3239785c 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -17,6 +17,8 @@ #include #include #include +#include + #if !defined(_WIN32) #include #else @@ -98,6 +100,26 @@ JL_DLLIMPORT void __tsan_destroy_fiber(void *fiber); JL_DLLIMPORT void __tsan_switch_to_fiber(void *fiber, unsigned flags); #endif +#ifndef _OS_WINDOWS_ + #if defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_WASM_) + #define MAX_ALIGN 8 + #elif defined(_CPU_AARCH64_) || (JL_LLVM_VERSION >= 180000 && (defined(_CPU_X86_64_) || defined(_CPU_X86_))) + // int128 is 16 bytes aligned on aarch64 and on x86 with LLVM >= 18 + #define MAX_ALIGN 16 + #elif defined(_P64) + // Generically we assume MAX_ALIGN is sizeof(void*) + #define MAX_ALIGN 8 + #else + #define MAX_ALIGN 4 + #endif +#else + #if JL_LLVM_VERSION >= 180000 + #define MAX_ALIGN 16 + #else + #define MAX_ALIGN 8 + #endif +#endif + #ifndef alignof # ifndef __cplusplus # ifdef __GNUC__ diff --git a/src/llvm-cpufeatures.cpp b/src/llvm-cpufeatures.cpp index 2539c5cd2e37c..05d62adc57926 100644 --- a/src/llvm-cpufeatures.cpp +++ b/src/llvm-cpufeatures.cpp @@ -94,7 +94,7 @@ bool lowerCPUFeatures(Module &M) JL_NOTSAFEPOINT for (auto &F: M.functions()) { auto FN = F.getName(); - if (FN.startswith("julia.cpu.have_fma.")) { + if (FN.starts_with("julia.cpu.have_fma.")) { for (Use &U: F.uses()) { User *RU = U.getUser(); CallInst *I = cast(RU); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 08600e24490b1..d544f182637b9 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -100,11 +100,11 @@ static uint32_t collect_func_info(Function &F, const Triple &TT, bool &has_vecca } if (auto callee = call->getCalledFunction()) { auto name = callee->getName(); - if (name.startswith("llvm.muladd.") || name.startswith("llvm.fma.")) { + if (name.starts_with("llvm.muladd.") || name.starts_with("llvm.fma.")) { flag |= JL_TARGET_CLONE_MATH; } - else if (name.startswith("julia.cpu.")) { - if (name.startswith("julia.cpu.have_fma.")) { + else if (name.starts_with("julia.cpu.")) { + if (name.starts_with("julia.cpu.have_fma.")) { // for some platforms we know they always do (or don't) support // FMA. in those cases we don't need to clone the function. // always_have_fma returns an optional diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index f29802b438e1e..07afa8c930deb 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -177,7 +177,7 @@ static bool processLoop(Loop &L, OptimizationRemarkEmitter &ORE, ScalarEvolution const MDString *S = dyn_cast(Op); if (S) { LLVM_DEBUG(dbgs() << "LSL: found " << S->getString() << "\n"); - if (S->getString().startswith("julia")) { + if (S->getString().starts_with("julia")) { if (S->getString().equals("julia.simdloop")) simd = true; if (S->getString().equals("julia.ivdep")) diff --git a/src/llvm_api.cpp b/src/llvm_api.cpp index d56fb3a0497fa..e98c375b711b3 100644 --- a/src/llvm_api.cpp +++ b/src/llvm_api.cpp @@ -21,6 +21,7 @@ #include #include +#if JL_LLVM_VERSION < 180000 namespace llvm { namespace orc { class OrcV2CAPIHelper { @@ -38,7 +39,7 @@ class OrcV2CAPIHelper { }; } // namespace orc } // namespace llvm - +#endif typedef struct JLOpaqueJuliaOJIT *JuliaOJITRef; typedef struct LLVMOrcOpaqueIRCompileLayer *LLVMOrcIRCompileLayerRef; @@ -46,8 +47,13 @@ typedef struct LLVMOrcOpaqueIRCompileLayer *LLVMOrcIRCompileLayerRef; DEFINE_SIMPLE_CONVERSION_FUNCTIONS(JuliaOJIT, JuliaOJITRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::JITDylib, LLVMOrcJITDylibRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ExecutionSession, LLVMOrcExecutionSessionRef) +#if JL_LLVM_VERSION >= 180000 +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::SymbolStringPoolEntryUnsafe::PoolEntry, + LLVMOrcSymbolStringPoolEntryRef) +#else DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::OrcV2CAPIHelper::PoolEntry, LLVMOrcSymbolStringPoolEntryRef) +#endif DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::IRCompileLayer, LLVMOrcIRCompileLayerRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::MaterializationResponsibility, LLVMOrcMaterializationResponsibilityRef) @@ -113,7 +119,11 @@ JL_DLLEXPORT_CODEGEN LLVMOrcSymbolStringPoolEntryRef JLJITMangleAndIntern_impl(JuliaOJITRef JIT, const char *Name) { +#if JL_LLVM_VERSION >= 180000 + return wrap(orc::SymbolStringPoolEntryUnsafe::take(unwrap(JIT)->mangle(Name)).rawPtr()); +#else return wrap(orc::OrcV2CAPIHelper::moveFromSymbolStringPtr(unwrap(JIT)->mangle(Name))); +#endif } JL_DLLEXPORT_CODEGEN const char * diff --git a/src/pipeline.cpp b/src/pipeline.cpp index f0dde6aa59a40..e01645cc1f154 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -19,18 +19,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include @@ -40,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +65,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index b9ad5e68111e5..d28e527ed44e8 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -839,7 +839,7 @@ template static inline bool try_read_procfs_line(llvm::StringRef line, const char *prefix, T &out, bool &flag, F &&reset) { - if (!line.startswith(prefix)) + if (!line.starts_with(prefix)) return false; if (flag) reset(); diff --git a/stdlib/LLD_jll/Project.toml b/stdlib/LLD_jll/Project.toml index 4f5e3a6659745..6a6cc72aa3c62 100644 --- a/stdlib/LLD_jll/Project.toml +++ b/stdlib/LLD_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLD_jll" uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "17.0.6+4" +version = "18.1.7+2" [deps] Zlib_jll = "83775a58-1f1d-513f-b197-d71354ab007a" @@ -10,7 +10,7 @@ Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] julia = "1.11" -libLLVM_jll = "17.0.6" +libLLVM_jll = "18.1.7" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/stdlib/libLLVM_jll/Project.toml b/stdlib/libLLVM_jll/Project.toml index f6d93dcb94042..a0eac13b3ab23 100644 --- a/stdlib/libLLVM_jll/Project.toml +++ b/stdlib/libLLVM_jll/Project.toml @@ -1,6 +1,6 @@ name = "libLLVM_jll" uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "17.0.6+4" +version = "18.1.7+2" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/test/llvmcall.jl b/test/llvmcall.jl index 98968bfcdf8bc..c83ac05b1ec48 100644 --- a/test/llvmcall.jl +++ b/test/llvmcall.jl @@ -70,13 +70,13 @@ end ret i32 %3""", Int32, Tuple{Int32, Int32}, Int32(1), Int32(2))) # llvmcall must be compiled to be called -# Test whether declarations work properly +#Since LLVM 18, LLVM does a best effort to automatically include the intrinsics function undeclared_ceil(x::Float64) llvmcall("""%2 = call double @llvm.ceil.f64(double %0) ret double %2""", Float64, Tuple{Float64}, x) end -@test_throws ErrorException undeclared_ceil(4.2) -@test_throws ErrorException undeclared_ceil(4.2) +@test undeclared_ceil(4.2) == 5.0 +@test undeclared_ceil(4.2) == 5.0 function declared_floor(x::Float64) llvmcall( diff --git a/test/llvmpasses/pipeline-prints.ll b/test/llvmpasses/pipeline-prints.ll index babd26c797a38..ecb70953026c2 100644 --- a/test/llvmpasses/pipeline-prints.ll +++ b/test/llvmpasses/pipeline-prints.ll @@ -298,12 +298,12 @@ attributes #2 = { inaccessiblemem_or_argmemonly } ; COM: Loop simplification makes the exit condition obvious ; AFTERLOOPSIMPLIFICATION: L35.lr.ph: -; AFTERLOOPSIMPLIFICATION-NEXT: add nuw nsw +; AFTERLOOPSIMPLIFICATION: add nuw nsw ; COM: Scalar optimization removes the previous add from the preheader -; AFTERSCALAROPTIMIZATION: L35.preheader: +; AFTERSCALAROPTIMIZATION: L35.lr.ph: ; AFTERSCALAROPTIMIZATION-NOT: add nuw nsw -; AFTERSCALAROPTIMIZATION-NEXT: br label %L35 +; AFTERSCALAROPTIMIZATION: br label %L35 ; COM: Vectorization does stuff ; AFTERVECTORIZATION: vector.body From 22e53624407bc443b9cb12a2380b119e3baf54e3 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 4 Aug 2024 06:21:32 -0400 Subject: [PATCH 005/548] REPL: enable import completion when in a macro (#55366) Fixes https://github.com/JuliaLang/julia/issues/55361 I think this regressed in https://github.com/JuliaLang/julia/pull/54719 given tests didn't include leading spaces/macros --- stdlib/REPL/src/REPLCompletions.jl | 9 ++++++--- stdlib/REPL/test/replcompletions.jl | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index bc8006ec2ed5d..609a7b4d81bc0 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -900,8 +900,11 @@ const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? " # Aux function to detect whether we're right after a using or import keyword function get_import_mode(s::String) + # allow all of these to start with leading whitespace and macros like @eval and @eval( + # ^\s*(?:@\w+\s*(?:\(\s*)?)? + # match simple cases like `using |` and `import |` - mod_import_match_simple = match(r"^\b(using|import)\s*$", s) + mod_import_match_simple = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s*$", s) if mod_import_match_simple !== nothing if mod_import_match_simple[1] == "using" return :using_module @@ -910,7 +913,7 @@ function get_import_mode(s::String) end end # match module import statements like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |` - mod_import_match = match(r"^\b(using|import)\s+([\w\.]+(?:\s*,\s*[\w\.]+)*),?\s*$", s) + mod_import_match = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s+([\w\.]+(?:\s*,\s*[\w\.]+)*),?\s*$", s) if mod_import_match !== nothing if mod_import_match.captures[1] == "using" return :using_module @@ -919,7 +922,7 @@ function get_import_mode(s::String) end end # now match explicit name import statements like `using Foo: |` and `import Foo: bar, baz|` - name_import_match = match(r"^\b(using|import)\s+([\w\.]+)\s*:\s*([\w@!\s,]+)$", s) + name_import_match = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s+([\w\.]+)\s*:\s*([\w@!\s,]+)$", s) if name_import_match !== nothing if name_import_match[1] == "using" return :using_name diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 15e3de2668ba1..3f8addcace73b 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -2238,6 +2238,26 @@ let s = "using .Iss" @test res @test "Issue52922" in c end +let s = " using .Iss" + c, r, res = test_complete_context(s) + @test res + @test "Issue52922" in c +end +let s = "@time using .Iss" + c, r, res = test_complete_context(s) + @test res + @test "Issue52922" in c +end +let s = " @time using .Iss" + c, r, res = test_complete_context(s) + @test res + @test "Issue52922" in c +end +let s = "@time(using .Iss" + c, r, res = test_complete_context(s) + @test res + @test "Issue52922" in c +end let s = "using .Issue52922.Inn" c, r, res = test_complete_context(s) @test res From 065d4567e771af1a642f14c482dd85b3240ec0f3 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 4 Aug 2024 12:30:41 +0000 Subject: [PATCH 006/548] Materialize complex Symmetric matrices in eigen (#55348) Currently, `eigen` for a complex Symmetric matrix fails, as there's no specialized LAPACK function to handle such matrices. We may instead materialize the matrix and use a generic solver. While a user may do it by themselves, I think returning an answer is better than throwing an error. --- stdlib/LinearAlgebra/src/bunchkaufman.jl | 15 +++++++++------ stdlib/LinearAlgebra/src/symmetriceigen.jl | 1 + stdlib/LinearAlgebra/test/hessenberg.jl | 7 +++++++ stdlib/LinearAlgebra/test/symmetriceigen.jl | 6 ++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bunchkaufman.jl b/stdlib/LinearAlgebra/src/bunchkaufman.jl index 5a73c656abe33..a44f1a1c99094 100644 --- a/stdlib/LinearAlgebra/src/bunchkaufman.jl +++ b/stdlib/LinearAlgebra/src/bunchkaufman.jl @@ -127,6 +127,9 @@ function bunchkaufman!(A::StridedMatrix{<:BlasFloat}, rook::Bool = false; check: end end +bkcopy_oftype(A, S) = eigencopy_oftype(A, S) +bkcopy_oftype(A::Symmetric{<:Complex}, S) = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) + """ bunchkaufman(A, rook::Bool=false; check = true) -> S::BunchKaufman @@ -206,7 +209,7 @@ julia> S.L*S.D*S.L' - A[S.p, S.p] ``` """ bunchkaufman(A::AbstractMatrix{T}, rook::Bool=false; check::Bool = true) where {T} = - bunchkaufman!(eigencopy_oftype(A, typeof(sqrt(oneunit(T)))), rook; check = check) + bunchkaufman!(bkcopy_oftype(A, typeof(sqrt(oneunit(T)))), rook; check = check) BunchKaufman{T}(B::BunchKaufman) where {T} = BunchKaufman(convert(Matrix{T}, B.LD), B.ipiv, B.uplo, B.symmetric, B.rook, B.info) @@ -1540,7 +1543,7 @@ function bunchkaufman(A::AbstractMatrix{TS}, rook::Bool = false; check::Bool = true ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - return bunchkaufman!(eigencopy_oftype(A, TS), rook; check) + return bunchkaufman!(bkcopy_oftype(A, TS), rook; check) end function bunchkaufman(A::AbstractMatrix{TS}, @@ -1562,15 +1565,15 @@ function bunchkaufman(A::AbstractMatrix{TS}, # We promote input to BigInt to avoid overflow problems if TA == Nothing if TS <: Integer - M = Rational{BigInt}.(eigencopy_oftype(A, TS)) + M = Rational{BigInt}.(bkcopy_oftype(A, TS)) else - M = Complex{Rational{BigInt}}.(eigencopy_oftype(A, TS)) + M = Complex{Rational{BigInt}}.(bkcopy_oftype(A, TS)) end else if TS <: Integer - M = TA(Rational{BigInt}.(eigencopy_oftype(A, TS)), Symbol(A.uplo)) + M = TA(Rational{BigInt}.(bkcopy_oftype(A, TS)), Symbol(A.uplo)) else - M = TA(Complex{Rational{BigInt}}.(eigencopy_oftype(A, TS)), + M = TA(Complex{Rational{BigInt}}.(bkcopy_oftype(A, TS)), Symbol(A.uplo)) end end diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index 666b9a9bc81df..fee524a702187 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -4,6 +4,7 @@ # Call `copytrito!` instead of `copy_similar` to only copy the matching triangular half eigencopy_oftype(A::Hermitian, S) = Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) eigencopy_oftype(A::Symmetric, S) = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) +eigencopy_oftype(A::Symmetric{<:Complex}, S) = copyto!(similar(parent(A), S), A) default_eigen_alg(A) = DivideAndConquer() diff --git a/stdlib/LinearAlgebra/test/hessenberg.jl b/stdlib/LinearAlgebra/test/hessenberg.jl index 767f40aa1e53f..54dbb70aa2065 100644 --- a/stdlib/LinearAlgebra/test/hessenberg.jl +++ b/stdlib/LinearAlgebra/test/hessenberg.jl @@ -272,4 +272,11 @@ end @test S[1,2] == S[Int8(1),UInt16(2)] == S[big(1), Int16(2)] end +@testset "complex Symmetric" begin + D = diagm(0=>ComplexF64[1,2]) + S = Symmetric(D) + H = hessenberg(S) + @test H.H == D +end + end # module TestHessenberg diff --git a/stdlib/LinearAlgebra/test/symmetriceigen.jl b/stdlib/LinearAlgebra/test/symmetriceigen.jl index cacdb72c63071..d55d1deb6bf33 100644 --- a/stdlib/LinearAlgebra/test/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/test/symmetriceigen.jl @@ -173,4 +173,10 @@ end @test D.vectors ≈ D32.vectors end +@testset "complex Symmetric" begin + S = Symmetric(rand(ComplexF64,2,2)) + λ, v = eigen(S) + @test S * v ≈ v * Diagonal(λ) +end + end # module TestSymmetricEigen From f4d1381b7c1242a17815bba6febeeb9a3a40c093 Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Sun, 4 Aug 2024 23:14:40 +0200 Subject: [PATCH 007/548] Document `Threads.threadid(::Task)` (#55369) This is quite handy to figure out which thread a task is running on, and I couldn't find another way to do it from outside the task. --- base/threadingconstructs.jl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 479bcd80c586d..a21d708b4a077 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -4,10 +4,10 @@ export threadid, nthreads, @threads, @spawn, threadpool, nthreadpools """ - Threads.threadid() -> Int + Threads.threadid([t::Task]) -> Int -Get the ID number of the current thread of execution. The master thread has -ID `1`. +Get the ID number of the current thread of execution, or the thread of task +`t`. The master thread has ID `1`. # Examples ```julia-repl @@ -21,12 +21,15 @@ julia> Threads.@threads for i in 1:4 2 5 4 + +julia> Threads.threadid(Threads.@spawn "foo") +2 ``` !!! note The thread that a task runs on may change if the task yields, which is known as [`Task Migration`](@ref man-task-migration). - For this reason in most cases it is not safe to use `threadid()` to index into, say, a vector of buffer or stateful objects. - + For this reason in most cases it is not safe to use `threadid([task])` to index into, say, a vector of buffers or stateful + objects. """ threadid() = Int(ccall(:jl_threadid, Int16, ())+1) From a163483e6714631e9f6fecf141bd2dca999fbd6a Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 5 Aug 2024 06:06:39 +0000 Subject: [PATCH 008/548] Fix tr for block SymTridiagonal (#55371) This ensures that `tr` for a block `SymTridiagonal` symmetrizes the diagonal elements. --- stdlib/LinearAlgebra/src/tridiag.jl | 2 +- stdlib/LinearAlgebra/test/tridiag.jl | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 3198e45ad3eb8..2ff688f4b4ed1 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -181,7 +181,7 @@ Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(ad ishermitian(S::SymTridiagonal) = isreal(S.dv) && isreal(_evview(S)) issymmetric(S::SymTridiagonal) = true -tr(S::SymTridiagonal) = sum(S.dv) +tr(S::SymTridiagonal) = sum(symmetric, S.dv) @noinline function throw_diag_outofboundserror(n, sz) sz1, sz2 = sz diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index fae708c4c8db4..5dc1d01e850d8 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -471,7 +471,7 @@ end end @testset "SymTridiagonal/Tridiagonal block matrix" begin - M = [1 2; 2 4] + M = [1 2; 3 4] n = 5 A = SymTridiagonal(fill(M, n), fill(M, n-1)) @test @inferred A[1,1] == Symmetric(M) @@ -485,6 +485,9 @@ end @test_throws ArgumentError diag(A, n+1) @test_throws ArgumentError diag(A, -n-1) + @test tr(A) == sum(diag(A)) + @test issymmetric(tr(A)) + A = Tridiagonal(fill(M, n-1), fill(M, n), fill(M, n-1)) @test @inferred A[1,1] == M @test @inferred A[1,2] == M From 996351f5f6651d1508aef3c35c7d37eb22a0fb1e Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 5 Aug 2024 06:07:12 +0000 Subject: [PATCH 009/548] Round-trippable show for `Bidiagonal` (#55347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes how a `Bidiagonal` is displayed using the 2-arg `show` to a form that may be parsed. After this, ```julia julia> B = Bidiagonal([1,2,3], [1,2], :U) 3×3 Bidiagonal{Int64, Vector{Int64}}: 1 1 ⋅ ⋅ 2 2 ⋅ ⋅ 3 julia> show(B) Bidiagonal([1, 2, 3], [1, 2], :U) ``` The displayed form is a valid constructor now. --- stdlib/LinearAlgebra/src/bidiag.jl | 13 +++++++------ stdlib/LinearAlgebra/test/bidiag.jl | 6 ++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 5f9a64904bd38..e2572c0f67af9 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -264,12 +264,13 @@ end #################### function show(io::IO, M::Bidiagonal) - # TODO: make this readable and one-line - summary(io, M) - print(io, ":\n diag:") - print_matrix(io, (M.dv)') - print(io, M.uplo == 'U' ? "\n super:" : "\n sub:") - print_matrix(io, (M.ev)') + print(io, "Bidiagonal(") + show(io, M.dv) + print(io, ", ") + show(io, M.ev) + print(io, ", ") + show(io, sym_uplo(M.uplo)) + print(io, ")") end size(M::Bidiagonal) = (n = length(M.dv); (n, n)) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 37503efe42518..3c99d0d3b6f5e 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -143,11 +143,9 @@ Random.seed!(1) @testset "show" begin BD = Bidiagonal(dv, ev, :U) - dstring = sprint(Base.print_matrix,BD.dv') - estring = sprint(Base.print_matrix,BD.ev') - @test sprint(show,BD) == "$(summary(BD)):\n diag:$dstring\n super:$estring" + @test sprint(show,BD) == "Bidiagonal($(repr(dv)), $(repr(ev)), :U)" BD = Bidiagonal(dv,ev,:L) - @test sprint(show,BD) == "$(summary(BD)):\n diag:$dstring\n sub:$estring" + @test sprint(show,BD) == "Bidiagonal($(repr(dv)), $(repr(ev)), :L)" end @testset for uplo in (:U, :L) From 40ecf690197a7a6221cfcffd9c74799258fb4495 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 5 Aug 2024 12:54:45 +0000 Subject: [PATCH 010/548] LinearAlgbera: pass sizes to muldiag_size_check (#55378) This will avoid having to specialize `_muldiag_size_check` on the matrix types, as we only need the sizes (and potentially `ndims`) for the error checks. --- stdlib/LinearAlgebra/src/bidiag.jl | 8 ++--- stdlib/LinearAlgebra/src/diagonal.jl | 53 +++++++++++++--------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index e2572c0f67af9..0caaec02b5056 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -461,7 +461,7 @@ const BiTri = Union{Bidiagonal,Tridiagonal} # B .= A * B function lmul!(A::Bidiagonal, B::AbstractVecOrMat) - _muldiag_size_check(A, B) + _muldiag_size_check(size(A), size(B)) (; dv, ev) = A if A.uplo == 'U' for k in axes(B,2) @@ -482,7 +482,7 @@ function lmul!(A::Bidiagonal, B::AbstractVecOrMat) end # B .= D * B function lmul!(D::Diagonal, B::Bidiagonal) - _muldiag_size_check(D, B) + _muldiag_size_check(size(D), size(B)) (; dv, ev) = B isL = B.uplo == 'L' dv[1] = D.diag[1] * dv[1] @@ -494,7 +494,7 @@ function lmul!(D::Diagonal, B::Bidiagonal) end # B .= B * A function rmul!(B::AbstractMatrix, A::Bidiagonal) - _muldiag_size_check(A, B) + _muldiag_size_check(size(A), size(B)) (; dv, ev) = A if A.uplo == 'U' for k in reverse(axes(dv,1)[2:end]) @@ -519,7 +519,7 @@ function rmul!(B::AbstractMatrix, A::Bidiagonal) end # B .= B * D function rmul!(B::Bidiagonal, D::Diagonal) - _muldiag_size_check(B, D) + _muldiag_size_check(size(B), size(D)) (; dv, ev) = B isU = B.uplo == 'U' dv[1] *= D.diag[1] diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 8f643a0054719..9cd13b4d26f00 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -293,42 +293,39 @@ Base.literal_pow(::typeof(^), D::Diagonal, valp::Val) = Diagonal(Base.literal_pow.(^, D.diag, valp)) # for speed Base.literal_pow(::typeof(^), D::Diagonal, ::Val{-1}) = inv(D) # for disambiguation -function _muldiag_size_check(A, B) - nA = size(A, 2) - mB = size(B, 1) - @noinline throw_dimerr(::AbstractMatrix, nA, mB) = throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match first dimension of B, $mB")) - @noinline throw_dimerr(::AbstractVector, nA, mB) = throw(DimensionMismatch(lazy"second dimension of D, $nA, does not match length of V, $mB")) - nA == mB || throw_dimerr(B, nA, mB) +function _muldiag_size_check(szA::NTuple{2,Integer}, szB::Tuple{Integer,Vararg{Integer}}) + nA = szA[2] + mB = szB[1] + @noinline throw_dimerr(szB::NTuple{2}, nA, mB) = throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match first dimension of B, $mB")) + @noinline throw_dimerr(szB::NTuple{1}, nA, mB) = throw(DimensionMismatch(lazy"second dimension of D, $nA, does not match length of V, $mB")) + nA == mB || throw_dimerr(szB, nA, mB) return nothing end # the output matrix should have the same size as the non-diagonal input matrix or vector @noinline throw_dimerr(szC, szA) = throw(DimensionMismatch(lazy"output matrix has size: $szC, but should have size $szA")) -_size_check_out(C, ::Diagonal, A) = _size_check_out(C, A) -_size_check_out(C, A, ::Diagonal) = _size_check_out(C, A) -_size_check_out(C, A::Diagonal, ::Diagonal) = _size_check_out(C, A) -function _size_check_out(C, A) - szA = size(A) - szC = size(C) - szA == szC || throw_dimerr(szC, szA) - return nothing +function _size_check_out(szC::NTuple{2}, szA::NTuple{2}, szB::NTuple{2}) + (szC[1] == szA[1] && szC[2] == szB[2]) || throw_dimerr(szC, (szA[1], szB[2])) +end +function _size_check_out(szC::NTuple{1}, szA::NTuple{2}, szB::NTuple{1}) + szC[1] == szA[1] || throw_dimerr(szC, (szA[1],)) end -function _muldiag_size_check(C, A, B) - _muldiag_size_check(A, B) - _size_check_out(C, A, B) +function _muldiag_size_check(szC::Tuple{Vararg{Integer}}, szA::Tuple{Vararg{Integer}}, szB::Tuple{Vararg{Integer}}) + _muldiag_size_check(szA, szB) + _size_check_out(szC, szA, szB) end function (*)(Da::Diagonal, Db::Diagonal) - _muldiag_size_check(Da, Db) + _muldiag_size_check(size(Da), size(Db)) return Diagonal(Da.diag .* Db.diag) end function (*)(D::Diagonal, V::AbstractVector) - _muldiag_size_check(D, V) + _muldiag_size_check(size(D), size(V)) return D.diag .* V end function rmul!(A::AbstractMatrix, D::Diagonal) - _muldiag_size_check(A, D) + _muldiag_size_check(size(A), size(D)) for I in CartesianIndices(A) row, col = Tuple(I) @inbounds A[row, col] *= D.diag[col] @@ -337,7 +334,7 @@ function rmul!(A::AbstractMatrix, D::Diagonal) end # T .= T * D function rmul!(T::Tridiagonal, D::Diagonal) - _muldiag_size_check(T, D) + _muldiag_size_check(size(T), size(D)) (; dl, d, du) = T d[1] *= D.diag[1] for i in axes(dl,1) @@ -349,7 +346,7 @@ function rmul!(T::Tridiagonal, D::Diagonal) end function lmul!(D::Diagonal, B::AbstractVecOrMat) - _muldiag_size_check(D, B) + _muldiag_size_check(size(D), size(B)) for I in CartesianIndices(B) row = I[1] @inbounds B[I] = D.diag[row] * B[I] @@ -360,7 +357,7 @@ end # in-place multiplication with a diagonal # T .= D * T function lmul!(D::Diagonal, T::Tridiagonal) - _muldiag_size_check(D, T) + _muldiag_size_check(size(D), size(T)) (; dl, d, du) = T d[1] = D.diag[1] * d[1] for i in axes(dl,1) @@ -452,7 +449,7 @@ function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) end function _mul_diag!(out, A, B, _add) - _muldiag_size_check(out, A, B) + _muldiag_size_check(size(out), size(A), size(B)) __muldiag!(out, A, B, _add) return out end @@ -469,14 +466,14 @@ _mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, _add) = _mul_diag!(C, Da, Db, _add) function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal) - _muldiag_size_check(Da, A) - _muldiag_size_check(A, Db) + _muldiag_size_check(size(Da), size(A)) + _muldiag_size_check(size(A), size(Db)) return broadcast(*, Da.diag, A, permutedims(Db.diag)) end function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) - _muldiag_size_check(Da, Db) - _muldiag_size_check(Db, Dc) + _muldiag_size_check(size(Da), size(Db)) + _muldiag_size_check(size(Db), size(Dc)) return Diagonal(Da.diag .* Db.diag .* Dc.diag) end From 4200203453c9c47c2df88f0c3b90dec46aeb2620 Mon Sep 17 00:00:00 2001 From: Satvik Date: Mon, 5 Aug 2024 12:44:48 -0700 Subject: [PATCH 011/548] Replace `@async` mentions in manual with `Threads.@spawn` (#55315) --- doc/src/base/parallel.md | 2 +- doc/src/devdocs/probes.md | 6 +++--- doc/src/manual/asynchronous-programming.md | 10 +++++----- doc/src/manual/distributed-computing.md | 8 ++++---- doc/src/manual/faq.md | 6 +++--- doc/src/manual/methods.md | 4 ++-- doc/src/manual/networking-and-streams.md | 14 +++++++------- doc/src/manual/performance-tips.md | 2 +- doc/src/manual/running-external-programs.md | 4 ++-- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/src/base/parallel.md b/doc/src/base/parallel.md index 58ec078a8e0cf..9f24db176b538 100644 --- a/doc/src/base/parallel.md +++ b/doc/src/base/parallel.md @@ -138,7 +138,7 @@ end ev = OneWayEvent() @sync begin - @async begin + Threads.@spawn begin wait(ev) println("done") end diff --git a/doc/src/devdocs/probes.md b/doc/src/devdocs/probes.md index 5a1af0d897bc6..a0e072c0b1ae3 100644 --- a/doc/src/devdocs/probes.md +++ b/doc/src/devdocs/probes.md @@ -206,7 +206,7 @@ Now we can start `bpftrace` and have it monitor `rt__new__task` for *only* this And if we spawn a single task: -`@async 1+1` +`Threads.@spawn 1+1` we see this task being created: @@ -215,8 +215,8 @@ we see this task being created: However, if we spawn a bunch of tasks from that newly-spawned task: ```julia -@async for i in 1:10 - @async 1+1 +Threads.@spawn for i in 1:10 + Threads.@spawn 1+1 end ``` diff --git a/doc/src/manual/asynchronous-programming.md b/doc/src/manual/asynchronous-programming.md index 15db6eda5f807..d1d095c48b2ff 100644 --- a/doc/src/manual/asynchronous-programming.md +++ b/doc/src/manual/asynchronous-programming.md @@ -64,8 +64,8 @@ the next input prompt appears. That is because the REPL is waiting for `t` to finish before proceeding. It is common to want to create a task and schedule it right away, so the -macro [`@async`](@ref) is provided for that purpose --- `@async x` is -equivalent to `schedule(@task x)`. +macro [`Threads.@spawn`](@ref) is provided for that purpose --- `Threads.@spawn x` is +equivalent to `task = @task x; task.sticky = false; schedule(task)`. ## Communicating with Channels @@ -186,7 +186,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and a read end : # we can schedule `n` instances of `foo` to be active concurrently. for _ in 1:n - errormonitor(@async foo()) + errormonitor(Threads.@spawn foo()) end ``` * Channels are created via the `Channel{T}(sz)` constructor. The channel will only hold objects @@ -264,10 +264,10 @@ julia> function make_jobs(n) julia> n = 12; -julia> errormonitor(@async make_jobs(n)); # feed the jobs channel with "n" jobs +julia> errormonitor(Threads.@spawn make_jobs(n)); # feed the jobs channel with "n" jobs julia> for i in 1:4 # start 4 tasks to process requests in parallel - errormonitor(@async do_work()) + errormonitor(Threads.@spawn do_work()) end julia> @elapsed while n > 0 # print out results diff --git a/doc/src/manual/distributed-computing.md b/doc/src/manual/distributed-computing.md index d325239fc9e2d..f60dfb7004ada 100644 --- a/doc/src/manual/distributed-computing.md +++ b/doc/src/manual/distributed-computing.md @@ -123,7 +123,7 @@ An important thing to remember is that, once fetched, a [`Future`](@ref Distribu locally. Further [`fetch`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref Distributed.Future)s have fetched, the remote stored value is deleted. -[`@async`](@ref) is similar to [`@spawnat`](@ref), but only runs tasks on the local process. We +[`Threads.@spawn`](@ref) is similar to [`@spawnat`](@ref), but only runs tasks on the local process. We use it to create a "feeder" task for each process. Each task picks the next index that needs to be computed, then waits for its process to finish, then repeats until we run out of indices. Note that the feeder tasks do not begin to execute until the main task reaches the end of the [`@sync`](@ref) @@ -657,7 +657,7 @@ julia> function make_jobs(n) julia> n = 12; -julia> errormonitor(@async make_jobs(n)); # feed the jobs channel with "n" jobs +julia> errormonitor(Threads.@spawn make_jobs(n)); # feed the jobs channel with "n" jobs julia> for p in workers() # start tasks on the workers to process requests in parallel remote_do(do_work, p, jobs, results) @@ -896,7 +896,7 @@ conflicts. For example: ```julia @sync begin for p in procs(S) - @async begin + Threads.@spawn begin remotecall_wait(fill!, p, S, p) end end @@ -978,7 +978,7 @@ and one that delegates in chunks: julia> function advection_shared!(q, u) @sync begin for p in procs(q) - @async remotecall_wait(advection_shared_chunk!, p, q, u) + Threads.@spawn remotecall_wait(advection_shared_chunk!, p, q, u) end end q diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 8984e1d15ddd3..2673ca7532acf 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -943,7 +943,7 @@ Consider the printed output from the following: ```jldoctest julia> @sync for i in 1:3 - @async write(stdout, string(i), " Foo ", " Bar ") + Threads.@spawn write(stdout, string(i), " Foo ", " Bar ") end 123 Foo Foo Foo Bar Bar Bar ``` @@ -956,7 +956,7 @@ in the above example results in: ```jldoctest julia> @sync for i in 1:3 - @async println(stdout, string(i), " Foo ", " Bar ") + Threads.@spawn println(stdout, string(i), " Foo ", " Bar ") end 1 Foo Bar 2 Foo Bar @@ -969,7 +969,7 @@ You can lock your writes with a `ReentrantLock` like this: julia> l = ReentrantLock(); julia> @sync for i in 1:3 - @async begin + Threads.@spawn begin lock(l) try write(stdout, string(i), " Foo ", " Bar ") diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index d45644bf55842..6be44dcf4fa13 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -614,7 +614,7 @@ Start some other operations that use `f(x)`: julia> g(x) = f(x) g (generic function with 1 method) -julia> t = @async f(wait()); yield(); +julia> t = Threads.@spawn f(wait()); yield(); ``` Now we add some new methods to `f(x)`: @@ -639,7 +639,7 @@ julia> g(1) julia> fetch(schedule(t, 1)) "original definition" -julia> t = @async f(wait()); yield(); +julia> t = Threads.@spawn f(wait()); yield(); julia> fetch(schedule(t, 1)) "definition for Int" diff --git a/doc/src/manual/networking-and-streams.md b/doc/src/manual/networking-and-streams.md index 45bf60a7944d2..35ba7fdf16601 100644 --- a/doc/src/manual/networking-and-streams.md +++ b/doc/src/manual/networking-and-streams.md @@ -233,7 +233,7 @@ Let's first create a simple server: ```julia-repl julia> using Sockets -julia> errormonitor(@async begin +julia> errormonitor(Threads.@spawn begin server = listen(2000) while true sock = accept(server) @@ -305,11 +305,11 @@ printed the message and waited for the next client. Reading and writing works in To see this, consider the following simple echo server: ```julia-repl -julia> errormonitor(@async begin +julia> errormonitor(Threads.@spawn begin server = listen(2001) while true sock = accept(server) - @async while isopen(sock) + Threads.@spawn while isopen(sock) write(sock, readline(sock, keep=true)) end end @@ -319,7 +319,7 @@ Task (runnable) @0x00007fd31dc12e60 julia> clientside = connect(2001) TCPSocket(RawFD(28) open, 0 bytes waiting) -julia> errormonitor(@async while isopen(clientside) +julia> errormonitor(Threads.@spawn while isopen(clientside) write(stdout, readline(clientside, keep=true)) end) Task (runnable) @0x00007fd31dc11870 @@ -357,10 +357,10 @@ ip"74.125.226.225" All I/O operations exposed by [`Base.read`](@ref) and [`Base.write`](@ref) can be performed asynchronously through the use of [coroutines](@ref man-tasks). You can create a new coroutine to -read from or write to a stream using the [`@async`](@ref) macro: +read from or write to a stream using the [`Threads.@spawn`](@ref) macro: ```julia-repl -julia> task = @async open("foo.txt", "w") do io +julia> task = Threads.@spawn open("foo.txt", "w") do io write(io, "Hello, World!") end; @@ -379,7 +379,7 @@ your program to block until all of the coroutines it wraps around have exited: julia> using Sockets julia> @sync for hostname in ("google.com", "github.com", "julialang.org") - @async begin + Threads.@spawn begin conn = connect(hostname, 80) write(conn, "GET / HTTP/1.1\r\nHost:$(hostname)\r\n\r\n") readline(conn, keep=true) diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 5c10652eb99cb..38e27476f0af8 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1723,7 +1723,7 @@ using Distributed responses = Vector{Any}(undef, nworkers()) @sync begin for (idx, pid) in enumerate(workers()) - @async responses[idx] = remotecall_fetch(foo, pid, args...) + Threads.@spawn responses[idx] = remotecall_fetch(foo, pid, args...) end end ``` diff --git a/doc/src/manual/running-external-programs.md b/doc/src/manual/running-external-programs.md index 4a9803337990b..1f9f3129ca16b 100644 --- a/doc/src/manual/running-external-programs.md +++ b/doc/src/manual/running-external-programs.md @@ -332,8 +332,8 @@ will attempt to store the data in the kernel's buffers while waiting for a reade Another common solution is to separate the reader and writer of the pipeline into separate [`Task`](@ref)s: ```julia -writer = @async write(process, "data") -reader = @async do_compute(read(process, String)) +writer = Threads.@spawn write(process, "data") +reader = Threads.@spawn do_compute(read(process, String)) wait(writer) fetch(reader) ``` From e38e4db8f80f91ba0968736936ce4fcb1c835240 Mon Sep 17 00:00:00 2001 From: GHTaarn <62629455+GHTaarn@users.noreply.github.com> Date: Mon, 5 Aug 2024 21:52:35 +0200 Subject: [PATCH 012/548] Make REPL.TerminalMenus and some if its symbols public (#55307) --- stdlib/REPL/src/REPL.jl | 2 ++ stdlib/REPL/src/TerminalMenus/TerminalMenus.jl | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 558f6ed9cab6f..67f5860082c8a 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -110,6 +110,8 @@ export LineEditREPL, StreamREPL +public TerminalMenus + import Base: AbstractDisplay, display, diff --git a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl index ffbe32575fea1..f970cd9a289c2 100644 --- a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl +++ b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl @@ -1,5 +1,12 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + REPL.TerminalMenus + +A module that contains code for displaying text mode interactive menus. +Key exported symbols include [`REPL.TerminalMenus.RadioMenu`](@ref) and +[`REPL.TerminalMenus.MultiSelectMenu`](@ref). +""" module TerminalMenus using ..REPL: REPL @@ -23,6 +30,9 @@ export Pager, request +public Config, config, MultiSelectConfig +public pick, cancel, writeline, options, numoptions, selected, header, keypress + # TODO: remove in Julia 2.0 # While not exported, AbstractMenu documented these as an extension interface @deprecate printMenu printmenu From 6ad6a8f1e8b960d8ecb114a03e8e0eebf712abfb Mon Sep 17 00:00:00 2001 From: Octogonapus Date: Mon, 5 Aug 2024 16:01:02 -0400 Subject: [PATCH 013/548] Delete broken and unhelpful const mutation example from docs (#55182) --- doc/src/manual/variables-and-scoping.md | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index 85a83120dc517..de97ff296e37e 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -755,28 +755,7 @@ julia> const z = 100 julia> z = 100 100 ``` -The last rule applies to immutable objects even if the variable binding would change, e.g.: -```julia-repl -julia> const s1 = "1" -"1" - -julia> s2 = "1" -"1" - -julia> pointer.([s1, s2], 1) -2-element Array{Ptr{UInt8},1}: - Ptr{UInt8} @0x00000000132c9638 - Ptr{UInt8} @0x0000000013dd3d18 - -julia> s1 = s2 -"1" - -julia> pointer.([s1, s2], 1) -2-element Array{Ptr{UInt8},1}: - Ptr{UInt8} @0x0000000013dd3d18 - Ptr{UInt8} @0x0000000013dd3d18 -``` -However, for mutable objects the warning is printed as expected: +* if an assignment would change the mutable object to which the variable points (regardless of whether those two objects are deeply equal), a warning is printed: ```jldoctest julia> const a = [1] 1-element Vector{Int64}: From d1b1a5dd254e127edc994076cce61ae1cca16979 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 5 Aug 2024 16:46:54 -0400 Subject: [PATCH 014/548] inference: fix missing LimitedAccuracy markers (#55362) If the LimitedAccuracy was supposed to resolve against the top-most frame (or hypothetically a non-InferenceState frame), it would not have a parentframe, preventing it from reaching the subsequent poison_callstack line that is required for reliable inference (avoiding caching bad results). This should restore the original intent of this code (pre #48913) --- base/compiler/abstractinterpretation.jl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 6a7d90a7bcb21..e3e3502d66173 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -698,13 +698,23 @@ function abstract_call_method(interp::AbstractInterpreter, end add_remark!(interp, sv, washardlimit ? RECURSION_MSG_HARDLIMIT : RECURSION_MSG) # TODO (#48913) implement a proper recursion handling for irinterp: - # This works just because currently the `:terminate` condition guarantees that - # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # This works just because currently the `:terminate` condition usually means this is unreachable here + # for irinterp because there are not unresolved cycles, but it's not a good solution. # We should revisit this once we have a better story for handling cycles in irinterp. - if isa(topmost, InferenceState) + if isa(sv, InferenceState) + # since the hardlimit is against the edge to the parent frame, + # we should try to poison the whole edge, not just the topmost frame parentframe = frame_parent(topmost) - if isa(sv, InferenceState) && isa(parentframe, InferenceState) - poison_callstack!(sv, parentframe === nothing ? topmost : parentframe) + while !isa(parentframe, InferenceState) + # attempt to find a parent frame that can handle this LimitedAccuracy result correctly + # so we don't try to cache this incomplete intermediate result + parentframe === nothing && break + parentframe = frame_parent(parentframe) + end + if isa(parentframe, InferenceState) + poison_callstack!(sv, parentframe) + elseif isa(topmost, InferenceState) + poison_callstack!(sv, topmost) end end # n.b. this heuristic depends on the non-local state, so we must record the limit later From 1b32fa6825739ab68542f6ea476b09931f77ec40 Mon Sep 17 00:00:00 2001 From: Michael Abbott <32575566+mcabbott@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:39:30 -0400 Subject: [PATCH 015/548] Compact printing for `Adjoint` vectors, and `Diagonal` (#40722) This changes the compact printing to preserve more information -- an adjoint vector is not quite a matrix, and Diagonal wastes a lot of space: ```julia julia> (Diagonal(1:4), [5,6,7]', transpose(8:10)) # before ([1 0 0 0; 0 2 0 0; 0 0 3 0; 0 0 0 4], [5 6 7], [8 9 10]) julia> (Diagonal(1:4), [5,6,7]', transpose(8:10)) # after (Diagonal(1:4), adjoint([5, 6, 7]), transpose(8:10)) ``` Would have been better to do at the same time as 1.6's other printing changes, I guess. --------- Co-authored-by: Jishnu Bhattacharya --- base/abstractarray.jl | 2 +- stdlib/LinearAlgebra/src/adjtrans.jl | 10 ++++++++++ stdlib/LinearAlgebra/src/diagonal.jl | 5 +++++ stdlib/LinearAlgebra/test/adjtrans.jl | 5 +++++ stdlib/LinearAlgebra/test/diagonal.jl | 5 +++++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 2fbae2c423196..40e36ce15f6ed 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1921,7 +1921,7 @@ julia> vcat(range(1, 2, length=3)) # collects lazy ranges 2.0 julia> two = ([10, 20, 30]', Float64[4 5 6; 7 8 9]) # row vector and a matrix -([10 20 30], [4.0 5.0 6.0; 7.0 8.0 9.0]) +(adjoint([10, 20, 30]), [4.0 5.0 6.0; 7.0 8.0 9.0]) julia> vcat(two...) 3×3 Matrix{Float64}: diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index daee587b82835..b722e49bb2c3d 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -302,6 +302,16 @@ function Base.showarg(io::IO, v::Transpose, toplevel) toplevel && print(io, " with eltype ", eltype(v)) return nothing end +function Base.show(io::IO, v::Adjoint{<:Real, <:AbstractVector}) + print(io, "adjoint(") + show(io, parent(v)) + print(io, ")") +end +function Base.show(io::IO, v::Transpose{<:Number, <:AbstractVector}) + print(io, "transpose(") + show(io, parent(v)) + print(io, ")") +end # some aliases for internal convenience use const AdjOrTrans{T,S} = Union{Adjoint{T,S},Transpose{T,S}} where {T,S} diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 9cd13b4d26f00..89202e66597f8 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -213,6 +213,11 @@ end function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString) i==j ? s : Base.replace_with_centered_mark(s) end +function Base.show(io::IO, A::Diagonal) + print(io, "Diagonal(") + show(io, A.diag) + print(io, ")") +end parent(D::Diagonal) = D.diag diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 1a66c7430723e..6cf2ff9ada09c 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -532,6 +532,11 @@ end @test String(take!(io)) == "transpose(::Matrix{Float64})" end +@testset "show" begin + @test repr(adjoint([1,2,3])) == "adjoint([1, 2, 3])" + @test repr(transpose([1f0,2f0])) == "transpose(Float32[1.0, 2.0])" +end + @testset "strided transposes" begin for t in (Adjoint, Transpose) @test strides(t(rand(3))) == (3, 1) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index e1fc9afa5ad2e..29f3a38473d4a 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1231,6 +1231,11 @@ Base.size(::SMatrix1) = (1, 1) @test C isa Matrix{SMatrix1{String}} end +@testset "show" begin + @test repr(Diagonal([1,2])) == "Diagonal([1, 2])" # 2-arg show + @test contains(repr(MIME"text/plain"(), Diagonal([1,2])), "⋅ 2") # 3-arg show +end + @testset "copyto! with UniformScaling" begin @testset "Fill" begin for len in (4, InfiniteArrays.Infinity()) From b0a8024a4598f60f72a0799b3b60a29383188523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Tue, 6 Aug 2024 10:46:41 +0200 Subject: [PATCH 016/548] [build] Some improvements to the LLVM build system (#55354) ### Elaboration of the issue After #55180 we implicitly require an LLVM built with Zlib support, but compiling Julia with `make USE_BINARYBUILDER_LLVM=0` builds an LLVM without Zlib support, despite the fact we attempt to request it at https://github.com/JuliaLang/julia/blob/996351f5f6651d1508aef3c35c7d37eb22a0fb1e/deps/llvm.mk#L97 This was first identified in #55337. ### Explanation of how configuration of LLVM failed `ZLIB_LIBRARY` must be the path to the zlib library, but we currently set it to the libdir where the library is installed (introduced in #42622): https://github.com/JuliaLang/julia/blob/996351f5f6651d1508aef3c35c7d37eb22a0fb1e/deps/llvm.mk#L97 which is wrong. However, CMake is actually able to find Zlib correctly, but then the check at https://github.com/llvm/llvm-project/blob/46425b8d0fac3c529aa4a716d19abd7032e452f3/llvm/cmake/config-ix.cmake#L139-L141 uses the value of `ZLIB_LIBRARY` to list the Zlib to link for the test, but being `ZLIB_LIBRARY` a directory, CMake doesn't see any valid Zlib and thus tries to run the test without linking any Zlib, and the test silently fails (they're silent only when `LLVM_ENABLE_ZLIB=ON`), resulting in no usable Zlib available, even if found. ### Proposed solution `ZLIB_ROOT` is the only [hint recommended by the CMake module `FindZLIB`](https://cmake.org/cmake/help/latest/module/FindZLIB.html#hints). This PR replaces a broken `ZLIB_LIBRARY` with an appropriate `ZLIB_ROOT`. Also, we set `LLVM_ENABLE_ZLIB=FORCE_ON` which is the only way to make CMake fail loudly if no usable Zlib is available, and avoid going on with a non-usable build. ### Other comments I confirm this fixes #55337 for me, it should likely address https://github.com/JuliaCI/julia-buildkite/issues/373 as well. Also, options `COMPILER_RT_ENABLE_IOS`, `COMPILER_RT_ENABLE_WATCHOS`, `COMPILER_RT_ENABLE_TVOS`, and `HAVE_HISTEDIT_H` don't exist anymore, and they are removed. --- deps/BOLT.mk | 2 +- deps/llvm.mk | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/deps/BOLT.mk b/deps/BOLT.mk index 70c5d03c762ec..34391ab10f716 100644 --- a/deps/BOLT.mk +++ b/deps/BOLT.mk @@ -30,7 +30,7 @@ LLVM_LDFLAGS += $(LDFLAGS) LLVM_LDFLAGS += $(LLVM_LDFLAGS) LLVM_CMAKE += -DLLVM_TARGETS_TO_BUILD:STRING=host -DCMAKE_BUILD_TYPE=Release LLVM_CMAKE += -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_HOST_TRIPLE="$(or $(XC_HOST),$(BUILD_MACHINE))" -LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=ON -DZLIB_LIBRARY="$(build_prefix)/lib" +LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=FORCE_ON -DZLIB_ROOT="$(build_prefix)" LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_LIBEDIT=Off diff --git a/deps/llvm.mk b/deps/llvm.mk index eddf9b60e38c7..08aff443dcff8 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -94,16 +94,15 @@ LLVM_LDFLAGS += $(BOLT_LDFLAGS) LLVM_CMAKE += -DLLVM_TARGETS_TO_BUILD:STRING="$(LLVM_TARGETS)" -DCMAKE_BUILD_TYPE="$(LLVM_CMAKE_BUILDTYPE)" LLVM_CMAKE += -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="$(LLVM_EXPERIMENTAL_TARGETS)" LLVM_CMAKE += -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_HOST_TRIPLE="$(or $(XC_HOST),$(BUILD_MACHINE))" -LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=ON -DZLIB_LIBRARY="$(build_prefix)/lib" +LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=FORCE_ON -DZLIB_ROOT="$(build_prefix)" LLVM_CMAKE += -DLLVM_ENABLE_ZSTD=OFF -LLVM_CMAKE += -DCOMPILER_RT_ENABLE_IOS=OFF -DCOMPILER_RT_ENABLE_WATCHOS=OFF -DCOMPILER_RT_ENABLE_TVOS=OFF ifeq ($(USE_POLLY_ACC),1) LLVM_CMAKE += -DPOLLY_ENABLE_GPGPU_CODEGEN=ON endif LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_INCLUDE_UTILS=ON -DLLVM_INSTALL_UTILS=ON -LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_HISTEDIT_H=Off -DHAVE_LIBEDIT=Off +LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_LIBEDIT=Off ifeq ($(LLVM_ASSERTIONS), 1) LLVM_CMAKE += -DLLVM_ENABLE_ASSERTIONS:BOOL=ON endif # LLVM_ASSERTIONS From 1e1e710ddd7a7c0fec3c79b69024fa3b7cb2a14c Mon Sep 17 00:00:00 2001 From: William Moses Date: Tue, 6 Aug 2024 05:01:59 -0400 Subject: [PATCH 017/548] AllocOpt: Fix stack lowering where alloca continas boxed and unboxed data (#55306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Valentin Churavy Co-authored-by: Mosè Giordano Co-authored-by: Gabriel Baraldi --- src/llvm-alloc-helpers.cpp | 10 ++++++++- src/llvm-alloc-helpers.h | 7 ++++++ src/llvm-alloc-opt.cpp | 15 +++++++++++++ test/llvmpasses/alloc-opt-bits.ll | 37 +++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/llvmpasses/alloc-opt-bits.ll diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 953ecc1830142..9d2fba832839c 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -88,6 +88,8 @@ bool AllocUseInfo::addMemOp(Instruction *inst, unsigned opno, uint32_t offset, memop.isaggr = isa(elty) || isa(elty) || isa(elty); memop.isobjref = hasObjref(elty); auto &field = getField(offset, size, elty); + field.second.hasunboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); + if (field.second.hasobjref != memop.isobjref) field.second.multiloc = true; // can't split this field, since it contains a mix of references and bits if (!isstore) @@ -198,6 +200,7 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r auto elty = inst->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } else if (!required.use_info.addMemOp(inst, 0, cur.offset, inst->getType(), @@ -289,6 +292,7 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r auto elty = storev->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } else if (!required.use_info.addMemOp(inst, use->getOperandNo(), cur.offset, storev->getType(), @@ -310,10 +314,14 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r } required.use_info.hasload = true; auto storev = isa(inst) ? cast(inst)->getNewValOperand() : cast(inst)->getValOperand(); + Type *elty = storev->getType(); if (cur.offset == UINT32_MAX || !required.use_info.addMemOp(inst, use->getOperandNo(), - cur.offset, storev->getType(), + cur.offset, elty, true, required.DL)) { LLVM_DEBUG(dbgs() << "Atomic inst has unknown offset\n"); + required.use_info.has_unknown_objref |= hasObjref(elty); + required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); + required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa(elty)); required.use_info.hasunknownmem = true; } required.use_info.refload = true; diff --git a/src/llvm-alloc-helpers.h b/src/llvm-alloc-helpers.h index 49c3b15332a56..20e9132d10b4c 100644 --- a/src/llvm-alloc-helpers.h +++ b/src/llvm-alloc-helpers.h @@ -46,6 +46,8 @@ namespace jl_alloc { bool hasaggr:1; bool multiloc:1; bool hasload:1; + // The alloc has a unboxed object at this offset. + bool hasunboxed:1; llvm::Type *elty; llvm::SmallVector accesses; Field(uint32_t size, llvm::Type *elty) @@ -54,6 +56,7 @@ namespace jl_alloc { hasaggr(false), multiloc(false), hasload(false), + hasunboxed(false), elty(elty) { } @@ -95,6 +98,9 @@ namespace jl_alloc { // The alloc has an aggregate Julia object reference not in an explicit field. bool has_unknown_objrefaggr:1; + // The alloc has an unboxed object at an unknown offset. + bool has_unknown_unboxed:1; + void reset() { escaped = false; @@ -110,6 +116,7 @@ namespace jl_alloc { allockind = llvm::AllocFnKind::Unknown; has_unknown_objref = false; has_unknown_objrefaggr = false; + has_unknown_unboxed = false; uses.clear(); preserves.clear(); memops.clear(); diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index e0cde7206b6b9..5984ad55d221c 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -252,10 +252,12 @@ void Optimizer::optimizeAll() removeAlloc(orig); continue; } + bool has_unboxed = use_info.has_unknown_unboxed; bool has_ref = use_info.has_unknown_objref; bool has_refaggr = use_info.has_unknown_objrefaggr; for (auto memop: use_info.memops) { auto &field = memop.second; + has_unboxed |= field.hasunboxed; if (field.hasobjref) { has_ref = true; // This can be relaxed a little based on hasload @@ -284,6 +286,19 @@ void Optimizer::optimizeAll() splitOnStack(orig); continue; } + // The move to stack code below, if has_ref is set, changes the allocation to an array of jlvalue_t's. This is fine + // if all objects are jlvalue_t's. However, if part of the allocation is an unboxed value (e.g. it is a { float, jlvaluet }), + // then moveToStack will create a [2 x jlvaluet] bitcast to { float, jlvaluet }. + // This later causes the GC rooting pass, to miss-characterize the float as a pointer to a GC value + if (has_unboxed && has_ref) { + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) + << "GC allocation could not be split since it contains both boxed and unboxed values, unable to move to stack " << ore::NV("GC Allocation", orig); + }); + if (use_info.hastypeof) + optimizeTag(orig); + continue; + } REMARK([&](){ return OptimizationRemark(DEBUG_TYPE, "Stack Move Allocation", orig) << "GC allocation moved to stack " << ore::NV("GC Allocation", orig); diff --git a/test/llvmpasses/alloc-opt-bits.ll b/test/llvmpasses/alloc-opt-bits.ll new file mode 100644 index 0000000000000..e19093f46f815 --- /dev/null +++ b/test/llvmpasses/alloc-opt-bits.ll @@ -0,0 +1,37 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s + + +@tag = external addrspace(10) global {} + +@glob = external addrspace(10) global {} + +; Test that the gc_preserve intrinsics are deleted directly. + +; CHECK-LABEL: @ptr_and_bits +; CHECK-NOT: alloca +; CHECK: call noalias ptr addrspace(10) @julia.gc_alloc_obj + +define void @ptr_and_bits(ptr %fptr, i1 %b, i1 %b2, i32 %idx) { + %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 16, ptr addrspace(10) @tag) + + %g0 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 1 + store ptr addrspace(10) @glob, ptr addrspace(10) %g0 + + %g1 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 0 + store i64 7, ptr addrspace(10) %g1 + + %res = load ptr addrspace(10), ptr addrspace(10) %g0 + %res2 = load i64, ptr addrspace(10) %g1 + ret void +} + +declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10)) + +declare ptr @julia.ptls_states() + +declare ptr @julia.get_pgcstack() From 0717a9454d7eb2275575d22f97962d9d9f1e95e2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 6 Aug 2024 06:53:10 -0400 Subject: [PATCH 018/548] Revert "better type inference for several functions taking `NTuple` args" (#55375) Reverts JuliaLang/julia#55124 as this turns out to hurt performance quite a bit Closes #55374 --- base/essentials.jl | 8 +------- base/ntuple.jl | 2 +- base/tuple.jl | 16 +++------------- test/tuple.jl | 7 ------- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 2e76235cba9a6..50017b3d7927d 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -501,13 +501,7 @@ julia> Base.tail(()) ERROR: ArgumentError: Cannot call tail on an empty tuple. ``` """ -function tail(x::Tuple{Any,Vararg}) - y = argtail(x...)::Tuple - if x isa NTuple # help the type inference - y = y::NTuple - end - y -end +tail(x::Tuple) = argtail(x...) tail(::Tuple{}) = throw(ArgumentError("Cannot call tail on an empty tuple.")) function unwrap_unionall(@nospecialize(a)) diff --git a/base/ntuple.jl b/base/ntuple.jl index 4adab38a8ee82..f81d2686b9764 100644 --- a/base/ntuple.jl +++ b/base/ntuple.jl @@ -95,5 +95,5 @@ end function reverse(t::NTuple{N}) where N ntuple(Val{N}()) do i t[end+1-i] - end::NTuple + end end diff --git a/base/tuple.jl b/base/tuple.jl index 5f74d486e1e69..fc213410cfd7c 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -340,15 +340,9 @@ ERROR: ArgumentError: Cannot call front on an empty tuple. """ function front(t::Tuple) @inline - if t === () - throw(ArgumentError("Cannot call front on an empty tuple.")) - end - r = _front(t...)::Tuple - if t isa NTuple # help the type inference - r = r::NTuple - end - r + _front(t...) end +_front() = throw(ArgumentError("Cannot call front on an empty tuple.")) _front(v) = () function _front(v, t...) @inline @@ -705,9 +699,5 @@ function circshift(x::Tuple{Any,Any,Any,Vararg{Any,N}}, shift::Integer) where {N @inline len = N + 3 j = mod1(shift, len) - y = ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple - if x isa NTuple # help the type inference - y = y::NTuple - end - y + ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple end diff --git a/test/tuple.jl b/test/tuple.jl index 59897c8adfdb2..b1894bd2bb6ce 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -845,10 +845,3 @@ end end end end - -@testset "abstract return type inference for homogeneous tuples" begin - @test NTuple == Core.Compiler.return_type(Base.tail, Tuple{NTuple}) - @test NTuple == Core.Compiler.return_type(Base.front, Tuple{NTuple}) - @test NTuple == Core.Compiler.return_type(reverse, Tuple{NTuple}) - @test NTuple == Core.Compiler.return_type(circshift, Tuple{NTuple,Int}) -end From 73777606489c1bd2a2851e73f85aed6224761f9b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 6 Aug 2024 15:14:30 +0000 Subject: [PATCH 019/548] Fuse complex conversion with function application for symmetric (#55391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids allocating an intermediate array, which reduces allocation slightly. ```julia julia> S = Symmetric(diagm(0=>-rand(100))); julia> @btime $S^0.2; 479.196 μs (25 allocations: 560.20 KiB) # nightly v"1.12.0-DEV.994" 478.213 μs (23 allocations: 558.58 KiB) # This PR ``` --- stdlib/LinearAlgebra/src/symmetric.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index f58670e255b58..d801158232673 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -821,7 +821,7 @@ function ^(A::Symmetric{<:Real}, p::Real) if all(λ -> λ ≥ 0, F.values) return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors') else - return Symmetric((F.vectors * Diagonal((complex(F.values)).^p)) * F.vectors') + return Symmetric((F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors') end end function ^(A::Symmetric{<:Complex}, p::Real) @@ -853,7 +853,7 @@ function ^(A::Hermitian{T}, p::Real) where T return Hermitian(retmat) end else - return (F.vectors * Diagonal((complex(F.values).^p))) * F.vectors' + return (F.vectors * Diagonal((complex.(F.values).^p))) * F.vectors' end end @@ -983,7 +983,7 @@ for func in (:log, :sqrt) end return Hermitian(retmat) else - retmat = (F.vectors * Diagonal(($func).(complex(F.values)))) * F.vectors' + retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' return retmat end end From f94fe63c46d6dd860a633d429003dc5be9c472e4 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 6 Aug 2024 11:14:58 -0400 Subject: [PATCH 020/548] Disable printing of message about including GPL libs in libsuitesparse.mk (#55387) https://github.com/JuliaLang/julia/pull/54240/files#r1704655126 Co-authored-by: Viral B. Shah --- deps/libsuitesparse.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deps/libsuitesparse.mk b/deps/libsuitesparse.mk index d840a7b298e17..85b2c23473a18 100644 --- a/deps/libsuitesparse.mk +++ b/deps/libsuitesparse.mk @@ -113,9 +113,7 @@ uninstall-libsuitesparse: endef remove-libsuitesparse-gpl-lib: -ifeq ($(USE_GPL_LIBS),1) - @echo This build contains [GPL-2.0+] libs: libcholmod librbio libspqr libumfpack -else +ifeq ($(USE_GPL_LIBS),0) @echo Removing GPL libs... -rm -f $(build_bindir)/libcholmod* -rm -f $(build_bindir)/libklu_cholmod* From 308cd7b61267c4e4dbbd48cc979948f3675e8fc1 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 6 Aug 2024 16:42:29 +0000 Subject: [PATCH 021/548] LAPACK: return union of tuples in gees and trsen (#55353) This simplifies the return type from a `Tuple` with a `Union` field to a `Union` of `Tuple`s. --- stdlib/LinearAlgebra/src/lapack.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 6d1d871ed85fd..97dff0031329b 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -6452,7 +6452,7 @@ for (gees, gges, gges3, elty) in resize!(work, lwork) end end - A, vs, iszero(wi) ? wr : complex.(wr, wi) + iszero(wi) ? (A, vs, wr) : (A, vs, complex.(wr, wi)) end # * .. Scalar Arguments .. @@ -6833,7 +6833,7 @@ for (trexc, trsen, tgsen, elty) in resize!(iwork, liwork) end end - T, Q, iszero(wi) ? wr : complex.(wr, wi), s[], sep[] + iszero(wi) ? (T, Q, wr, s[], sep[]) : (T, Q, complex.(wr, wi), s[], sep[]) end trsen!(select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = trsen!('N', 'V', select, T, Q) From 09e5c40173297365cf09a09aec876d0ba6493958 Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Tue, 6 Aug 2024 19:03:54 +0200 Subject: [PATCH 022/548] fix #55389: type-unstable `join` (#55395) --- base/strings/io.jl | 5 +++-- test/strings/io.jl | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index c88b7ac939c8d..acbd945c8e137 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -354,7 +354,8 @@ function join(io::IO, iterator, delim="") end function _join_preserve_annotations(iterator, args...) - if isconcretetype(eltype(iterator)) && !_isannotated(eltype(iterator)) && !any(_isannotated, args) + et = @default_eltype(iterator) + if isconcretetype(et) && !_isannotated(et) && !any(_isannotated, args) sprint(join, iterator, args...) else io = AnnotatedIOBuffer() @@ -363,7 +364,7 @@ function _join_preserve_annotations(iterator, args...) # of iterators with a non-concrete eltype), that the result is annotated # in nature, we extract an `AnnotatedString`, otherwise we just extract # a plain `String` from `io`. - if isconcretetype(eltype(iterator)) || !isempty(io.annotations) + if isconcretetype(et) || !isempty(io.annotations) read(seekstart(io), AnnotatedString{String}) else String(take!(io.io)) diff --git a/test/strings/io.jl b/test/strings/io.jl index 79e11d8cf5184..209844580b3cd 100644 --- a/test/strings/io.jl +++ b/test/strings/io.jl @@ -344,3 +344,8 @@ end @testset "`string` return types" begin @test all(T -> T <: AbstractString, Base.return_types(string)) end + +@testset "type stable `join` (#55389)" begin + itr = ("foo" for _ in 1:100) + @test Base.return_types(join, (typeof(itr),))[] == String +end From fb4e4e5bddf5896bf6c8596026ecd275e599b9af Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 6 Aug 2024 19:04:06 +0000 Subject: [PATCH 023/548] Don't read destination indices when copying structured matrices (#55322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following regression introduced in v1.11 ```julia julia> using LinearAlgebra julia> D = Diagonal(rand(4)); julia> T = Tridiagonal(Vector{BigFloat}(undef, 3), Vector{BigFloat}(undef, 4), Vector{BigFloat}(undef, 3)) 4×4 Tridiagonal{BigFloat, Vector{BigFloat}}: #undef #undef ⋅ ⋅ #undef #undef #undef ⋅ ⋅ #undef #undef #undef ⋅ ⋅ #undef #undef julia> copyto!(T, D) ERROR: UndefRefError: access to undefined reference Stacktrace: [1] getindex @ ./essentials.jl:907 [inlined] [2] _broadcast_getindex @ ./broadcast.jl:644 [inlined] [3] _getindex @ ./broadcast.jl:675 [inlined] [4] _broadcast_getindex @ ./broadcast.jl:650 [inlined] [5] getindex @ ./broadcast.jl:610 [inlined] [6] macro expansion @ ./broadcast.jl:973 [inlined] [7] macro expansion @ ./simdloop.jl:77 [inlined] [8] copyto! @ ./broadcast.jl:972 [inlined] [9] copyto! @ ./broadcast.jl:925 [inlined] [10] materialize! @ ./broadcast.jl:883 [inlined] [11] materialize! @ ./broadcast.jl:880 [inlined] [12] _copyto_banded!(T::Tridiagonal{BigFloat, Vector{BigFloat}}, D::Diagonal{Float64, Vector{Float64}}) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:323 [13] copyto!(dest::Tridiagonal{BigFloat, Vector{BigFloat}}, src::Diagonal{Float64, Vector{Float64}}) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/special.jl:315 [14] top-level scope @ REPL[4]:1 ``` After this PR ```julia julia> copyto!(T, D) 4×4 Tridiagonal{BigFloat, Vector{BigFloat}}: 0.909968 0.0 ⋅ ⋅ 0.0 0.193341 0.0 ⋅ ⋅ 0.0 0.194794 0.0 ⋅ ⋅ 0.0 0.506905 ``` The current implementation used an optimization that may not be applicable for non-isbits types, and this PR ensures that we always read from the source and write to the destination. --- stdlib/LinearAlgebra/src/bidiag.jl | 2 +- stdlib/LinearAlgebra/src/special.jl | 14 +++++++------- stdlib/LinearAlgebra/src/tridiag.jl | 2 +- stdlib/LinearAlgebra/test/special.jl | 17 +++++++++-------- stdlib/LinearAlgebra/test/tridiag.jl | 2 +- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 0caaec02b5056..24958422015ab 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -305,7 +305,7 @@ function _copyto_banded!(A::Bidiagonal, B::Bidiagonal) if A.uplo == B.uplo A.ev .= B.ev elseif iszero(B.ev) # diagonal source - A.ev .= zero.(A.ev) + A.ev .= B.ev else zeroband = istriu(A) ? "lower" : "upper" uplo = A.uplo diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 9633594574055..28a1b4b1b2eab 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -320,20 +320,20 @@ function copyto!(dest::BandedMatrix, src::BandedMatrix) end function _copyto_banded!(T::Tridiagonal, D::Diagonal) T.d .= D.diag - T.dl .= zero.(T.dl) - T.du .= zero.(T.du) + T.dl .= view(D, diagind(D, -1, IndexStyle(D))) + T.du .= view(D, diagind(D, 1, IndexStyle(D))) return T end function _copyto_banded!(SymT::SymTridiagonal, D::Diagonal) issymmetric(D) || throw(ArgumentError("cannot copy a non-symmetric Diagonal matrix to a SymTridiagonal")) SymT.dv .= D.diag _ev = _evview(SymT) - _ev .= zero.(_ev) + _ev .= view(D, diagind(D, 1, IndexStyle(D))) return SymT end function _copyto_banded!(B::Bidiagonal, D::Diagonal) B.dv .= D.diag - B.ev .= zero.(B.ev) + B.ev .= view(D, diagind(D, B.uplo == 'U' ? 1 : -1, IndexStyle(D))) return B end function _copyto_banded!(D::Diagonal, B::Bidiagonal) @@ -361,10 +361,10 @@ function _copyto_banded!(T::Tridiagonal, B::Bidiagonal) T.d .= B.dv if B.uplo == 'U' T.du .= B.ev - T.dl .= zero.(T.dl) + T.dl .= view(B, diagind(B, -1, IndexStyle(B))) else T.dl .= B.ev - T.du .= zero.(T.du) + T.du .= view(B, diagind(B, 1, IndexStyle(B))) end return T end @@ -372,7 +372,7 @@ function _copyto_banded!(SymT::SymTridiagonal, B::Bidiagonal) issymmetric(B) || throw(ArgumentError("cannot copy a non-symmetric Bidiagonal matrix to a SymTridiagonal")) SymT.dv .= B.dv _ev = _evview(SymT) - _ev .= zero.(_ev) + _ev .= B.ev return SymT end function _copyto_banded!(B::Bidiagonal, T::Tridiagonal) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 2ff688f4b4ed1..e217425402df9 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -1040,7 +1040,7 @@ function _copyto_banded!(A::Tridiagonal, B::SymTridiagonal) return A end function _copyto_banded!(A::SymTridiagonal, B::Tridiagonal) - issymmetric(B) || throw(ArgumentError("cannot copy a non-symmetric Tridiagonal matrix to a SymTridiagonal")) + issymmetric(B) || throw(ArgumentError("cannot copy an asymmetric Tridiagonal matrix to a SymTridiagonal")) A.dv .= B.d _evview(A) .= B.du return A diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index 2f870373c9586..9bb84ba0e9d03 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -555,8 +555,8 @@ end @testset "from Diagonal" begin D = Diagonal(d) @testset "to Bidiagonal" begin - BU = Bidiagonal(zero(d), oneunit.(du), :U) - BL = Bidiagonal(zero(d), oneunit.(dl), :L) + BU = Bidiagonal(similar(d, BigInt), similar(du, BigInt), :U) + BL = Bidiagonal(similar(d, BigInt), similar(dl, BigInt), :L) for B in (BL, BU) copyto!(B, D) @test B == D @@ -573,7 +573,7 @@ end end end @testset "to Tridiagonal" begin - T = Tridiagonal(oneunit.(dl), zero(d), oneunit.(du)) + T = Tridiagonal(similar(dl, BigInt), similar(d, BigInt), similar(du, BigInt)) copyto!(T, D) @test T == D @@ -586,8 +586,8 @@ end end end @testset "to SymTridiagonal" begin - for du2 in (oneunit.(du), oneunit.(d)) - S = SymTridiagonal(zero(d), du2) + for du2 in (similar(du, BigInt), similar(d, BigInt)) + S = SymTridiagonal(similar(d), du2) copyto!(S, D) @test S == D end @@ -630,13 +630,14 @@ end end end @testset "to Tridiagonal" begin - T = Tridiagonal(oneunit.(dl), zero(d), oneunit.(du)) + T = Tridiagonal(similar(dl, BigInt), similar(d, BigInt), similar(du, BigInt)) for B in (BL, BU, BLones, BUones) copyto!(T, B) @test T == B end @testset "mismatched size" begin + T = Tridiagonal(oneunit.(dl), zero(d), oneunit.(du)) for uplo in (:L, :U) T .= 0 copyto!(T, Bidiagonal([1], Int[], uplo)) @@ -647,8 +648,8 @@ end end end @testset "to SymTridiagonal" begin - for du2 in (oneunit.(du), oneunit.(d)) - S = SymTridiagonal(zero(d), du2) + for du2 in (similar(du, BigInt), similar(d, BigInt)) + S = SymTridiagonal(similar(d, BigInt), du2) for B in (BL, BU) copyto!(S, B) @test S == B diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 5dc1d01e850d8..41a28631b27a0 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -808,7 +808,7 @@ end @test copyto!(zero(S), T) == T T2 = Tridiagonal(ones(length(ev)), zero(dv), zero(ev)) - @test_throws "cannot copy a non-symmetric Tridiagonal matrix to a SymTridiagonal" copyto!(zero(S), T2) + @test_throws "cannot copy an asymmetric Tridiagonal matrix to a SymTridiagonal" copyto!(zero(S), T2) @testset "mismatched sizes" begin dv2 = [4; @view dv[2:end]] From 22f55804b9c0da372c8a5f8819732b034b442b51 Mon Sep 17 00:00:00 2001 From: Markus Hauru Date: Tue, 6 Aug 2024 22:28:51 +0100 Subject: [PATCH 024/548] Deprecate conflicting @testset arguments (#55174) Currently `@testset` allows specifying multiple descriptions and testset types, and only the last one will take effect. The others will be silently ignored. This PR starts printing deprecation warnings whenever such conflicting arguments are provided. --- stdlib/Test/src/Test.jl | 10 ++++++++++ stdlib/Test/test/runtests.jl | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 3ecf0c151164d..b4ada2ce3a9cf 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1838,9 +1838,19 @@ function parse_testset_args(args) # a standalone symbol is assumed to be the test set we should use # the same is true for a symbol that's not exported from a module if isa(arg, Symbol) || Base.isexpr(arg, :.) + if testsettype !== nothing + msg = """Multiple testset types provided to @testset. \ + This is deprecated and may error in the future.""" + Base.depwarn(msg, :testset_multiple_testset_types; force=true) + end testsettype = esc(arg) # a string is the description elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head === :string) + if desc !== nothing + msg = """Multiple descriptions provided to @testset. \ + This is deprecated and may error in the future.""" + Base.depwarn(msg, :testset_multiple_descriptions; force=true) + end desc = esc(arg) # an assignment is an option elseif isa(arg, Expr) && arg.head === :(=) diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 31919c2881f6b..3ddcd7d5de0fd 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1725,4 +1725,14 @@ end result = read(pipeline(ignorestatus(cmd), stderr=devnull), String) @test occursin(expected, result) end + +end + +@testset "Deprecated multiple arguments" begin + msg1 = """Multiple descriptions provided to @testset. \ + This is deprecated and may error in the future.""" + @test_deprecated msg1 @macroexpand @testset "name1" "name2" begin end + msg2 = """Multiple testset types provided to @testset. \ + This is deprecated and may error in the future.""" + @test_deprecated msg2 @macroexpand @testset DefaultTestSet DefaultTestSet begin end end From e4678ab775dadc95dd8bb050637817e449ee7300 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Tue, 6 Aug 2024 17:43:27 -0400 Subject: [PATCH 025/548] Increase default stack size limit on 64-bit systems (#55185) This increases the default stack size limit on 64-bit systems from 4 MB to 8 MB, matching glibc and typical modern Linux and macOS machines, as well as the stack size limit of the root Julia process. --- src/options.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.h b/src/options.h index 5da9bf2b1a740..800be866183b0 100644 --- a/src/options.h +++ b/src/options.h @@ -110,7 +110,7 @@ #if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_) #define JL_STACK_SIZE (64*1024*1024) #elif defined(_P64) -#define JL_STACK_SIZE (4*1024*1024) +#define JL_STACK_SIZE (8*1024*1024) #else #define JL_STACK_SIZE (2*1024*1024) #endif From 016d035cd2678d3af90601ca2aa3aed35d50721d Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 6 Aug 2024 19:48:10 -0400 Subject: [PATCH 026/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20e4a6723bf=20to=207aef1f044=20(#55399)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 | 1 + .../Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 | 1 + .../Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/md5 | 1 - .../Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 create mode 100644 deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/sha512 diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 new file mode 100644 index 0000000000000..218260c77ea07 --- /dev/null +++ b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 @@ -0,0 +1 @@ +832f88c404516179ece213581cd227f8 diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 new file mode 100644 index 0000000000000..fc763c8d86f40 --- /dev/null +++ b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 @@ -0,0 +1 @@ +4355bb51a7f83bde489e587527e1e3a9c70799a5c0d27cd7f42b4227a5fbca2a1200a83db0317a75c582ee997bec72e9e8afafb059c395bd46e2aa015f481dca diff --git a/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/md5 b/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/md5 deleted file mode 100644 index 9151d83645ac6..0000000000000 --- a/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e42b7400acc62aa5987dca1be49290ae diff --git a/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/sha512 b/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/sha512 deleted file mode 100644 index 5498d9f6a3069..0000000000000 --- a/deps/checksums/Pkg-e4a6723bf3074764ff9266e5e13dfea501431b33.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -71dd216051496210416db6a3489f9a934eb21ba59054fa921758e588450c6b1450f7771c8796d7b78ae3e481e0d730ad1bf48573dd2ec39975a840f9cc33114a diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 7742b41441528..964c43dfcc786 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = e4a6723bf3074764ff9266e5e13dfea501431b33 +PKG_SHA1 = 7aef1f044f3483e8b07d33fb4cfe918be554de69 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From b3a62b4c30f718e7f0f226342abb0b636b91f3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Wed, 7 Aug 2024 03:16:44 +0200 Subject: [PATCH 027/548] Restore cmdlineargs tests on non-Windows platforms (#55368) --- test/cmdlineargs.jl | 64 ++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 01a8acaeaea94..c6720e23739d8 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -339,43 +339,37 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test errors_not_signals(`$exename -C invalidtarget`) @test errors_not_signals(`$exename --cpu-target=invalidtarget`) - if Sys.iswindows() - # -t, --threads - code = "print(Threads.threadpoolsize())" - cpu_threads = ccall(:jl_effective_threads, Int32, ()) - @test string(cpu_threads) == - read(`$exename --threads auto -e $code`, String) == - read(`$exename --threads=auto -e $code`, String) == - read(`$exename -tauto -e $code`, String) == - read(`$exename -t auto -e $code`, String) - for nt in (nothing, "1") - withenv("JULIA_NUM_THREADS" => nt) do - @test read(`$exename --threads=2 -e $code`, String) == - read(`$exename -t 2 -e $code`, String) == "2" - end - end - # We want to test oversubscription, but on manycore machines, this can - # actually exhaust limited PID spaces - cpu_threads = max(2*cpu_threads, min(50, 10*cpu_threads)) - if Sys.WORD_SIZE == 32 - cpu_threads = min(cpu_threads, 50) - end - @test read(`$exename -t $cpu_threads -e $code`, String) == string(cpu_threads) - withenv("JULIA_NUM_THREADS" => string(cpu_threads)) do - @test read(`$exename -e $code`, String) == string(cpu_threads) + # -t, --threads + code = "print(Threads.threadpoolsize())" + cpu_threads = ccall(:jl_effective_threads, Int32, ()) + @test string(cpu_threads) == + read(`$exename --threads auto -e $code`, String) == + read(`$exename --threads=auto -e $code`, String) == + read(`$exename -tauto -e $code`, String) == + read(`$exename -t auto -e $code`, String) + for nt in (nothing, "1") + withenv("JULIA_NUM_THREADS" => nt) do + @test read(`$exename --threads=2 -e $code`, String) == + read(`$exename -t 2 -e $code`, String) == "2" end - @test errors_not_signals(`$exename -t 0`) - @test errors_not_signals(`$exename -t -1`) + end + # We want to test oversubscription, but on manycore machines, this can + # actually exhaust limited PID spaces + cpu_threads = max(2*cpu_threads, min(50, 10*cpu_threads)) + if Sys.WORD_SIZE == 32 + cpu_threads = min(cpu_threads, 50) + end + @test read(`$exename -t $cpu_threads -e $code`, String) == string(cpu_threads) + withenv("JULIA_NUM_THREADS" => string(cpu_threads)) do + @test read(`$exename -e $code`, String) == string(cpu_threads) + end + @test errors_not_signals(`$exename -t 0`) + @test errors_not_signals(`$exename -t -1`) - # Combining --threads and --procs: --threads does propagate - withenv("JULIA_NUM_THREADS" => nothing) do - code = "print(sum(remotecall_fetch(Threads.threadpoolsize, x) for x in procs()))" - @test read(`$exename -p2 -t2 -e $code`, String) == "6" - end - else - @test_skip "Command line tests with -t are flakey on non-Windows OS" - # Known issue: https://github.com/JuliaLang/julia/issues/49154 - # These tests should be fixed and reenabled on all operating systems. + # Combining --threads and --procs: --threads does propagate + withenv("JULIA_NUM_THREADS" => nothing) do + code = "print(sum(remotecall_fetch(Threads.threadpoolsize, x) for x in procs()))" + @test read(`$exename -p2 -t2 -e $code`, String) == "6" end # Combining --threads and invalid -C should yield a decent error From b43e24715869b15eb8a9f6e669a03f4722bfb32f Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 7 Aug 2024 07:48:13 -0400 Subject: [PATCH 028/548] optimized textwidth(::Char) for ASCII (#55398) --- base/strings/unicode.jl | 9 +++++++++ test/strings/util.jl | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 42a4106d0f52f..3c6710025077c 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -256,6 +256,15 @@ julia> textwidth('⛵') ``` """ function textwidth(c::AbstractChar) + ismalformed(c) && return 1 + i = codepoint(c) + i < 0x7f && return Int(i >= 0x20) # ASCII fast path + Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), i)) +end + +function textwidth(c::Char) + b = bswap(reinterpret(UInt32, c)) # from isascii(c) + b < 0x7f && return Int(b >= 0x20) # ASCII fast path ismalformed(c) && return 1 Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), c)) end diff --git a/test/strings/util.jl b/test/strings/util.jl index 59638dc3b9ca6..e5db9bd03ae8e 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -2,6 +2,20 @@ SubStr(s) = SubString("abc$(s)de", firstindex(s) + 3, lastindex(s) + 3) +@testset "textwidth" begin + for (c, w) in [('x', 1), ('α', 1), ('🍕', 2), ('\0', 0), ('\u0302', 0), ('\xc0', 1)] + @test textwidth(c) == w + @test textwidth(c^3) == w*3 + @test w == @invoke textwidth(c::AbstractChar) + end + for i in 0x00:0x7f # test all ASCII chars (which have fast path) + w = Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), i)) + c = Char(i) + @test textwidth(c) == w + @test w == @invoke textwidth(c::AbstractChar) + end +end + @testset "padding (lpad and rpad)" begin @test lpad("foo", 2) == "foo" @test rpad("foo", 2) == "foo" From bd582f78ed3ecd3950b676e14bf0a7a64092aa3d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 7 Aug 2024 16:42:30 +0000 Subject: [PATCH 029/548] Accept axes in Base.checkdims_perm (#55403) Since `checkdims_perm` only checks the axes of the arrays that are passed to it, this PR adds a method that accepts the axes as arguments instead of the arrays. This will avoid having to specialize on array types. An example of an improvement: On master ```julia julia> using LinearAlgebra julia> D = Diagonal(zeros(1)); julia> Dv = Diagonal(view(zeros(1),:)); julia> @time @eval permutedims(D, (2,1)); 0.016841 seconds (13.68 k allocations: 680.672 KiB, 51.37% compilation time) julia> @time @eval permutedims(Dv, (2,1)); 0.009303 seconds (11.24 k allocations: 564.203 KiB, 97.79% compilation time) ``` This PR ```julia julia> @time @eval permutedims(D, (2,1)); 0.016837 seconds (13.42 k allocations: 667.438 KiB, 51.05% compilation time) julia> @time @eval permutedims(Dv, (2,1)); 0.009076 seconds (6.59 k allocations: 321.156 KiB, 97.46% compilation time) ``` The allocations are lower in the second call. I've retained the original method as well, as some packages seem to be using it. This now forwards the axes to the new method. --- base/multidimensional.jl | 9 ++++----- base/permuteddimsarray.jl | 2 +- stdlib/LinearAlgebra/src/bidiag.jl | 2 +- stdlib/LinearAlgebra/src/diagonal.jl | 2 +- stdlib/LinearAlgebra/src/tridiag.jl | 4 ++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index bd3641db4999c..5e32a19c2cafb 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1669,11 +1669,10 @@ function permutedims(B::StridedArray, perm) permutedims!(P, B, perm) end -function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N} - indsB = axes(B) - length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))")) +checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N} = checkdims_perm(axes(P), axes(B), perm) +function checkdims_perm(indsP::NTuple{N, AbstractUnitRange}, indsB::NTuple{N, AbstractUnitRange}, perm) where {N} + length(perm) == N || throw(ArgumentError(LazyString("expected permutation of size ", N, ", but length(perm)=", length(perm)))) isperm(perm) || throw(ArgumentError("input is not a permutation")) - indsP = axes(P) for i in eachindex(perm) indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size")) end @@ -1683,7 +1682,7 @@ end for (V, PT, BT) in Any[((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)] @eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...) quote - checkdims_perm(P, B, perm) + checkdims_perm(axes(P), axes(B), perm) #calculates all the strides native_strides = size_to_strides(1, size(B)...) diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl index 4e77d6b13ce21..cf9748168aac2 100644 --- a/base/permuteddimsarray.jl +++ b/base/permuteddimsarray.jl @@ -282,7 +282,7 @@ regions. See also [`permutedims`](@ref). """ function permutedims!(dest, src::AbstractArray, perm) - Base.checkdims_perm(dest, src, perm) + Base.checkdims_perm(axes(dest), axes(src), perm) P = PermutedDimsArray(dest, invperm(perm)) _copy!(P, src) return dest diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 24958422015ab..adb5f8c51bf47 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -287,7 +287,7 @@ adjoint(B::Bidiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = transpose(B::Bidiagonal{<:Number}) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? :L : :U) permutedims(B::Bidiagonal) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? 'L' : 'U') function permutedims(B::Bidiagonal, perm) - Base.checkdims_perm(B, B, perm) + Base.checkdims_perm(axes(B), axes(B), perm) NTuple{2}(perm) == (2, 1) ? permutedims(B) : B end function Base.copy(aB::Adjoint{<:Any,<:Bidiagonal}) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 89202e66597f8..77459f7cca520 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -745,7 +745,7 @@ adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag))) adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag))) adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) permutedims(D::Diagonal) = D -permutedims(D::Diagonal, perm) = (Base.checkdims_perm(D, D, perm); D) +permutedims(D::Diagonal, perm) = (Base.checkdims_perm(axes(D), axes(D), perm); D) function diag(D::Diagonal{T}, k::Integer=0) where T # every branch call similar(..., ::Int) to make sure the diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index e217425402df9..c14ed5690198c 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -173,7 +173,7 @@ adjoint(S::SymTridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) permutedims(S::SymTridiagonal) = S function permutedims(S::SymTridiagonal, perm) - Base.checkdims_perm(S, S, perm) + Base.checkdims_perm(axes(S), axes(S), perm) NTuple{2}(perm) == (2, 1) ? permutedims(S) : S end Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(adjoint.(x)), (S.parent.dv, S.parent.ev))...) @@ -639,7 +639,7 @@ adjoint(S::Tridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = transpose(S::Tridiagonal{<:Number}) = Tridiagonal(S.du, S.d, S.dl) permutedims(T::Tridiagonal) = Tridiagonal(T.du, T.d, T.dl) function permutedims(T::Tridiagonal, perm) - Base.checkdims_perm(T, T, perm) + Base.checkdims_perm(axes(T), axes(T), perm) NTuple{2}(perm) == (2, 1) ? permutedims(T) : T end Base.copy(aS::Adjoint{<:Any,<:Tridiagonal}) = (S = aS.parent; Tridiagonal(map(x -> copy.(adjoint.(x)), (S.du, S.d, S.dl))...)) From c767032b8ffec975cf2335105de082a6e6079c31 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 8 Aug 2024 02:12:08 +0000 Subject: [PATCH 030/548] Improve performance in `Bidiagonal` times `Diagonal` (#55175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds specialized methods to improve performance, and avoid allocations that were arising currently from the fallback tridiagonal implementations. ```julia julia> using LinearAlgebra, BenchmarkTools julia> n = 10000; B = Bidiagonal(rand(n), rand(n-1), :U); D = Diagonal(rand(size(B,1))); C = similar(B, size(B)); julia> @btime mul!($C, $B, $D); 25.552 ms (3 allocations: 78.19 KiB) # v"1.12.0-DEV.870" 25.559 ms (0 allocations: 0 bytes) # This PR julia> C = similar(B); julia> @btime mul!($C, $B, $D); 23.551 μs (3 allocations: 78.19 KiB) # v"1.12.0-DEV.870" 7.123 μs (0 allocations: 0 bytes) # This PR, specialized method ``` --- stdlib/LinearAlgebra/src/bidiag.jl | 168 +++++++++++++++++++++++++--- stdlib/LinearAlgebra/test/bidiag.jl | 39 ++++++- test/testhelpers/SizedArrays.jl | 4 + 3 files changed, 192 insertions(+), 19 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index adb5f8c51bf47..5aa4314c9ae51 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -299,6 +299,13 @@ function Base.copy(tB::Transpose{<:Any,<:Bidiagonal}) return Bidiagonal(map(x -> copy.(transpose.(x)), (B.dv, B.ev))..., B.uplo == 'U' ? :L : :U) end +@noinline function throw_zeroband_error(A) + uplo = A.uplo + zeroband = uplo == 'U' ? "lower" : "upper" + throw(ArgumentError(LazyString("cannot set the ", + zeroband, " bidiagonal band to a nonzero value for uplo=:", uplo))) +end + # copyto! for matching axes function _copyto_banded!(A::Bidiagonal, B::Bidiagonal) A.dv .= B.dv @@ -307,10 +314,7 @@ function _copyto_banded!(A::Bidiagonal, B::Bidiagonal) elseif iszero(B.ev) # diagonal source A.ev .= B.ev else - zeroband = istriu(A) ? "lower" : "upper" - uplo = A.uplo - throw(ArgumentError(LazyString("cannot set the ", - zeroband, " bidiagonal band to a nonzero value for uplo=:", uplo))) + throw_zeroband_error(A) end return A end @@ -620,7 +624,6 @@ function _mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, _add::MulAddMul) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C Al = _diag(A, -1) @@ -629,28 +632,99 @@ function _mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, _add::MulAddMul) Bd = B.diag @inbounds begin # first row of C - C[1,1] += _add(A[1,1]*B[1,1]) - C[1,2] += _add(A[1,2]*B[2,2]) + for j in 1:min(2, n) + C[1,j] += _add(A[1,j]*B[j,j]) + end # second row of C - C[2,1] += _add(A[2,1]*B[1,1]) - C[2,2] += _add(A[2,2]*B[2,2]) - C[2,3] += _add(A[2,3]*B[3,3]) + if n > 1 + for j in 1:min(3, n) + C[2,j] += _add(A[2,j]*B[j,j]) + end + end for j in 3:n-2 C[j, j-1] += _add(Al[j-1]*Bd[j-1]) C[j, j ] += _add(Ad[j ]*Bd[j ]) C[j, j+1] += _add(Au[j ]*Bd[j+1]) end - # row before last of C - C[n-1,n-2] += _add(A[n-1,n-2]*B[n-2,n-2]) - C[n-1,n-1] += _add(A[n-1,n-1]*B[n-1,n-1]) - C[n-1,n ] += _add(A[n-1, n]*B[n ,n ]) + if n > 3 + # row before last of C + for j in n-2:n + C[n-1,j] += _add(A[n-1,j]*B[j,j]) + end + end # last row of C - C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1]) - C[n,n ] += _add(A[n,n ]*B[n, n ]) + if n > 2 + for j in n-1:n + C[n,j] += _add(A[n,j]*B[j,j]) + end + end end # inbounds C end +function _mul!(C::AbstractMatrix, A::Bidiagonal, B::Diagonal, _add::MulAddMul) + require_one_based_indexing(C) + check_A_mul_B!_sizes(size(C), size(A), size(B)) + n = size(A,1) + iszero(n) && return C + _rmul_or_fill!(C, _add.beta) # see the same use above + iszero(_add.alpha) && return C + (; dv, ev) = A + Bd = B.diag + rowshift = A.uplo == 'U' ? -1 : 1 + evshift = Int(A.uplo == 'U') + @inbounds begin + # first row of C + C[1,1] += _add(dv[1]*Bd[1]) + if n > 1 + if A.uplo == 'L' + C[2,1] += _add(ev[1]*Bd[1]) + end + for col in 2:n-1 + C[col+rowshift, col] += _add(ev[col - evshift]*Bd[col]) + C[col, col] += _add(dv[col]*Bd[col]) + end + if A.uplo == 'U' + C[n-1,n] += _add(ev[n-1]*Bd[n]) + end + C[n, n] += _add(dv[n]*Bd[n]) + end + end # inbounds + C +end + +function _mul!(C::Bidiagonal, A::Bidiagonal, B::Diagonal, _add::MulAddMul) + check_A_mul_B!_sizes(size(C), size(A), size(B)) + n = size(A,1) + iszero(n) && return C + iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) + Adv, Aev = A.dv, A.ev + Cdv, Cev = C.dv, C.ev + Bd = B.diag + shift = Int(A.uplo == 'U') + if C.uplo == A.uplo + @inbounds begin + _modify!(_add, Adv[1]*Bd[1], Cdv, 1) + for j in eachindex(IndexLinear(), Aev, Cev) + _modify!(_add, Aev[j]*Bd[j+shift], Cev, j) + _modify!(_add, Adv[j+1]*Bd[j+1], Cdv, j+1) + end + end # inbounds + else + @inbounds begin + _modify!(_add, Adv[1]*Bd[1], Cdv, 1) + for j in eachindex(IndexLinear(), Aev, Cev) + _modify!(_add, Adv[j+1]*Bd[j+1], Cdv, j+1) + # this branch will error unless the value is zero + _modify!(_add, Aev[j]*Bd[j+shift], C, (j+1-shift, j+shift)) + # zeros of the correct type + _modify!(_add, A[j+shift, j+1-shift]*Bd[j+1-shift], Cev, j) + end + end + end + C +end + function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulAddMul) require_one_based_indexing(C, B) nA = size(A,1) @@ -781,6 +855,68 @@ function _dibimul!(C, A, B, _add) end # inbounds C end +function _dibimul!(C::AbstractMatrix, A::Diagonal, B::Bidiagonal, _add) + require_one_based_indexing(C) + check_A_mul_B!_sizes(size(C), size(A), size(B)) + n = size(A,1) + iszero(n) && return C + _rmul_or_fill!(C, _add.beta) # see the same use above + iszero(_add.alpha) && return C + Ad = A.diag + Bdv, Bev = B.dv, B.ev + rowshift = B.uplo == 'U' ? -1 : 1 + evshift = Int(B.uplo == 'U') + @inbounds begin + # first row of C + C[1,1] += _add(Ad[1]*Bdv[1]) + if n > 1 + if B.uplo == 'L' + C[2,1] += _add(Ad[2]*Bev[1]) + end + for col in 2:n-1 + evrow = col+rowshift + C[evrow, col] += _add(Ad[evrow]*Bev[col - evshift]) + C[col, col] += _add(Ad[col]*Bdv[col]) + end + if B.uplo == 'U' + C[n-1,n] += _add(Ad[n-1]*Bev[n-1]) + end + C[n, n] += _add(Ad[n]*Bdv[n]) + end + end # inbounds + C +end +function _dibimul!(C::Bidiagonal, A::Diagonal, B::Bidiagonal, _add) + check_A_mul_B!_sizes(size(C), size(A), size(B)) + n = size(A,1) + n == 0 && return C + iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) + Ad = A.diag + Bdv, Bev = B.dv, B.ev + Cdv, Cev = C.dv, C.ev + shift = Int(B.uplo == 'L') + if C.uplo == B.uplo + @inbounds begin + _modify!(_add, Ad[1]*Bdv[1], Cdv, 1) + for j in eachindex(IndexLinear(), Bev, Cev) + _modify!(_add, Ad[j+shift]*Bev[j], Cev, j) + _modify!(_add, Ad[j+1]*Bdv[j+1], Cdv, j+1) + end + end # inbounds + else + @inbounds begin + _modify!(_add, Ad[1]*Bdv[1], Cdv, 1) + for j in eachindex(IndexLinear(), Bev, Cev) + _modify!(_add, Ad[j+1]*Bdv[j+1], Cdv, j+1) + # this branch will error unless the value is zero + _modify!(_add, Ad[j+shift]*Bev[j], C, (j+shift, j+1-shift)) + # zeros of the correct type + _modify!(_add, Ad[j+1-shift]*B[j+1-shift,j+shift], Cev, j) + end + end + end + C +end function *(A::UpperOrUnitUpperTriangular, B::Bidiagonal) TS = promote_op(matprod, eltype(A), eltype(B)) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 3c99d0d3b6f5e..e19d890237a26 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -942,9 +942,6 @@ end @test_throws ArgumentError rmul!(B, A) @test_throws ArgumentError lmul!(A, B) end - D = Diagonal(dv) - @test rmul!(copy(A), D) ≈ A * D - @test lmul!(D, copy(A)) ≈ D * A end @testset "non-commutative" begin S32 = SizedArrays.SizedArray{(3,2)}(rand(3,2)) @@ -966,6 +963,42 @@ end end end +@testset "mul with Diagonal" begin + for n in 0:4 + dv, ev = rand(n), rand(max(n-1,0)) + d = rand(n) + for uplo in (:U, :L) + A = Bidiagonal(dv, ev, uplo) + D = Diagonal(d) + M = Matrix(A) + S = similar(A, size(A)) + @test A * D ≈ mul!(S, A, D) ≈ M * D + @test D * A ≈ mul!(S, D, A) ≈ D * M + @test mul!(copy(S), D, A, 2, 2) ≈ D * M * 2 + S * 2 + @test mul!(copy(S), A, D, 2, 2) ≈ M * D * 2 + S * 2 + + A2 = Bidiagonal(dv, zero(ev), uplo) + M2 = Array(A2) + S2 = Bidiagonal(copy(dv), copy(ev), uplo == (:U) ? (:L) : (:U)) + MS2 = Array(S2) + @test mul!(copy(S2), D, A2) ≈ D * M2 + @test mul!(copy(S2), A2, D) ≈ M2 * D + @test mul!(copy(S2), A2, D, 2, 2) ≈ M2 * D * 2 + MS2 * 2 + @test mul!(copy(S2), D, A2, 2, 2) ≈ D * M2 * 2 + MS2 * 2 + end + end + + t1 = SizedArrays.SizedArray{(2,3)}([1 2 3; 3 4 5]) + t2 = SizedArrays.SizedArray{(3,2)}([1 2; 3 4; 5 6]) + dv, ev, d = fill(t1, 4), fill(2t1, 3), fill(t2, 4) + for uplo in (:U, :L) + A = Bidiagonal(dv, ev, uplo) + D = Diagonal(d) + @test A * D ≈ Array(A) * Array(D) + @test D * A ≈ Array(D) * Array(A) + end +end + @testset "conversion to Tridiagonal for immutable bands" begin n = 4 dv = FillArrays.Fill(3, n) diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index bc02fb5cbbd20..a435ca7591cac 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -64,6 +64,10 @@ function Base.similar(::Type{A}, shape::Tuple{SOneTo, Vararg{SOneTo}}) where {A< R = similar(A, length.(shape)) SizedArray{length.(shape)}(R) end +function Base.similar(x::SizedArray, ::Type{T}, shape::Tuple{SOneTo, Vararg{SOneTo}}) where {T} + sz = map(length, shape) + SizedArray{sz}(similar(parent(x), T, sz)) +end const SizedMatrixLike = Union{SizedMatrix, Transpose{<:Any, <:SizedMatrix}, Adjoint{<:Any, <:SizedMatrix}} From 07f563ea05cb5cce2661d1e5f8a0a6218089689f Mon Sep 17 00:00:00 2001 From: Zentrik Date: Thu, 8 Aug 2024 13:07:38 +0100 Subject: [PATCH 031/548] Fix unterminated strings in bolt makefiles (#55410) --- contrib/bolt/Makefile | 2 +- contrib/pgo-lto-bolt/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/bolt/Makefile b/contrib/bolt/Makefile index 2e911fcbcdc68..ea92ba9ff936a 100644 --- a/contrib/bolt/Makefile +++ b/contrib/bolt/Makefile @@ -52,7 +52,7 @@ FILES_TO_OPTIMIZE := $(shell for file in $(SYMLINKS_TO_OPTIMIZE); do readlink $( AFTER_INSTRUMENT_MESSAGE:='Run `make finish_stage1` to finish off the build. $\ You can now optionally collect more profiling data by running Julia with an appropriate workload, $\ if you wish, run `make clean_profiles` before doing so to remove any profiling data generated by `make finish_stage1`. $\ - You should end up with some data in $(PROFILE_DIR). Afterwards run `make merge_data && make bolt`. $\ + You should end up with some data in $(PROFILE_DIR). Afterwards run `make merge_data && make bolt`.' $(STAGE0_BUILD) $(STAGE1_BUILD): $(MAKE) -C $(JULIA_ROOT) O=$@ configure diff --git a/contrib/pgo-lto-bolt/Makefile b/contrib/pgo-lto-bolt/Makefile index 6787b3bc4e919..fa88cdcd3d6a7 100644 --- a/contrib/pgo-lto-bolt/Makefile +++ b/contrib/pgo-lto-bolt/Makefile @@ -62,7 +62,7 @@ FILES_TO_OPTIMIZE := $(shell for file in $(SYMLINKS_TO_OPTIMIZE); do readlink $( AFTER_INSTRUMENT_MESSAGE:='Run `make finish_stage2` to finish off the build. $\ You can now optionally collect more profiling data by running Julia with an appropriate workload, $\ if you wish, run `make clean_profiles` before doing so to remove any profiling data generated by `make finish_stage2`. $\ - You should end up with some data in $(BOLT_PROFILE_DIR). Afterwards run `make merge_data && make bolt`. $\ + You should end up with some data in $(BOLT_PROFILE_DIR). Afterwards run `make merge_data && make bolt`.' # When building a single libLLVM.so we need to increase -vp-counters-per-site # significantly From e4398360c33b6065e550b6b5b663543c1e497a57 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 8 Aug 2024 09:41:59 -0400 Subject: [PATCH 032/548] add `rtruncate`, `ltruncate`, `ctruncate` for truncating strings in terms of `textwidth` (#55351) Co-authored-by: Timothy Co-authored-by: Steven G. Johnson <2913679+stevengj@users.noreply.github.com> --- NEWS.md | 1 + base/exports.jl | 3 + base/strings/util.jl | 148 ++++++++++++++++++++++++++++++++++++++++ doc/src/base/strings.md | 3 + test/strings/util.jl | 46 +++++++++++++ 5 files changed, 201 insertions(+) diff --git a/NEWS.md b/NEWS.md index 2e9b32befe342..c4e46acca164e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -102,6 +102,7 @@ New library features the uniquing checking ([#53474]) * `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988]) * `Lockable` is now exported ([#54595]) +* New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) Standard library changes ------------------------ diff --git a/base/exports.jl b/base/exports.jl index dbe12f933e597..daba9a010a9e6 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -596,6 +596,7 @@ export codepoint, codeunit, codeunits, + ctruncate, digits, digits!, eachsplit, @@ -620,6 +621,7 @@ export join, lpad, lstrip, + ltruncate, ncodeunits, ndigits, nextind, @@ -632,6 +634,7 @@ export rpad, rsplit, rstrip, + rtruncate, split, string, strip, diff --git a/base/strings/util.jl b/base/strings/util.jl index 4b701001a8676..0ba76e1c76fa0 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -513,6 +513,154 @@ function rpad( r == 0 ? stringfn(s, p^q) : stringfn(s, p^q, first(p, r)) end +""" + rtruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…') + +Truncate `str` to at most `maxwidth` columns (as estimated by [`textwidth`](@ref)), replacing the last characters +with `replacement` if necessary. The default replacement string is "…". + +# Examples +```jldoctest +julia> s = rtruncate("🍕🍕 I love 🍕", 10) +"🍕🍕 I lo…" + +julia> textwidth(s) +10 + +julia> rtruncate("foo", 3) +"foo" +``` + +!!! compat "Julia 1.12" + This function was added in Julia 1.12. + +See also [`ltruncate`](@ref) and [`ctruncate`](@ref). +""" +function rtruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…') + ret = string_truncate_boundaries(str, Int(maxwidth), replacement, Val(:right)) + if isnothing(ret) + return string(str) + else + left, _ = ret::Tuple{Int,Int} + @views return str[begin:left] * replacement + end +end + +""" + ltruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…') + +Truncate `str` to at most `maxwidth` columns (as estimated by [`textwidth`](@ref)), replacing the first characters +with `replacement` if necessary. The default replacement string is "…". + +# Examples +```jldoctest +julia> s = ltruncate("🍕🍕 I love 🍕", 10) +"…I love 🍕" + +julia> textwidth(s) +10 + +julia> ltruncate("foo", 3) +"foo" +``` + +!!! compat "Julia 1.12" + This function was added in Julia 1.12. + +See also [`rtruncate`](@ref) and [`ctruncate`](@ref). +""" +function ltruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…') + ret = string_truncate_boundaries(str, Int(maxwidth), replacement, Val(:left)) + if isnothing(ret) + return string(str) + else + _, right = ret::Tuple{Int,Int} + @views return replacement * str[right:end] + end +end + +""" + ctruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…'; prefer_left::Bool = true) + +Truncate `str` to at most `maxwidth` columns (as estimated by [`textwidth`](@ref)), replacing the middle characters +with `replacement` if necessary. The default replacement string is "…". By default, the truncation +prefers keeping chars on the left, but this can be changed by setting `prefer_left` to `false`. + +# Examples +```jldoctest +julia> s = ctruncate("🍕🍕 I love 🍕", 10) +"🍕🍕 …e 🍕" + +julia> textwidth(s) +10 + +julia> ctruncate("foo", 3) +"foo" +``` + +!!! compat "Julia 1.12" + This function was added in Julia 1.12. + +See also [`ltruncate`](@ref) and [`rtruncate`](@ref). +""" +function ctruncate(str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar} = '…'; prefer_left::Bool = true) + ret = string_truncate_boundaries(str, Int(maxwidth), replacement, Val(:center), prefer_left) + if isnothing(ret) + return string(str) + else + left, right = ret::Tuple{Int,Int} + @views return str[begin:left] * replacement * str[right:end] + end +end + +function string_truncate_boundaries( + str::AbstractString, + maxwidth::Integer, + replacement::Union{AbstractString,AbstractChar}, + ::Val{mode}, + prefer_left::Bool = true) where {mode} + + maxwidth >= 0 || throw(ArgumentError("maxwidth $maxwidth should be non-negative")) + + # check efficiently for early return if str is less wide than maxwidth + total_width = 0 + for c in str + total_width += textwidth(c) + total_width > maxwidth && break + end + total_width <= maxwidth && return nothing + + l0, _ = left, right = firstindex(str), lastindex(str) + width = textwidth(replacement) + # used to balance the truncated width on either side + rm_width_left, rm_width_right, force_other = 0, 0, false + @inbounds while true + if mode === :left || (mode === :center && (!prefer_left || left > l0)) + rm_width = textwidth(str[right]) + if mode === :left || (rm_width_right <= rm_width_left || force_other) + force_other = false + (width += rm_width) <= maxwidth || break + rm_width_right += rm_width + right = prevind(str, right) + else + force_other = true + end + end + if mode ∈ (:right, :center) + rm_width = textwidth(str[left]) + if mode === :left || (rm_width_left <= rm_width_right || force_other) + force_other = false + (width += textwidth(str[left])) <= maxwidth || break + rm_width_left += rm_width + left = nextind(str, left) + else + force_other = true + end + end + end + return prevind(str, left), nextind(str, right) +end + """ eachsplit(str::AbstractString, dlm; limit::Integer=0, keepempty::Bool=true) eachsplit(str::AbstractString; limit::Integer=0, keepempty::Bool=false) diff --git a/doc/src/base/strings.md b/doc/src/base/strings.md index ef470be6b55cc..b7d16ffc7d487 100644 --- a/doc/src/base/strings.md +++ b/doc/src/base/strings.md @@ -48,6 +48,9 @@ Base.:(==)(::AbstractString, ::AbstractString) Base.cmp(::AbstractString, ::AbstractString) Base.lpad Base.rpad +Base.ltruncate +Base.rtruncate +Base.ctruncate Base.findfirst(::AbstractString, ::AbstractString) Base.findnext(::AbstractString, ::AbstractString, ::Integer) Base.findnext(::AbstractChar, ::AbstractString, ::Integer) diff --git a/test/strings/util.jl b/test/strings/util.jl index e5db9bd03ae8e..ae16e24f4ea8b 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -67,6 +67,52 @@ end @test rpad("⟨k|H₁|k⟩", 12) |> textwidth == 12 end +@testset "string truncation (ltruncate, rtruncate, ctruncate)" begin + @test ltruncate("foo", 4) == "foo" + @test ltruncate("foo", 3) == "foo" + @test ltruncate("foo", 2) == "…o" + @test ltruncate("🍕🍕 I love 🍕", 10) == "…I love 🍕" # handle wide emojis + @test ltruncate("🍕🍕 I love 🍕", 10, "[…]") == "[…]love 🍕" + # when the replacement string is longer than the trunc + # trust that the user wants the replacement string rather than erroring + @test ltruncate("abc", 2, "xxxxxx") == "xxxxxx" + + @inferred ltruncate("xxx", 4) + @inferred ltruncate("xxx", 2) + @inferred ltruncate(@view("xxxxxxx"[1:4]), 4) + @inferred ltruncate(@view("xxxxxxx"[1:4]), 2) + + @test rtruncate("foo", 4) == "foo" + @test rtruncate("foo", 3) == "foo" + @test rtruncate("foo", 2) == "f…" + @test rtruncate("🍕🍕 I love 🍕", 10) == "🍕🍕 I lo…" + @test rtruncate("🍕🍕 I love 🍕", 10, "[…]") == "🍕🍕 I […]" + @test rtruncate("abc", 2, "xxxxxx") == "xxxxxx" + + @inferred rtruncate("xxx", 4) + @inferred rtruncate("xxx", 2) + @inferred rtruncate(@view("xxxxxxx"[1:4]), 4) + @inferred rtruncate(@view("xxxxxxx"[1:4]), 2) + + @test ctruncate("foo", 4) == "foo" + @test ctruncate("foo", 3) == "foo" + @test ctruncate("foo", 2) == "f…" + @test ctruncate("foo", 2; prefer_left=true) == "f…" + @test ctruncate("foo", 2; prefer_left=false) == "…o" + @test ctruncate("foobar", 6) == "foobar" + @test ctruncate("foobar", 5) == "fo…ar" + @test ctruncate("foobar", 4) == "fo…r" + @test ctruncate("🍕🍕 I love 🍕", 10) == "🍕🍕 …e 🍕" + @test ctruncate("🍕🍕 I love 🍕", 10, "[…]") == "🍕🍕[…] 🍕" + @test ctruncate("abc", 2, "xxxxxx") == "xxxxxx" + @test ctruncate("🍕🍕🍕🍕🍕🍕xxxxxxxxxxx", 9) == "🍕🍕…xxxx" + + @inferred ctruncate("xxxxx", 5) + @inferred ctruncate("xxxxx", 3) + @inferred ctruncate(@view("xxxxxxx"[1:5]), 5) + @inferred ctruncate(@view("xxxxxxx"[1:5]), 3) +end + # string manipulation @testset "lstrip/rstrip/strip" begin @test strip("") == "" From f0a2a7a0a9438433eb1ac824bd09627ab8fd0586 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Thu, 8 Aug 2024 10:51:48 -0400 Subject: [PATCH 033/548] re-add `unsafe_convert` for Reinterpret and Reshaped array (#55226) Fxes https://github.com/JuliaLang/julia/issues/54725 --- base/reinterpretarray.jl | 1 + base/reshapedarray.jl | 1 + test/ccall.jl | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index d74a043293a3a..d31f3ebb5dd2d 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -373,6 +373,7 @@ has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent) elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T) cconvert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = cconvert(Ptr{S}, a.parent) +unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent)) @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S} if isprimitivetype(T) && isprimitivetype(S) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 4173ef1d3f598..019f1d30a25c2 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -324,6 +324,7 @@ setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err() @noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect") cconvert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = cconvert(Ptr{T}, parent(a)) +unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, a.parent) # Add a few handy specializations to further speed up views of reshaped ranges const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}} diff --git a/test/ccall.jl b/test/ccall.jl index a406af46f0c34..b10504de21abc 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1937,7 +1937,10 @@ end # issue #52025 @test Base.unsafe_convert(Ptr{Ptr{Cchar}}, Base.cconvert(Ptr{Ptr{Cchar}}, map(pointer, ["ab"]))) isa Ptr{Ptr{Cchar}} - +#issue #54725 +for A in (reinterpret(UInt, [0]), reshape([0, 0], 1, 2)) + @test pointer(A) == Base.unsafe_convert(Ptr{Cvoid}, A) == Base.unsafe_convert(Ptr{Int}, A) +end # Cglobal with non-static symbols doesn't error function cglobal_non_static1() sym = (:global_var, libccalltest) From 30d5a3400077f08a7968f7827912920796c63a7c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:41:33 -0700 Subject: [PATCH 034/548] inference: remove `throw` block deoptimization completely (#49260) Co-authored-by: Cody Tapscott Co-authored-by: Oscar Smith --- base/compiler/abstractinterpretation.jl | 9 ------ base/compiler/compiler.jl | 3 +- base/compiler/inferencestate.jl | 25 ---------------- base/compiler/optimize.jl | 40 +++++++++++-------------- base/compiler/types.jl | 21 ------------- src/julia.h | 21 +++++++------ stdlib/REPL/src/REPLCompletions.jl | 3 +- test/compiler/AbstractInterpreter.jl | 1 - test/compiler/codegen.jl | 2 +- test/dict.jl | 4 +-- 10 files changed, 33 insertions(+), 96 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index e3e3502d66173..789b7e6f5a962 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -43,15 +43,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), sv::AbsIntState, max_methods::Int) 𝕃ₚ, 𝕃ᵢ = ipo_lattice(interp), typeinf_lattice(interp) ⊑ₚ, ⊔ₚ, ⊔ᵢ = partialorder(𝕃ₚ), join(𝕃ₚ), join(𝕃ᵢ) - if !should_infer_this_call(interp, sv) - add_remark!(interp, sv, "Skipped call in throw block") - # At this point we are guaranteed to end up throwing on this path, - # which is all that's required for :consistent-cy. Of course, we don't - # know anything else about this statement. - effects = Effects(; consistent=ALWAYS_TRUE) - return CallMeta(Any, Any, effects, NoCallInfo()) - end - argtypes = arginfo.argtypes matches = find_method_matches(interp, argtypes, atype; max_methods) if isa(matches, FailedMethodMatch) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 9aecdaad51aa5..629641308a217 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -184,8 +184,7 @@ baremodule BuildSettings using Core: ARGS, include using Core.Compiler: >, getindex, length -MAX_METHODS::Int = 3 -UNOPTIMIZE_THROW_BLOCKS::Bool = true +global MAX_METHODS::Int = 3 if length(ARGS) > 2 && ARGS[2] === "--buildsettings" include(BuildSettings, ARGS[3]) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 38011656e41ea..06b038ecb6d2e 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -348,7 +348,6 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) # some more setups - InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_info) !iszero(cache_mode & CACHE_MODE_LOCAL) && push!(get_inference_cache(interp), result) this = new( @@ -1102,30 +1101,6 @@ bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::InferenceStat bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::IRInterpretationState) = state.rt === Any -function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) - if InferenceParams(interp).unoptimize_throw_blocks - # Disable inference of calls in throw blocks, since we're unlikely to - # need their types. There is one exception however: If up until now, the - # function has not seen any side effects, we would like to make sure there - # aren't any in the throw block either to enable other optimizations. - if is_stmt_throw_block(get_curr_ssaflag(sv)) - should_infer_for_effects(sv) || return false - end - end - return true -end -function should_infer_for_effects(sv::InferenceState) - def = sv.linfo.def - def isa Method || return false # toplevel frame will not be [semi-]concrete-evaluated - effects = sv.ipo_effects - override = decode_effects_override(def.purity) - effects.consistent === ALWAYS_FALSE && !is_effect_overridden(override, :consistent) && return false - effects.effect_free === ALWAYS_FALSE && !is_effect_overridden(override, :effect_free) && return false - !effects.terminates && !is_effect_overridden(override, :terminates_globally) && return false - return true -end -should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true - add_remark!(::AbstractInterpreter, ::InferenceState, remark) = return add_remark!(::AbstractInterpreter, ::IRInterpretationState, remark) = return diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 85d4a92b3919a..9c89e8596d237 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -23,31 +23,29 @@ const IR_FLAG_INBOUNDS = one(UInt32) << 0 const IR_FLAG_INLINE = one(UInt32) << 1 # This statement is marked as @noinline by user const IR_FLAG_NOINLINE = one(UInt32) << 2 -# This statement is on a code path that eventually `throw`s. -const IR_FLAG_THROW_BLOCK = one(UInt32) << 3 # An optimization pass has updated this statement in a way that may # have exposed information that inference did not see. Re-running # inference on this statement may be profitable. -const IR_FLAG_REFINED = one(UInt32) << 4 +const IR_FLAG_REFINED = one(UInt32) << 3 # This statement is proven :consistent -const IR_FLAG_CONSISTENT = one(UInt32) << 5 +const IR_FLAG_CONSISTENT = one(UInt32) << 4 # This statement is proven :effect_free -const IR_FLAG_EFFECT_FREE = one(UInt32) << 6 +const IR_FLAG_EFFECT_FREE = one(UInt32) << 5 # This statement is proven :nothrow -const IR_FLAG_NOTHROW = one(UInt32) << 7 +const IR_FLAG_NOTHROW = one(UInt32) << 6 # This statement is proven :terminates -const IR_FLAG_TERMINATES = one(UInt32) << 8 +const IR_FLAG_TERMINATES = one(UInt32) << 7 # This statement is proven :noub -const IR_FLAG_NOUB = one(UInt32) << 9 +const IR_FLAG_NOUB = one(UInt32) << 8 # TODO: Both of these should eventually go away once # This statement is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY -const IR_FLAG_EFIIMO = one(UInt32) << 10 +const IR_FLAG_EFIIMO = one(UInt32) << 9 # This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY -const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 11 +const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 10 # This statement has no users and may be deleted if flags get refined to IR_FLAGS_REMOVABLE -const IR_FLAG_UNUSED = one(UInt32) << 12 +const IR_FLAG_UNUSED = one(UInt32) << 11 -const NUM_IR_FLAGS = 13 # sync with julia.h +const NUM_IR_FLAGS = 12 # sync with julia.h const IR_FLAGS_EFFECTS = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_TERMINATES | IR_FLAG_NOUB @@ -249,9 +247,8 @@ end _topmod(sv::OptimizationState) = _topmod(sv.mod) -is_stmt_inline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_INLINE) -is_stmt_noinline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_NOINLINE) -is_stmt_throw_block(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_THROW_BLOCK) +is_stmt_inline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_INLINE) +is_stmt_noinline(stmt_flag::UInt32) = has_flag(stmt_flag, IR_FLAG_NOINLINE) function new_expr_effect_flags(𝕃ₒ::AbstractLattice, args::Vector{Any}, src::Union{IRCode,IncrementalCompact}, pattern_match=nothing) Targ = args[1] @@ -1272,7 +1269,7 @@ plus_saturate(x::Int, y::Int) = max(x, y, x+y) isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isconcretetype(widenconst(T)) function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{VarState}, - params::OptimizationParams, error_path::Bool = false) + params::OptimizationParams) #=const=# UNKNOWN_CALL_COST = 20 head = ex.head if is_meta_expr_head(head) @@ -1333,10 +1330,10 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp return 0 elseif (f === Core.memoryrefget || f === Core.memoryref_isassigned) && length(ex.args) >= 3 atyp = argextype(ex.args[2], src, sptypes) - return isknowntype(atyp) ? 1 : error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return isknowntype(atyp) ? 1 : params.inline_nonleaf_penalty elseif f === Core.memoryrefset! && length(ex.args) >= 3 atyp = argextype(ex.args[2], src, sptypes) - return isknowntype(atyp) ? 5 : error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return isknowntype(atyp) ? 5 : params.inline_nonleaf_penalty elseif f === typeassert && isconstType(widenconst(argextype(ex.args[3], src, sptypes))) return 1 end @@ -1352,7 +1349,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp if extyp === Union{} return 0 end - return error_path ? params.inline_error_path_cost : params.inline_nonleaf_penalty + return params.inline_nonleaf_penalty elseif head === :foreigncall foreigncall = ex.args[1] if foreigncall isa QuoteNode && foreigncall.value === :jl_string_ptr @@ -1375,7 +1372,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp end a = ex.args[2] if a isa Expr - cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params, error_path)) + cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params)) end return cost elseif head === :copyast @@ -1389,8 +1386,7 @@ function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{Cod thiscost = 0 dst(tgt) = isa(src, IRCode) ? first(src.cfg.blocks[tgt].stmts) : tgt if stmt isa Expr - thiscost = statement_cost(stmt, line, src, sptypes, params, - is_stmt_throw_block(isa(src, IRCode) ? src.stmts.flag[line] : src.ssaflags[line]))::Int + thiscost = statement_cost(stmt, line, src, sptypes, params)::Int elseif stmt isa GotoNode # loops are generally always expensive # but assume that forward jumps are already counted for from diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 7021601bf87cf..f315b7968fd9b 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -156,11 +156,6 @@ Parameters that control abstract interpretation-based type inference operation. information available. [`Base.@constprop :aggressive`](@ref Base.@constprop) can have a more fine-grained control on this configuration with per-method annotation basis. --- -- `inf_params.unoptimize_throw_blocks::Bool = true`\\ - If `true`, skips inferring calls that are in a block that is known to `throw`. - It may improve the compiler latency without sacrificing the runtime performance - in common situations. ---- - `inf_params.assume_bindings_static::Bool = false`\\ If `true`, assumes that no new bindings will be added, i.e. a non-existing binding at inference time can be assumed to always not exist at runtime (and thus e.g. any access to @@ -176,7 +171,6 @@ struct InferenceParams tuple_complexity_limit_depth::Int ipo_constant_propagation::Bool aggressive_constant_propagation::Bool - unoptimize_throw_blocks::Bool assume_bindings_static::Bool ignore_recursion_hardlimit::Bool @@ -188,7 +182,6 @@ struct InferenceParams tuple_complexity_limit_depth::Int, ipo_constant_propagation::Bool, aggressive_constant_propagation::Bool, - unoptimize_throw_blocks::Bool, assume_bindings_static::Bool, ignore_recursion_hardlimit::Bool) return new( @@ -199,7 +192,6 @@ struct InferenceParams tuple_complexity_limit_depth, ipo_constant_propagation, aggressive_constant_propagation, - unoptimize_throw_blocks, assume_bindings_static, ignore_recursion_hardlimit) end @@ -213,7 +205,6 @@ function InferenceParams( #=tuple_complexity_limit_depth::Int=# 3, #=ipo_constant_propagation::Bool=# true, #=aggressive_constant_propagation::Bool=# false, - #=unoptimize_throw_blocks::Bool=# BuildSettings.UNOPTIMIZE_THROW_BLOCKS, #=assume_bindings_static::Bool=# false, #=ignore_recursion_hardlimit::Bool=# false); max_methods::Int = params.max_methods, @@ -223,7 +214,6 @@ function InferenceParams( tuple_complexity_limit_depth::Int = params.tuple_complexity_limit_depth, ipo_constant_propagation::Bool = params.ipo_constant_propagation, aggressive_constant_propagation::Bool = params.aggressive_constant_propagation, - unoptimize_throw_blocks::Bool = params.unoptimize_throw_blocks, assume_bindings_static::Bool = params.assume_bindings_static, ignore_recursion_hardlimit::Bool = params.ignore_recursion_hardlimit) return InferenceParams( @@ -234,7 +224,6 @@ function InferenceParams( tuple_complexity_limit_depth, ipo_constant_propagation, aggressive_constant_propagation, - unoptimize_throw_blocks, assume_bindings_static, ignore_recursion_hardlimit) end @@ -259,10 +248,6 @@ Parameters that control optimizer operation. tuple return types (in hopes of splitting it up). `opt_params.inline_tupleret_bonus` will be added to `opt_params.inline_cost_threshold` when making inlining decision. --- -- `opt_params.inline_error_path_cost::Int = 20`\\ - Specifies the penalty cost for an un-optimized dynamic call in a block that is known to - `throw`. See also [`(inf_params::InferenceParams).unoptimize_throw_blocks`](@ref InferenceParams). ---- - `opt_params.max_tuple_splat::Int = 32`\\ When attempting to inline `Core._apply_iterate`, abort the optimization if the tuple contains more than this many elements. @@ -289,7 +274,6 @@ struct OptimizationParams inline_cost_threshold::Int inline_nonleaf_penalty::Int inline_tupleret_bonus::Int - inline_error_path_cost::Int max_tuple_splat::Int compilesig_invokes::Bool assume_fatal_throw::Bool @@ -300,7 +284,6 @@ struct OptimizationParams inline_cost_threshold::Int, inline_nonleaf_penalty::Int, inline_tupleret_bonus::Int, - inline_error_path_cost::Int, max_tuple_splat::Int, compilesig_invokes::Bool, assume_fatal_throw::Bool, @@ -310,7 +293,6 @@ struct OptimizationParams inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, - inline_error_path_cost, max_tuple_splat, compilesig_invokes, assume_fatal_throw, @@ -323,7 +305,6 @@ function OptimizationParams( #=inline_cost_threshold::Int=# 100, #=inline_nonleaf_penalty::Int=# 1000, #=inline_tupleret_bonus::Int=# 250, - #=inline_error_path_cost::Int=# 20, #=max_tuple_splat::Int=# 32, #=compilesig_invokes::Bool=# true, #=assume_fatal_throw::Bool=# false, @@ -332,7 +313,6 @@ function OptimizationParams( inline_cost_threshold::Int = params.inline_cost_threshold, inline_nonleaf_penalty::Int = params.inline_nonleaf_penalty, inline_tupleret_bonus::Int = params.inline_tupleret_bonus, - inline_error_path_cost::Int = params.inline_error_path_cost, max_tuple_splat::Int = params.max_tuple_splat, compilesig_invokes::Bool = params.compilesig_invokes, assume_fatal_throw::Bool = params.assume_fatal_throw, @@ -342,7 +322,6 @@ function OptimizationParams( inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, - inline_error_path_cost, max_tuple_splat, compilesig_invokes, assume_fatal_throw, diff --git a/src/julia.h b/src/julia.h index cbe60e78c2d24..2054a434577e7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -276,7 +276,7 @@ typedef union __jl_purity_overrides_t { } _jl_purity_overrides_t; #define NUM_EFFECTS_OVERRIDES 10 -#define NUM_IR_FLAGS 13 +#define NUM_IR_FLAGS 12 // This type describes a single function body typedef struct _jl_code_info_t { @@ -288,16 +288,15 @@ typedef struct _jl_code_info_t { // 1 << 0 = inbounds region // 1 << 1 = callsite inline region // 1 << 2 = callsite noinline region - // 1 << 3 = throw block - // 1 << 4 = refined statement - // 1 << 5 = :consistent - // 1 << 6 = :effect_free - // 1 << 7 = :nothrow - // 1 << 8 = :terminates - // 1 << 9 = :noub - // 1 << 10 = :effect_free_if_inaccessiblememonly - // 1 << 11 = :inaccessiblemem_or_argmemonly - // 1 << 12-19 = callsite effects overrides + // 1 << 3 = refined statement + // 1 << 4 = :consistent + // 1 << 5 = :effect_free + // 1 << 6 = :nothrow + // 1 << 7 = :terminates + // 1 << 8 = :noub + // 1 << 9 = :effect_free_if_inaccessiblememonly + // 1 << 10 = :inaccessiblemem_or_argmemonly + // 1 << 11-19 = callsite effects overrides // miscellaneous data: jl_array_t *slotnames; // names of local variables jl_array_t *slotflags; // local var bit flags diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 609a7b4d81bc0..dc21cfe529e46 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -556,8 +556,7 @@ struct REPLInterpreter <: CC.AbstractInterpreter function REPLInterpreter(limit_aggressive_inference::Bool=false; world::UInt = Base.get_world_counter(), inf_params::CC.InferenceParams = CC.InferenceParams(; - aggressive_constant_propagation=true, - unoptimize_throw_blocks=false), + aggressive_constant_propagation=true), opt_params::CC.OptimizationParams = CC.OptimizationParams(), inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[]) return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 0d475a8259000..d95354cefa80c 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -176,7 +176,6 @@ end == Val{6} @newinterp Issue48097Interp @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) ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 3d719cbc244e4..cd2702ff0e6aa 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -697,7 +697,7 @@ mktempdir() do pfx libs_deleted += 1 end @test libs_deleted > 0 - @test readchomp(`$pfx/bin/$(Base.julia_exename()) -e 'print("no codegen!\n")'`) == "no codegen!" + @test readchomp(`$pfx/bin/$(Base.julia_exename()) --startup-file=no -e 'print("no codegen!\n")'`) == "no codegen!" # PR #47343 libs_emptied = 0 diff --git a/test/dict.jl b/test/dict.jl index ca8a598de0b81..e327c86521c88 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1510,9 +1510,9 @@ end for T in (Int, Float64, String, Symbol) @testset let T=T @test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T))) - @test_broken Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) @test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T))) - @test_broken Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) end end From 1d7b036b7318215b418a369dff700ea2a406ebec Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 8 Aug 2024 18:24:40 +0000 Subject: [PATCH 035/548] move clamp from math to intfuncs This is a more apt description, since it is not floating point related, and used earlier (such as in IOBuffer). Fixes #55279 --- base/intfuncs.jl | 99 ++++++++++++++++++++++++++++++++++++++++++++++ base/math.jl | 101 +---------------------------------------------- base/missing.jl | 1 + 3 files changed, 101 insertions(+), 100 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index c73ef68551266..f72ac6ee08d4d 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -1237,3 +1237,102 @@ function binomial(x::Number, k::Integer) # and instead divide each term by i, to avoid spurious overflow. return prod(i -> (x-(i-1))/i, OneTo(k), init=oneunit(x)/one(k)) end + +""" + clamp(x, lo, hi) + +Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments +are promoted to a common type. + +See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). + +!!! compat "Julia 1.3" + `missing` as the first argument requires at least Julia 1.3. + +# Examples +```jldoctest +julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) +3-element Vector{BigFloat}: + 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 + 2.0 + 9.0 + +julia> clamp.([11, 8, 5], 10, 6) # an example where lo > hi +3-element Vector{Int64}: + 6 + 6 + 10 +``` +""" +function clamp(x::X, lo::L, hi::H) where {X,L,H} + T = promote_type(X, L, H) + return (x > hi) ? convert(T, hi) : (x < lo) ? convert(T, lo) : convert(T, x) +end + +""" + clamp(x, T)::T + +Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. + +See also [`trunc`](@ref). + +# Examples +```jldoctest +julia> clamp(200, Int8) +127 + +julia> clamp(-200, Int8) +-128 + +julia> trunc(Int, 4pi^2) +39 +``` +""" +function clamp(x, ::Type{T}) where {T<:Integer} + # delegating to clamp(x, typemin(T), typemax(T)) would promote types + # this way, we avoid unnecessary conversions + # think of, e.g., clamp(big(2) ^ 200, Int16) + lo = typemin(T) + hi = typemax(T) + return (x > hi) ? hi : (x < lo) ? lo : convert(T, x) +end + + +""" + clamp!(array::AbstractArray, lo, hi) + +Restrict values in `array` to the specified range, in-place. +See also [`clamp`](@ref). + +!!! compat "Julia 1.3" + `missing` entries in `array` require at least Julia 1.3. + +# Examples +```jldoctest +julia> row = collect(-4:4)'; + +julia> clamp!(row, 0, Inf) +1×9 adjoint(::Vector{Int64}) with eltype Int64: + 0 0 0 0 0 1 2 3 4 + +julia> clamp.((-4:4)', 0, Inf) +1×9 Matrix{Float64}: + 0.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0 +``` +""" +function clamp!(x::AbstractArray, lo, hi) + @inbounds for i in eachindex(x) + x[i] = clamp(x[i], lo, hi) + end + x +end + +""" + clamp(x::Integer, r::AbstractUnitRange) + +Clamp `x` to lie within range `r`. + +!!! compat "Julia 1.6" + This method requires at least Julia 1.6. +""" +clamp(x::Integer, r::AbstractUnitRange{<:Integer}) = clamp(x, first(r), last(r)) diff --git a/base/math.jl b/base/math.jl index de275a2afc048..da51ab3a17bd0 100644 --- a/base/math.jl +++ b/base/math.jl @@ -23,7 +23,7 @@ import .Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, using .Base: sign_mask, exponent_mask, exponent_one, exponent_half, uinttype, significand_mask, significand_bits, exponent_bits, exponent_bias, - exponent_max, exponent_raw_max + exponent_max, exponent_raw_max, clamp, clamp! using Core.Intrinsics: sqrt_llvm @@ -69,104 +69,6 @@ end return Txy, T(xy-Txy) end -""" - clamp(x, lo, hi) - -Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments -are promoted to a common type. - -See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). - -!!! compat "Julia 1.3" - `missing` as the first argument requires at least Julia 1.3. - -# Examples -```jldoctest -julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) -3-element Vector{BigFloat}: - 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 - 2.0 - 9.0 - -julia> clamp.([11, 8, 5], 10, 6) # an example where lo > hi -3-element Vector{Int64}: - 6 - 6 - 10 -``` -""" -function clamp(x::X, lo::L, hi::H) where {X,L,H} - T = promote_type(X, L, H) - return (x > hi) ? convert(T, hi) : (x < lo) ? convert(T, lo) : convert(T, x) -end - -""" - clamp(x, T)::T - -Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. - -See also [`trunc`](@ref). - -# Examples -```jldoctest -julia> clamp(200, Int8) -127 - -julia> clamp(-200, Int8) --128 - -julia> trunc(Int, 4pi^2) -39 -``` -""" -function clamp(x, ::Type{T}) where {T<:Integer} - # delegating to clamp(x, typemin(T), typemax(T)) would promote types - # this way, we avoid unnecessary conversions - # think of, e.g., clamp(big(2) ^ 200, Int16) - lo = typemin(T) - hi = typemax(T) - return (x > hi) ? hi : (x < lo) ? lo : convert(T, x) -end - - -""" - clamp!(array::AbstractArray, lo, hi) - -Restrict values in `array` to the specified range, in-place. -See also [`clamp`](@ref). - -!!! compat "Julia 1.3" - `missing` entries in `array` require at least Julia 1.3. - -# Examples -```jldoctest -julia> row = collect(-4:4)'; - -julia> clamp!(row, 0, Inf) -1×9 adjoint(::Vector{Int64}) with eltype Int64: - 0 0 0 0 0 1 2 3 4 - -julia> clamp.((-4:4)', 0, Inf) -1×9 Matrix{Float64}: - 0.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0 -``` -""" -function clamp!(x::AbstractArray, lo, hi) - @inbounds for i in eachindex(x) - x[i] = clamp(x[i], lo, hi) - end - x -end - -""" - clamp(x::Integer, r::AbstractUnitRange) - -Clamp `x` to lie within range `r`. - -!!! compat "Julia 1.6" - This method requires at least Julia 1.6. -""" -clamp(x::Integer, r::AbstractUnitRange{<:Integer}) = clamp(x, first(r), last(r)) """ evalpoly(x, p) @@ -1690,7 +1592,6 @@ end exp2(x::AbstractFloat) = 2^x exp10(x::AbstractFloat) = 10^x -clamp(::Missing, lo, hi) = missing fourthroot(::Missing) = missing end # module diff --git a/base/missing.jl b/base/missing.jl index ce174edc297e3..1f34195efed88 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -135,6 +135,7 @@ min(::Any, ::Missing) = missing max(::Missing, ::Missing) = missing max(::Missing, ::Any) = missing max(::Any, ::Missing) = missing +clamp(::Missing, lo, hi) = missing missing_conversion_msg(@nospecialize T) = LazyString("cannot convert a missing value to type ", T, ": use Union{", T, ", Missing} instead") From fc6047bf17e0e6ef13fb487852248017d082d949 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 8 Aug 2024 18:42:45 +0000 Subject: [PATCH 036/548] copyuntil: reduce over-allocation to start This fits into a 32-byte allocation pool, saving up to 64 bytes when repeatedly reading small chunks of data (e.g. tokenizing a CSV file). In some local `@btime` measurements, this seems to take <10% more time across a range of output lengths. --- base/io.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/io.jl b/base/io.jl index 0f1812942d23e..83a215d6359fc 100644 --- a/base/io.jl +++ b/base/io.jl @@ -543,8 +543,8 @@ julia> rm("my_file.txt") ``` """ readuntil(filename::AbstractString, delim; kw...) = open(io->readuntil(io, delim; kw...), convert(String, filename)::String) -readuntil(stream::IO, delim::UInt8; kw...) = _unsafe_take!(copyuntil(IOBuffer(sizehint=70), stream, delim; kw...)) -readuntil(stream::IO, delim::Union{AbstractChar, AbstractString}; kw...) = String(_unsafe_take!(copyuntil(IOBuffer(sizehint=70), stream, delim; kw...))) +readuntil(stream::IO, delim::UInt8; kw...) = _unsafe_take!(copyuntil(IOBuffer(sizehint=16), stream, delim; kw...)) +readuntil(stream::IO, delim::Union{AbstractChar, AbstractString}; kw...) = String(_unsafe_take!(copyuntil(IOBuffer(sizehint=16), stream, delim; kw...))) readuntil(stream::IO, delim::T; keep::Bool=false) where T = _copyuntil(Vector{T}(), stream, delim, keep) @@ -617,7 +617,7 @@ Logan readline(filename::AbstractString; keep::Bool=false) = open(io -> readline(io; keep), filename) readline(s::IO=stdin; keep::Bool=false) = - String(_unsafe_take!(copyline(IOBuffer(sizehint=70), s; keep))) + String(_unsafe_take!(copyline(IOBuffer(sizehint=16), s; keep))) """ copyline(out::IO, io::IO=stdin; keep::Bool=false) @@ -1111,7 +1111,7 @@ function copyuntil(out::IO, io::IO, target::AbstractString; keep::Bool=false) end function readuntil(io::IO, target::AbstractVector{T}; keep::Bool=false) where T - out = (T === UInt8 ? resize!(StringVector(70), 0) : Vector{T}()) + out = (T === UInt8 ? resize!(StringVector(16), 0) : Vector{T}()) readuntil_vector!(io, target, keep, out) return out end From be77f650deb8dbf6496e4bb85e19409a0459f1a5 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 8 Aug 2024 14:59:53 -0400 Subject: [PATCH 037/548] improve docs for `collect` and square brackets (#55352) fixes #55350 --------- Co-authored-by: Neven Sajko --- base/abstractarray.jl | 2 ++ base/array.jl | 20 +++++++++++++++++++- base/broadcast.jl | 1 + base/docs/basedocs.jl | 7 +++++-- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 40e36ce15f6ed..77aae63399ec8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3408,6 +3408,8 @@ mapany(f, itr) = Any[f(x) for x in itr] Transform collection `c` by applying `f` to each element. For multiple collection arguments, apply `f` elementwise, and stop when any of them is exhausted. +The element type of the result is determined in the same manner as in [`collect`](@ref). + See also [`map!`](@ref), [`foreach`](@ref), [`mapreduce`](@ref), [`mapslices`](@ref), [`zip`](@ref), [`Iterators.map`](@ref). # Examples diff --git a/base/array.jl b/base/array.jl index 008a52abb952e..648fedd5036e1 100644 --- a/base/array.jl +++ b/base/array.jl @@ -660,7 +660,7 @@ _array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(it """ - collect(collection) + collect(iterator) Return an `Array` of all items in a collection or iterator. For dictionaries, returns a `Vector` of `key=>value` [Pair](@ref Pair)s. If the argument is array-like or is an iterator @@ -671,6 +671,9 @@ Used by [comprehensions](@ref man-comprehensions) to turn a [generator expressio into an `Array`. Thus, *on generators*, the square-brackets notation may be used instead of calling `collect`, see second example. +The element type of the returned array is based on the types of the values collected. However, if the +iterator is empty then the element type of the returned (empty) array is determined by type inference. + # Examples Collect items from a `UnitRange{Int64}` collection: @@ -692,6 +695,21 @@ julia> collect(x^2 for x in 1:3) 4 9 ``` + +Collecting an empty iterator where the result type depends on type inference: + +```jldoctest +julia> [rand(Bool) ? 1 : missing for _ in []] +Union{Missing, Int64}[] +``` + +When the iterator is non-empty, the result type depends only on values: + +```julia-repl +julia> [rand(Bool) ? 1 : missing for _ in [""]] +1-element Vector{Int64}: + 1 +``` """ collect(itr) = _collect(1:1 #= Array =#, itr, IteratorEltype(itr), IteratorSize(itr)) diff --git a/base/broadcast.jl b/base/broadcast.jl index 57eac7f3a094c..927c946e53e02 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -751,6 +751,7 @@ The resulting container type is established by the following rules: - All other combinations of arguments default to returning an `Array`, but custom container types can define their own implementation and promotion-like rules to customize the result when they appear as arguments. + - The element type is determined in the same manner as in [`collect`](@ref). A special syntax exists for broadcasting: `f.(args...)` is equivalent to `broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 2ed1bd98caa5c..e03d0db78f29f 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -663,8 +663,11 @@ kw"{", kw"{}", kw"}" """ [] -Square braces are used for [indexing](@ref man-array-indexing), [indexed assignment](@ref man-indexed-assignment), -[array literals](@ref man-array-literals), and [array comprehensions](@ref man-comprehensions). +Square brackets are used for [indexing](@ref man-array-indexing) ([`getindex`](@ref)), +[indexed assignment](@ref man-indexed-assignment) ([`setindex!`](@ref)), +[array literals](@ref man-array-literals) ([`Base.vect`](@ref)), +[array concatenation](@ref man-array-concatenation) ([`vcat`](@ref), [`hcat`](@ref), [`hvcat`](@ref), [`hvncat`](@ref)), +and [array comprehensions](@ref man-comprehensions) ([`collect`](@ref)). """ kw"[", kw"[]", kw"]" From f2767570fd5b1f3cfb3f69e8acca22cbb850a386 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 8 Aug 2024 15:17:34 -0400 Subject: [PATCH 038/548] REPL: disable flaky win32 stacktrace tests (#55408) Disables these tests on win32 that have been flaky on that platform since February at least https://github.com/JuliaLang/julia/issues/53340 --- stdlib/REPL/test/repl.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 6f0c4a5c3d6ba..f4d594b2a02e1 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -244,8 +244,9 @@ fake_repl(options = REPL.Options(confirm_exit=false,hascolor=true)) do stdin_wri @test occursin("shell> ", s) # check for the echo of the prompt @test occursin("'", s) # check for the echo of the input s = readuntil(stdout_read, "\n\n") - @test startswith(s, "\e[0mERROR: unterminated single quote\nStacktrace:\n [1] ") || - startswith(s, "\e[0m\e[1m\e[91mERROR: \e[39m\e[22m\e[91munterminated single quote\e[39m\nStacktrace:\n [1] ") + @test(startswith(s, "\e[0mERROR: unterminated single quote\nStacktrace:\n [1] ") || + startswith(s, "\e[0m\e[1m\e[91mERROR: \e[39m\e[22m\e[91munterminated single quote\e[39m\nStacktrace:\n [1] "), + skip = Sys.iswindows() && Sys.WORD_SIZE == 32) write(stdin_write, "\b") wait(t) end @@ -1650,12 +1651,12 @@ fake_repl() do stdin_write, stdout_read, repl write(stdin_write, "foobar\n") readline(stdout_read) @test readline(stdout_read) == "\e[0mERROR: UndefVarError: `foobar` not defined in `Main`" - @test readline(stdout_read) == "" + @test readline(stdout_read) == "" skip = Sys.iswindows() && Sys.WORD_SIZE == 32 readuntil(stdout_read, "julia> ", keep=true) # check that top-level error did not change `err` write(stdin_write, "err\n") readline(stdout_read) - @test readline(stdout_read) == "\e[0m" + @test readline(stdout_read) == "\e[0m" skip = Sys.iswindows() && Sys.WORD_SIZE == 32 readuntil(stdout_read, "julia> ", keep=true) # generate deeper error write(stdin_write, "foo() = foobar\n") From 32423a8039daeb57ecd7bc26db5476125c0bfb62 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 8 Aug 2024 15:18:11 -0400 Subject: [PATCH 039/548] handle unbound vars in NTuple fields (#55405) Comparing objects by `==` will happily answer nonsense for malformed type comparisons, such as `unwrap_unionall(A) == A`. Avoid forming that query. Additionally, need to recourse through Vararg when examining type structure to make decisions. Fix #55076 Fix #55189 --- src/builtins.c | 6 ++++++ src/jltypes.c | 6 +++--- test/core.jl | 7 +++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 8cc1465592068..045a9914f5078 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2084,6 +2084,12 @@ static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layou return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) || references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars); } + if (jl_is_vararg(p)) { + jl_value_t *T = ((jl_vararg_t*)p)->T; + jl_value_t *N = ((jl_vararg_t*)p)->N; + return (T && references_name(T, name, affects_layout, freevars)) || + (N && references_name(N, name, affects_layout, freevars)); + } if (jl_is_typevar(p)) return 0; // already checked by unionall, if applicable if (jl_is_datatype(p)) { diff --git a/src/jltypes.c b/src/jltypes.c index fe490d2c45acb..5dc50ff0ca4e6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1961,7 +1961,7 @@ static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *t, int check, int not t = normalize_unionalls(t); p = t; jl_value_t *tw = extract_wrapper(t); - if (tw && t != tw && jl_types_equal(t, tw)) + if (tw && t != tw && !jl_has_free_typevars(t) && jl_types_equal(t, tw)) t = tw; p = t; check = 0; // remember that checks are already done now @@ -2045,7 +2045,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value // normalize types equal to wrappers (prepare for Typeofwrapper) jl_value_t *tw = extract_wrapper(pi); if (tw && tw != pi && (tn != jl_type_typename || jl_typeof(pi) == jl_typeof(tw)) && - jl_types_equal(pi, tw)) { + !jl_has_free_typevars(pi) && jl_types_equal(pi, tw)) { iparams[i] = tw; if (p) jl_gc_wb(p, tw); } @@ -2717,7 +2717,7 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check, int nothrow if (valid) { t = normalize_unionalls(t); jl_value_t *tw = extract_wrapper(t); - if (tw && t != tw && jl_types_equal(t, tw)) + if (tw && t != tw && !jl_has_free_typevars(t) && jl_types_equal(t, tw)) t = tw; } } diff --git a/test/core.jl b/test/core.jl index e765d5a2ab7d7..79373722185b7 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7495,6 +7495,13 @@ struct A43411{S, T} end @test isbitstype(A43411{(:a,), Tuple{Int}}) +# issue #55189 +struct A55189{N} + children::NTuple{N,A55189{N}} +end +@test fieldtype(A55189{2}, 1) === Tuple{A55189{2}, A55189{2}} +@assert !isbitstype(A55189{2}) + # issue #44614 struct T44614_1{T} m::T From 7e1f0be207b5247a8303549f1aec2c73e79c403e Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 8 Aug 2024 16:27:33 -0300 Subject: [PATCH 040/548] codgen: make the Memory GEP an inbounds GEP (#55107) The Julia memory model is always inbounds for GEP. This makes the code in https://github.com/JuliaLang/julia/issues/55090 look almost the same as it did before the change. Locally I wasn't able to reproduce the regression, but given it's vectorized code I suspect it is backend sensitive. Fixes https://github.com/JuliaLang/julia/issues/55090 Co-authored-by: Zentrik --- src/cgutils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 08d51f52b613b..3695dc1370050 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -4286,9 +4286,8 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg ovflw = ctx.builder.CreateICmpUGE(ctx.builder.CreateAdd(offset, mlen), ctx.builder.CreateNUWAdd(mlen, mlen)); } #endif - //Is this change fine boffset = ctx.builder.CreateMul(offset, elsz); - newdata = ctx.builder.CreateGEP(getInt8Ty(ctx.builder.getContext()), data, boffset); + newdata = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), data, boffset); (void)boffset; // LLVM is very bad at handling GEP with types different from the load if (bc) { BasicBlock *failBB, *endBB; From 1db5cf7bfc965d50ae96fa4b1eb34944731cca21 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 8 Aug 2024 18:37:08 -0400 Subject: [PATCH 041/548] ml-matches: ensure all methods are included (#55365) Some methods were filtered out based simply on visit order, which was not intentional, with the lim==-1 weak-edges mode. Fix #55231 --- src/gf.c | 2 +- test/ambiguous.jl | 16 ++++++++++++++++ test/core.jl | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index 659261d434659..5ae7644c01363 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3669,7 +3669,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array int msp2 = !msp && jl_method_morespecific(m2, m); if (!msp) { if (subt || !include_ambiguous || (lim != -1 && msp2)) { - if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + if (subt2 || ((lim != -1 || (!include_ambiguous && !msp2)) && jl_subtype((jl_value_t*)ti, m2->sig))) { // this may be filtered out as fully intersected, if applicable later mayexclude = 1; } diff --git a/test/ambiguous.jl b/test/ambiguous.jl index d6f69f21bcdce..acdfdc70ba30c 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -447,4 +447,20 @@ cc46601(::Type{T}, x::Int) where {T<:AbstractString} = 7 @test length(methods(cc46601, Tuple{Type{<:Integer}, Integer})) == 2 @test length(Base.methods_including_ambiguous(cc46601, Tuple{Type{<:Integer}, Integer})) == 7 +# Issue #55231 +struct U55231{P} end +struct V55231{P} end +U55231(::V55231) = nothing +(::Type{T})(::V55231) where {T<:U55231} = nothing +@test length(methods(U55231)) == 2 +U55231(a, b) = nothing +@test length(methods(U55231)) == 3 +struct S55231{P} end +struct T55231{P} end +(::Type{T})(::T55231) where {T<:S55231} = nothing +S55231(::T55231) = nothing +@test length(methods(S55231)) == 2 +S55231(a, b) = nothing +@test length(methods(S55231)) == 3 + nothing diff --git a/test/core.jl b/test/core.jl index 79373722185b7..4cbb872ce4e50 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7572,7 +7572,7 @@ end # issue #31696 foo31696(x::Int8, y::Int8) = 1 foo31696(x::T, y::T) where {T <: Int8} = 2 -@test length(methods(foo31696)) == 1 +@test length(methods(foo31696)) == 2 let T1 = Tuple{Int8}, T2 = Tuple{T} where T<:Int8, a = T1[(1,)], b = T2[(1,)] b .= a @test b[1] == (1,) From 57aef91b4776e3bc07a813989ab56475b459ab9c Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Thu, 8 Aug 2024 18:56:56 -0400 Subject: [PATCH 042/548] Make `mv` more atomic by trying rename before deleting `dst` (#55384) As noted in https://github.com/JuliaLang/julia/issues/41584 and https://discourse.julialang.org/t/safe-overwriting-of-files/117758/3 `mv` is usually expected to be "best effort atomic". Currently calling `mv` with `force=true` calls `checkfor_mv_cp_cptree(src, dst, "moving"; force=true)` before renaming. `checkfor_mv_cp_cptree` will delete `dst` if exists and isn't the same as `src`. If `dst` is an existing file and julia stops after deleting `dst` but before doing the rename, `dst` will be removed but will not be replaced with `src`. This PR changes `mv` with `force=true` to first try rename, and only delete `dst` if that fails. Assuming file system support and the first rename works, julia stopping will not lead to `dst` being removed without being replaced. This also replaces a stopgap solution from https://github.com/JuliaLang/julia/pull/36638#discussion_r453820564 --- base/file.jl | 77 ++++++++++++++++++++++++++++++++++++++++------ base/loading.jl | 6 ++-- test/file.jl | 2 +- test/filesystem.jl | 2 +- 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/base/file.jl b/base/file.jl index e1b8e8a748fae..3987029d5f74f 100644 --- a/base/file.jl +++ b/base/file.jl @@ -440,11 +440,61 @@ julia> rm("goodbye.txt"); ``` """ function mv(src::AbstractString, dst::AbstractString; force::Bool=false) - checkfor_mv_cp_cptree(src, dst, "moving"; force=force) - rename(src, dst) + if force + _mv_replace(src, dst) + else + _mv_noreplace(src, dst) + end +end + +function _mv_replace(src::AbstractString, dst::AbstractString) + # This check is copied from checkfor_mv_cp_cptree + if ispath(dst) && Base.samefile(src, dst) + abs_src = islink(src) ? abspath(readlink(src)) : abspath(src) + abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst) + throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir. ", + "This is not supported.\n ", + "`src` refers to: $(abs_src)\n ", + "`dst` refers to: $(abs_dst)\n"))) + end + # First try to do a regular rename, because this might avoid a situation + # where dst is deleted or truncated. + try + rename(src, dst) + catch err + err isa IOError || rethrow() + err.code==Base.UV_ENOENT && rethrow() + # on rename error try to delete dst if it exists and isn't the same as src + checkfor_mv_cp_cptree(src, dst, "moving"; force=true) + try + rename(src, dst) + catch err + err isa IOError || rethrow() + # on second error, default to force cp && rm + cp(src, dst; force=true, follow_symlinks=false) + rm(src; recursive=true) + end + end + dst +end + +function _mv_noreplace(src::AbstractString, dst::AbstractString) + # Error if dst exists. + # This check currently has TOCTTOU issues. + checkfor_mv_cp_cptree(src, dst, "moving"; force=false) + try + rename(src, dst) + catch err + err isa IOError || rethrow() + err.code==Base.UV_ENOENT && rethrow() + # on error, default to cp && rm + cp(src, dst; force=false, follow_symlinks=false) + rm(src; recursive=true) + end dst end + """ touch(path::AbstractString) touch(fd::File) @@ -1126,15 +1176,24 @@ function unlink(p::AbstractString) nothing end -# For move command -function rename(src::AbstractString, dst::AbstractString; force::Bool=false) - err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), src, dst) - # on error, default to cp && rm +""" + rename(oldpath::AbstractString, newpath::AbstractString) + +Change the name of a file from `oldpath` to `newpath`. If `newpath` is an existing file it may be replaced. +Equivalent to [rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html). +Throws an `IOError` on failure. +Return `newpath`. + +OS-specific restrictions may apply when `oldpath` and `newpath` are in different directories. + +See also: [`mv`](@ref). +""" +function rename(oldpath::AbstractString, newpath::AbstractString) + err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), oldpath, newpath) if err < 0 - cp(src, dst; force=force, follow_symlinks=false) - rm(src; recursive=true) + uv_error("rename($(repr(oldpath)), $(repr(newpath)))", err) end - nothing + newpath end function sendfile(src::AbstractString, dst::AbstractString) diff --git a/base/loading.jl b/base/loading.jl index c273e4505701f..eb467d9cc0bd4 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3047,7 +3047,9 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in end end # this is atomic according to POSIX (not Win32): - rename(tmppath, cachefile; force=true) + # but force=true means it will fall back to non atomic + # move if the initial rename fails. + mv(tmppath, cachefile; force=true) return cachefile, ocachefile end finally @@ -3066,7 +3068,7 @@ end function rename_unique_ocachefile(tmppath_so::String, ocachefile_orig::String, ocachefile::String = ocachefile_orig, num = 0) try - rename(tmppath_so, ocachefile; force=true) + mv(tmppath_so, ocachefile; force=true) catch e e isa IOError || rethrow() # If `rm` was called on a dir containing a loaded DLL, we moved it to temp for cleanup diff --git a/test/file.jl b/test/file.jl index f82b2a0fd8f39..005c765e08b90 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1031,7 +1031,7 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER @test_throws Base._UVError("open($(repr(nonexisting_src)), $(Base.JL_O_RDONLY), 0)", Base.UV_ENOENT) cp(nonexisting_src, dst; force=true, follow_symlinks=false) @test_throws Base._UVError("open($(repr(nonexisting_src)), $(Base.JL_O_RDONLY), 0)", Base.UV_ENOENT) cp(nonexisting_src, dst; force=true, follow_symlinks=true) # mv - @test_throws Base._UVError("open($(repr(nonexisting_src)), $(Base.JL_O_RDONLY), 0)", Base.UV_ENOENT) mv(nonexisting_src, dst; force=true) + @test_throws Base._UVError("rename($(repr(nonexisting_src)), $(repr(dst)))", Base.UV_ENOENT) mv(nonexisting_src, dst; force=true) end end diff --git a/test/filesystem.jl b/test/filesystem.jl index 870350dee9f35..036a3dda30cca 100644 --- a/test/filesystem.jl +++ b/test/filesystem.jl @@ -44,7 +44,7 @@ end @testset "Base.Filesystem docstrings" begin undoc = Docs.undocumented_names(Base.Filesystem) @test_broken isempty(undoc) - @test undoc == [:File, :Filesystem, :cptree, :futime, :rename, :sendfile, :unlink] + @test undoc == [:File, :Filesystem, :cptree, :futime, :sendfile, :unlink] end @testset "write return type" begin From e7e8768a77548250d6a06a9fcd35086a0e876ddb Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Thu, 8 Aug 2024 16:44:30 -0700 Subject: [PATCH 043/548] Vendor the terminfo database for use with base/terminfo.jl (#55411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the `terminfo` database to `deps/`, providing a better user experience on systems that don't have `terminfo` on the system by default. The database is built using BinaryBuilder but is not actually platform-specific (it's built for `AnyPlatform`) and as such, this fetches the artifact directly rather than adding a new JLL to stdlib, and it requires no compilation. A build flag, `WITH_TERMINFO`, is added here and assumed true by default, allowing users to set `WITH_TERMINFO=0` in Make.user to avoid bundling `terminfo` should they want to do so. The lookup policy for `terminfo` entries is still compliant with what's described in `terminfo(5)`; the bundled directory is taken to be the first "compiled in" location, i.e. prepended to `@TERMINFO_DIRS@`. This allows any user settings that exist locally, such as custom entries or locations, to take precedence. Fixes #55274 Co-authored-by: Mosè Giordano --- Makefile | 4 +++ NEWS.md | 4 +++ base/terminfo.jl | 11 +++++++- deps/Makefile | 8 +++++- deps/checksums/terminfo | 2 ++ deps/terminfo.mk | 43 ++++++++++++++++++++++++++++++ deps/terminfo.version | 3 +++ stdlib/REPL/test/precompilation.jl | 7 +++-- 8 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 deps/checksums/terminfo create mode 100644 deps/terminfo.mk create mode 100644 deps/terminfo.version diff --git a/Makefile b/Makefile index 023c01aadaa2a..3d8bf5436b476 100644 --- a/Makefile +++ b/Makefile @@ -403,6 +403,10 @@ endif # Install appdata file mkdir -p $(DESTDIR)$(datarootdir)/metainfo/ $(INSTALL_F) $(JULIAHOME)/contrib/julia.appdata.xml $(DESTDIR)$(datarootdir)/metainfo/ + # Install terminal info database +ifneq ($(WITH_TERMINFO),0) + cp -R -L $(build_datarootdir)/terminfo $(DESTDIR)$(datarootdir) +endif # Update RPATH entries and JL_SYSTEM_IMAGE_PATH if $(private_libdir_rel) != $(build_private_libdir_rel) ifneq ($(private_libdir_rel),$(build_private_libdir_rel)) diff --git a/NEWS.md b/NEWS.md index c4e46acca164e..4bbe7645165dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -171,6 +171,10 @@ Deprecated or removed External dependencies --------------------- +- The terminal info database, `terminfo`, is now vendored by default, providing a better + REPL user experience when `terminfo` is not available on the system. Julia can be built + without vendoring the database using the Makefile option `WITH_TERMINFO=0`. ([#55411]) + Tooling Improvements -------------------- diff --git a/base/terminfo.jl b/base/terminfo.jl index 6f1d1ca8015f0..79713f4a86aa3 100644 --- a/base/terminfo.jl +++ b/base/terminfo.jl @@ -245,7 +245,8 @@ end Locate the terminfo file for `term`, return `nothing` if none could be found. The lookup policy is described in `terminfo(5)` "Fetching Compiled -Descriptions". +Descriptions". A terminfo database is included by default with Julia and is +taken to be the first entry of `@TERMINFO_DIRS@`. """ function find_terminfo_file(term::String) isempty(term) && return @@ -261,6 +262,7 @@ function find_terminfo_file(term::String) append!(terminfo_dirs, replace(split(ENV["TERMINFO_DIRS"], ':'), "" => "/usr/share/terminfo")) + push!(terminfo_dirs, normpath(Sys.BINDIR, DATAROOTDIR, "terminfo")) Sys.isunix() && push!(terminfo_dirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") for dir in terminfo_dirs @@ -268,8 +270,15 @@ function find_terminfo_file(term::String) return joinpath(dir, chr, term) elseif isfile(joinpath(dir, chrcode, term)) return joinpath(dir, chrcode, term) + elseif isfile(joinpath(dir, lowercase(chr), lowercase(term))) + # The vendored terminfo database is fully lowercase to avoid issues on + # case-sensitive filesystems. On Unix-like systems, terminfo files with + # different cases are hard links to one another, so this is still + # correct for non-vendored terminfo, just redundant. + return joinpath(dir, lowercase(chr), lowercase(term)) end end + return nothing end """ diff --git a/deps/Makefile b/deps/Makefile index 2f9050f448d67..b87a3e1e58609 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -175,6 +175,10 @@ ifeq ($(WITH_NVTX),1) DEP_LIBS += nvtx endif +ifneq ($(WITH_TERMINFO),0) +DEP_LIBS += terminfo +endif + # Only compile standalone LAPACK if we are not using OpenBLAS. # OpenBLAS otherwise compiles LAPACK as part of its build. # This is useful where one wants to use the vendor BLAS, but @@ -197,7 +201,8 @@ DEP_LIBS_STAGED := $(DEP_LIBS) DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ - sanitizers libsuitesparse lld libtracyclient ittapi nvtx JuliaSyntax + sanitizers libsuitesparse lld libtracyclient ittapi nvtx JuliaSyntax \ + terminfo DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) ifneq ($(USE_BINARYBUILDER_OPENBLAS),0) @@ -259,6 +264,7 @@ include $(SRCDIR)/libgit2.mk include $(SRCDIR)/libwhich.mk include $(SRCDIR)/p7zip.mk include $(SRCDIR)/libtracyclient.mk +include $(SRCDIR)/terminfo.mk # vendored Julia libs include $(SRCDIR)/JuliaSyntax.mk diff --git a/deps/checksums/terminfo b/deps/checksums/terminfo new file mode 100644 index 0000000000000..bd971e72b1be8 --- /dev/null +++ b/deps/checksums/terminfo @@ -0,0 +1,2 @@ +TermInfoDB-v2023.12.9.any.tar.gz/md5/573d9b5adaf6af500e3dfae6e3d15ebf +TermInfoDB-v2023.12.9.any.tar.gz/sha512/e0a5bfe54346f9d5690a840628b329f6fac7375b0d29337bc70813ae3553a72bb397f8034d221c544289e40c4cfc685d5805777b7528f05bbe0123b5905c24a4 diff --git a/deps/terminfo.mk b/deps/terminfo.mk new file mode 100644 index 0000000000000..63194f786f566 --- /dev/null +++ b/deps/terminfo.mk @@ -0,0 +1,43 @@ +## TERMINFO-DB ## +include $(SRCDIR)/terminfo.version + +$(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/JuliaBinaryWrappers/TermInfoDB_jll.jl/releases/download/$(TERMINFO_TAG)/TermInfoDB.v$(TERMINFO_VER).any.tar.gz + touch -c $@ + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz + $(JLCHECKSUM) $< + rm -rf $(dir $@) + mkdir -p $(dir $@) + $(TAR) -C $(dir $@) --strip-components 1 -xf $< + echo 1 > $@ + +checksum-terminfo: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz + $(JLCHECKSUM) $< + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted + echo 1 > $@ + +$(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-checked: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled + echo 1 > $@ + +define TERMINFO_INSTALL + mkdir -p $2/$$(build_datarootdir) + cp -R $1/terminfo $2/$$(build_datarootdir) +endef +$(eval $(call staged-install, \ + terminfo,TermInfoDB-v$(TERMINFO_VER), \ + TERMINFO_INSTALL,,,,)) + +clean-terminfo: + -rm -f $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled + +distclean-terminfo: + rm -rf $(SRCCACHE)/TermInfoDB*.tar.gz $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER) $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER) + +get-terminfo: $(SRCCACHE)/TermInfoDB-v$(TERMINFO_VER).any.tar.gz +extract-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/source-extracted +configure-terminfo: extract-terminfo +compile-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-compiled +fastcheck-terminfo: check-terminfo +check-terminfo: $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-checked diff --git a/deps/terminfo.version b/deps/terminfo.version new file mode 100644 index 0000000000000..b7c020b830517 --- /dev/null +++ b/deps/terminfo.version @@ -0,0 +1,3 @@ +# -*- makefile -*- +TERMINFO_VER := 2023.12.9 +TERMINFO_TAG := TermInfoDB-v$(TERMINFO_VER)+0 diff --git a/stdlib/REPL/test/precompilation.jl b/stdlib/REPL/test/precompilation.jl index 228cbd212a2c1..7efcf0b5e8282 100644 --- a/stdlib/REPL/test/precompilation.jl +++ b/stdlib/REPL/test/precompilation.jl @@ -15,8 +15,11 @@ if !Sys.iswindows() @testset "No interactive startup compilation" begin f, _ = mktemp() - # start an interactive session - cmd = `$(Base.julia_cmd()[1]) --trace-compile=$f -q --startup-file=no -i` + # start an interactive session, ensuring `TERM` is unset since it can trigger + # different amounts of precompilation stemming from `base/terminfo.jl` depending + # on the value, making the test here unreliable + cmd = addenv(`$(Base.julia_cmd()[1]) --trace-compile=$f -q --startup-file=no -i`, + Dict("TERM" => "")) pts, ptm = open_fake_pty() p = run(cmd, pts, pts, pts; wait=false) Base.close_stdio(pts) From ac9558c265b53cb3b3569e37be898df9d91d5ce8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 9 Aug 2024 07:07:31 -0400 Subject: [PATCH 044/548] codegen: move undef freeze before promotion point (#55428) Fixes #55396 --- src/cgutils.cpp | 10 ++++++++-- test/compiler/codegen.jl | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 3695dc1370050..0969f78f10bb4 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3947,8 +3947,6 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else { strct = emit_static_alloca(ctx, lt); setName(ctx.emission_context, strct, arg_typename); - if (nargs < nf) - promotion_point = ctx.builder.CreateStore(ctx.builder.CreateFreeze(UndefValue::get(lt)), strct); if (tracked.count) undef_derived_strct(ctx, strct, sty, ctx.tbaa().tbaa_stack); } @@ -4104,6 +4102,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } } } + if (promotion_point && nargs < nf) { + assert(!init_as_value); + IRBuilderBase::InsertPoint savedIP = ctx.builder.saveIP(); + ctx.builder.SetInsertPoint(promotion_point); + promotion_point = cast(ctx.builder.CreateFreeze(UndefValue::get(lt))); + ctx.builder.CreateStore(promotion_point, strct); + ctx.builder.restoreIP(savedIP); + } if (type_is_ghost(lt)) return mark_julia_const(ctx, sty->instance); else if (init_as_value) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index cd2702ff0e6aa..c74dfbb29d3dd 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -956,3 +956,13 @@ function foonopreds() pkgid.uuid !== nothing ? pkgid.uuid : false end @test foonopreds() !== nothing + +# issue 55396 +struct Incomplete55396 + x::Tuple{Int} + y::Int + @noinline Incomplete55396(x::Int) = new((x,)) +end +let x = Incomplete55396(55396) + @test x.x === (55396,) +end From 2727e36a13431c1e7e7e464b2f0b8af0db149198 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 9 Aug 2024 10:59:51 -0300 Subject: [PATCH 045/548] Remove deprecated non string API for LLVM pass pipeline and parse all options (#55407) This technically removes the option for Oz in julia but it doesn't actually do what one wants. This removes an API currently used by Enzyme.jl and AllocCheck.jl but given that LLVM.jl doesn't support this API anymore that seems fine. @wsmoses @maleadt Do we want the replacement for this (a function that parses the PipelineConfig struct) to live in LLVM.jl or GPUCompiler.jl ? --- src/codegen-stubs.c | 2 -- src/jl_exported_funcs.inc | 1 - src/pipeline.cpp | 67 +++++--------------------------------- test/llvmpasses/parsing.ll | 3 ++ 4 files changed, 11 insertions(+), 62 deletions(-) diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 3e97c149bffe3..41812d903816c 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -107,8 +107,6 @@ JL_DLLEXPORT uint64_t jl_getUnwindInfo_fallback(uint64_t dwAddr) return 0; } -JL_DLLEXPORT void jl_build_newpm_pipeline_fallback(void *MPM, void *PB, void *config) UNAVAILABLE - JL_DLLEXPORT void jl_register_passbuilder_callbacks_fallback(void *PB) { } #define MODULE_PASS(NAME, CLASS, CREATE_PASS) \ diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 246b666f942c1..1976dbe709733 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -542,7 +542,6 @@ YY(jl_type_to_llvm) \ YY(jl_getUnwindInfo) \ YY(jl_get_libllvm) \ - YY(jl_build_newpm_pipeline) \ YY(jl_register_passbuilder_callbacks) \ YY(LLVMExtraMPMAddCPUFeaturesPass) \ YY(LLVMExtraMPMAddRemoveNIPass) \ diff --git a/src/pipeline.cpp b/src/pipeline.cpp index e01645cc1f154..236be179e12c9 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -609,65 +609,6 @@ static void buildPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationL MPM.addPass(AfterOptimizationMarkerPass()); } -struct PipelineConfig { - int Speedup; - int Size; - int lower_intrinsics; - int dump_native; - int external_use; - int llvm_only; - int always_inline; - int enable_early_simplifications; - int enable_early_optimizations; - int enable_scalar_optimizations; - int enable_loop_optimizations; - int enable_vector_pipeline; - int remove_ni; - int cleanup; - int warn_missed_transformations; -}; - -extern "C" JL_DLLEXPORT_CODEGEN void jl_build_newpm_pipeline_impl(void *MPM, void *PB, PipelineConfig* config) JL_NOTSAFEPOINT -{ - OptimizationLevel O; - switch (config->Size) { - case 1: - O = OptimizationLevel::Os; - break; - default: - O = OptimizationLevel::Oz; - break; - case 0: - switch (config->Speedup) { - case 0: - O = OptimizationLevel::O0; - break; - case 1: - O = OptimizationLevel::O1; - break; - case 2: - O = OptimizationLevel::O2; - break; - default: - O = OptimizationLevel::O3; - break; - } - } - buildPipeline(*reinterpret_cast(MPM), reinterpret_cast(PB), O, - OptimizationOptions{!!config->lower_intrinsics, - !!config->dump_native, - !!config->external_use, - !!config->llvm_only, - !!config->always_inline, - !!config->enable_early_simplifications, - !!config->enable_early_optimizations, - !!config->enable_scalar_optimizations, - !!config->enable_loop_optimizations, - !!config->enable_vector_pipeline, - !!config->remove_ni, - !!config->cleanup, - !!config->warn_missed_transformations}); -} #undef JULIA_PASS @@ -865,6 +806,14 @@ static Optional> parseJuliaPip OPTION(dump_native), OPTION(external_use), OPTION(llvm_only), + OPTION(always_inline), + OPTION(enable_early_simplifications), + OPTION(enable_early_optimizations), + OPTION(enable_scalar_optimizations), + OPTION(enable_loop_optimizations), + OPTION(enable_vector_pipeline), + OPTION(remove_ni), + OPTION(cleanup), OPTION(warn_missed_transformations) #undef OPTION }; diff --git a/test/llvmpasses/parsing.ll b/test/llvmpasses/parsing.ll index e75ba292f254a..e0a726176b225 100644 --- a/test/llvmpasses/parsing.ll +++ b/test/llvmpasses/parsing.ll @@ -1,6 +1,9 @@ ; COM: NewPM-only test, tests for ability to parse Julia passes ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null define void @test() { ret void From c3d0d67ac424c889fd7f24552a557c3a7ea8f1e6 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 10 Aug 2024 00:33:43 +0900 Subject: [PATCH 046/548] inference: follow up #49260, remove no longer necessary functions (#55430) --- base/compiler/inferencestate.jl | 66 --------------------------------- 1 file changed, 66 deletions(-) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 06b038ecb6d2e..87647628f772e 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -517,72 +517,6 @@ function compute_trycatch(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothi return handler_info end -function is_throw_call(e::Expr, code::Vector{Any}) - if e.head === :call - f = e.args[1] - if isa(f, SSAValue) - f = code[f.id] - end - if isa(f, GlobalRef) - ff = abstract_eval_globalref_type(f) - if isa(ff, Const) && ff.val === Core.throw - return true - end - end - end - return false -end - -function mark_throw_blocks!(src::CodeInfo, handler_info::Union{Nothing,HandlerInfo}) - for stmt in find_throw_blocks(src.code, handler_info) - src.ssaflags[stmt] |= IR_FLAG_THROW_BLOCK - end - return nothing -end - -# this utility function is incomplete and won't catch every block that always throws, since: -# - it only recognizes direct calls to `throw` within the target code, so it can't mark -# blocks that deterministically call `throw` internally, like those containing `error`. -# - it just does a reverse linear traverse of statements, there's a chance it might miss -# blocks, particularly when there are reverse control edges. -function find_throw_blocks(code::Vector{Any}, handler_info::Union{Nothing,HandlerInfo}) - stmts = BitSet() - n = length(code) - for i in n:-1:1 - s = code[i] - if isa(s, Expr) - if s.head === :gotoifnot - if i+1 in stmts && s.args[2]::Int in stmts - push!(stmts, i) - end - elseif s.head === :return - # see `ReturnNode` handling - elseif is_throw_call(s, code) - if handler_info === nothing || handler_info.handler_at[i][1] == 0 - push!(stmts, i) - end - elseif i+1 in stmts - push!(stmts, i) - end - elseif isa(s, ReturnNode) - # NOTE: it potentially makes sense to treat unreachable nodes - # (where !isdefined(s, :val)) as `throw` points, but that can cause - # worse codegen around the call site (issue #37558) - elseif isa(s, GotoNode) - if s.label in stmts - push!(stmts, i) - end - elseif isa(s, GotoIfNot) - if i+1 in stmts && s.dest in stmts - push!(stmts, i) - end - elseif i+1 in stmts - push!(stmts, i) - end - end - return stmts -end - # check if coverage mode is enabled function should_insert_coverage(mod::Module, debuginfo::DebugInfo) coverage_enabled(mod) && return true From 18340a3eb40758f5c21428c9c03b3f2b696f475d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 9 Aug 2024 19:47:20 +0200 Subject: [PATCH 047/548] `stale_cachefile`: handle if the expected cache file is missing (#55419) Part of fixing https://github.com/JuliaLang/Pkg.jl/issues/3984 --- base/loading.jl | 8 +++++++- test/precompile.jl | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index eb467d9cc0bd4..4dc735f0099d8 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3666,7 +3666,13 @@ end ignore_loaded::Bool=false, requested_flags::CacheFlags=CacheFlags(), reasons::Union{Dict{String,Int},Nothing}=nothing, stalecheck::Bool=true) # n.b.: this function does nearly all of the file validation, not just those checks related to stale, so the name is potentially unclear - io = open(cachefile, "r") + io = try + open(cachefile, "r") + catch ex + ex isa IOError || ex isa SystemError || rethrow() + @debug "Rejecting cache file $cachefile for $modkey because it could not be opened" isfile(cachefile) + return true + end try checksum = isvalid_cache_header(io) if iszero(checksum) diff --git a/test/precompile.jl b/test/precompile.jl index 3241ee8b25a35..f45eb4bb1e79e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -597,6 +597,10 @@ precompile_test_harness(false) do dir @test Base.invokelatest(Baz.baz) === 1 @test Baz === UseBaz.Baz + # should not throw if the cachefile does not exist + @test !isfile("DoesNotExist.ji") + @test Base.stale_cachefile("", "DoesNotExist.ji") === true + # Issue #12720 FooBar1_file = joinpath(dir, "FooBar1.jl") write(FooBar1_file, From 7ec39e71361c835c9ff4e659d0a23f4892f8249f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 9 Aug 2024 19:50:58 +0200 Subject: [PATCH 048/548] fix swallowing internal errors in precompilepkgs (#55432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing: - with a package error ``` (SimpleLooper) pkg> precompile Precompiling all packages... ✗ SimpleLooper 0 dependencies successfully precompiled in 2 seconds ERROR: The following 1 direct dependency failed to precompile: SimpleLooper Failed to precompile SimpleLooper [ff33fe5b-d8e3-4cbd-8bd9-3d2408ff8cab] to "/Users/ian/.julia/compiled/v1.12/SimpleLooper/jl_PQArnH". ERROR: LoadError: Stacktrace: [1] error() @ Base ./error.jl:53 ``` - with interrupt ``` (SimpleLooper) pkg> precompile Precompiling all packages... ^C Interrupted: Exiting precompilation... ◒ SimpleLooper 1 dependency had output during precompilation: ┌ SimpleLooper │ [57879] signal 2: Interrupt: 2 │ in expression starting at /Users/ian/Documents/GitHub/SimpleLooper.jl/src/SimpleLooper.jl:2 └ ``` - an internal error simulated in the same scope that https://github.com/JuliaLang/Pkg.jl/issues/3984 was failing to throw from ``` JULIA stdlib/release.image Unhandled Task ERROR: Stacktrace: [1] error() @ Base ./error.jl:53 [2] (::Base.Precompilation.var"#27#65"{Bool, Bool, Vector{Task}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, String}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, String}, Base.Event, Base.Event, ReentrantLock, Vector{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, String}, Vector{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}}, Int64, Vector{Base.PkgId}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, Bool}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, Base.Event}, Dict{Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}, Bool}, Vector{Base.PkgId}, Dict{Base.PkgId, String}, Dict{Tuple{Base.PkgId, UInt128, String, String}, Bool}, Base.Precompilation.var"#color_string#38"{Bool}, Bool, Base.Semaphore, Bool, String, Vector{String}, Vector{Base.PkgId}, Base.PkgId, Base.CacheFlags, Cmd, Pair{Cmd, Base.CacheFlags}, Tuple{Base.PkgId, Pair{Cmd, Base.CacheFlags}}})() @ Base.Precompilation ./precompilation.jl:819 ``` --- base/precompilation.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/precompilation.jl b/base/precompilation.jl index dfaf671a63534..feee394588b19 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -897,6 +897,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; length(tasks) == 1 && notify(interrupted_or_done) end end + Base.errormonitor(task) # interrupts are handled separately so ok to watch for other errors like this push!(tasks, task) end end From 86231ce5763a41a6661d7834a28ad1c37526044a Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 9 Aug 2024 19:20:42 +0000 Subject: [PATCH 049/548] LinearAlgebra: round-trippable 2-argument show for `Tridiagonal`/`SymTridiagonal` (#55415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the displayed form of a `Tridiaognal` and a `SymTridiagonal` valid constructors. ```julia julia> T = Tridiagonal(1:3, 1:4, 1:3) 4×4 Tridiagonal{Int64, UnitRange{Int64}}: 1 1 ⋅ ⋅ 1 2 2 ⋅ ⋅ 2 3 3 ⋅ ⋅ 3 4 julia> show(T) Tridiagonal(1:3, 1:4, 1:3) julia> S = SymTridiagonal(1:4, 1:3) 4×4 SymTridiagonal{Int64, UnitRange{Int64}}: 1 1 ⋅ ⋅ 1 2 2 ⋅ ⋅ 2 3 3 ⋅ ⋅ 3 4 julia> show(S) SymTridiagonal(1:4, 1:3) ``` Displaying the bands has several advantages: firstly, it's briefer than printing the full array, and secondly, it displays the special structure in the bands, if any. E.g.: ```julia julia> T = Tridiagonal(spzeros(3), spzeros(4), spzeros(3)); julia> show(T) Tridiagonal(sparsevec(Int64[], Float64[], 3), sparsevec(Int64[], Float64[], 4), sparsevec(Int64[], Float64[], 3)) ``` It's clear from the displayed form that `T` has sparse bands. A special handling for `SymTridiagonal` matrices is necessary, as the diagonal band is symmetrized. This means: ```julia julia> using StaticArrays julia> m = SMatrix{2,2}(1:4); julia> S = SymTridiagonal(fill(m,3), fill(m,2)) 3×3 SymTridiagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [1 3; 3 4] [1 3; 2 4] ⋅ [1 2; 3 4] [1 3; 3 4] [1 3; 2 4] ⋅ [1 2; 3 4] [1 3; 3 4] julia> show(S) SymTridiagonal(SMatrix{2, 2, Int64, 4}[[1 3; 3 4], [1 3; 3 4], [1 3; 3 4]], SMatrix{2, 2, Int64, 4}[[1 3; 2 4], [1 3; 2 4]]) ``` The displayed values correspond to the symmetrized band, and not the actual input arguments. I think displaying the symmetrized elements makes more sense here, as this matches the form in the 3-argument `show`. --- stdlib/LinearAlgebra/src/tridiag.jl | 18 ++++++++++++++++++ stdlib/LinearAlgebra/test/tridiag.jl | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index c14ed5690198c..0ba03634d82ad 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -1045,3 +1045,21 @@ function _copyto_banded!(A::SymTridiagonal, B::Tridiagonal) _evview(A) .= B.du return A end + +# display +function show(io::IO, T::Tridiagonal) + print(io, "Tridiagonal(") + show(io, T.dl) + print(io, ", ") + show(io, T.d) + print(io, ", ") + show(io, T.du) + print(io, ")") +end +function show(io::IO, S::SymTridiagonal) + print(io, "SymTridiagonal(") + show(io, eltype(S) <: Number ? S.dv : view(S, diagind(S, IndexStyle(S)))) + print(io, ", ") + show(io, S.ev) + print(io, ")") +end diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 41a28631b27a0..e0a8e32d77852 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -18,6 +18,9 @@ using .Main.FillArrays isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) using .Main.OffsetArrays +isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) +using .Main.SizedArrays + include("testutils.jl") # test_approx_eq_modphase #Test equivalence of eigenvectors/singular vectors taking into account possible phase (sign) differences @@ -914,4 +917,17 @@ end end end +@testset "show" begin + T = Tridiagonal(1:3, 1:4, 1:3) + @test sprint(show, T) == "Tridiagonal(1:3, 1:4, 1:3)" + S = SymTridiagonal(1:4, 1:3) + @test sprint(show, S) == "SymTridiagonal(1:4, 1:3)" + + m = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) + T = Tridiagonal(fill(m,2), fill(m,3), fill(m,2)) + @test sprint(show, T) == "Tridiagonal($(repr(diag(T,-1))), $(repr(diag(T))), $(repr(diag(T,1))))" + S = SymTridiagonal(fill(m,3), fill(m,2)) + @test sprint(show, S) == "SymTridiagonal($(repr(diag(S))), $(repr(diag(S,1))))" +end + end # module TestTridiagonal From 7e809b0953cd80ce0fa3078dea20e848bc0d7271 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 10 Aug 2024 00:31:37 -0400 Subject: [PATCH 050/548] compiler: apply more accurate effects to return_type_tfunc (#55338) In extreme cases, the compiler could mark this function for concrete-eval, even though that is illegal unless the compiler has first deleted this instruction. Otherwise the attempt to concrete-eval will re-run the function repeatedly until it hits a StackOverflow. Workaround to fix #55147 @aviatesk You might know how to solve this even better, using post-optimization effect refinements? Since we should actually only apply the refinement of terminates=false => terminates=true (and thus allowing concrete eval) if the optimization occurs, and not just in inference thinks the optimization would be legal. --------- Co-authored-by: Shuhei Kadowaki --- base/boot.jl | 3 +- base/cmd.jl | 2 +- base/compiler/abstractinterpretation.jl | 7 ++-- base/compiler/compiler.jl | 11 +++-- base/compiler/effects.jl | 55 ++++++++++++++++++------- base/compiler/optimize.jl | 30 ++++++++++---- base/compiler/ssair/show.jl | 2 + base/compiler/tfuncs.jl | 20 +++++---- base/compiler/typeinfer.jl | 3 ++ base/essentials.jl | 33 ++++++++++----- base/expr.jl | 25 +++++++++-- src/julia.h | 5 ++- src/method.c | 2 + 13 files changed, 141 insertions(+), 57 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index f7aee9677e2f2..608e273d4b514 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -284,7 +284,8 @@ macro _foldable_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end macro inline() Expr(:meta, :inline) end diff --git a/base/cmd.jl b/base/cmd.jl index 202527abdf644..84ec52f865e98 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -482,7 +482,7 @@ function cmd_gen(parsed) end end -@assume_effects :effect_free :terminates_globally :noub function cmd_gen( +@assume_effects :foldable !:consistent function cmd_gen( parsed::Tuple{Vararg{Tuple{Vararg{Union{String, SubString{String}}}}}} ) return @invoke cmd_gen(parsed::Any) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 789b7e6f5a962..90d395600bbde 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -980,7 +980,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter, end end mi = result.edge - if mi !== nothing && is_foldable(effects) + if mi !== nothing && is_foldable(effects, #=check_rtcall=#true) if f !== nothing && is_all_const_arg(arginfo, #=start=#2) if (is_nonoverlayed(interp) || is_nonoverlayed(effects) || # Even if overlay methods are involved, when `:consistent_overlay` is @@ -2910,8 +2910,9 @@ 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 : - effects.noub) + (override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS : + effects.noub, + nortcall = override.nortcall ? true : effects.nortcall) end isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 629641308a217..5cc01391267d7 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -49,10 +49,11 @@ struct EffectsOverride noub::Bool noub_if_noinbounds::Bool consistent_overlay::Bool + nortcall::Bool end function EffectsOverride( override::EffectsOverride = - EffectsOverride(false, false, false, false, false, false, false, false, false, false); + EffectsOverride(false, false, false, false, false, false, false, false, false, false, false); consistent::Bool = override.consistent, effect_free::Bool = override.effect_free, nothrow::Bool = override.nothrow, @@ -62,7 +63,8 @@ function EffectsOverride( inaccessiblememonly::Bool = override.inaccessiblememonly, noub::Bool = override.noub, noub_if_noinbounds::Bool = override.noub_if_noinbounds, - consistent_overlay::Bool = override.consistent_overlay) + consistent_overlay::Bool = override.consistent_overlay, + nortcall::Bool = override.nortcall) return EffectsOverride( consistent, effect_free, @@ -73,9 +75,10 @@ function EffectsOverride( inaccessiblememonly, noub, noub_if_noinbounds, - consistent_overlay) + consistent_overlay, + nortcall) end -const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h +const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h # essential files and libraries include("essentials.jl") diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 0375b8dba922c..166df78f3130c 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -58,6 +58,9 @@ following meanings: 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. +- `nortcall::Bool`: this method does not call `Core.Compiler.return_type`, + and it is guaranteed that any other methods this method might call also do not call + `Core.Compiler.return_type`. 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 @@ -103,6 +106,9 @@ The output represents the state of different effect properties in the following - `+o` (green): `ALWAYS_TRUE` - `-o` (red): `ALWAYS_FALSE` - `?o` (yellow): `CONSISTENT_OVERLAY` +9. `:nortcall` (`r`): + - `+r` (green): `true` + - `-r` (red): `false` """ struct Effects consistent::UInt8 @@ -113,6 +119,7 @@ struct Effects inaccessiblememonly::UInt8 noub::UInt8 nonoverlayed::UInt8 + nortcall::Bool function Effects( consistent::UInt8, effect_free::UInt8, @@ -121,7 +128,8 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, noub::UInt8, - nonoverlayed::UInt8) + nonoverlayed::UInt8, + nortcall::Bool) return new( consistent, effect_free, @@ -130,7 +138,8 @@ struct Effects notaskstate, inaccessiblememonly, noub, - nonoverlayed) + nonoverlayed, + nortcall) end end @@ -160,10 +169,10 @@ const NOUB_IF_NOINBOUNDS = 0x01 << 1 # :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 +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # 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, false) # unknown really function Effects(effects::Effects = _EFFECTS_UNKNOWN; consistent::UInt8 = effects.consistent, @@ -173,7 +182,8 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate::Bool = effects.notaskstate, inaccessiblememonly::UInt8 = effects.inaccessiblememonly, noub::UInt8 = effects.noub, - nonoverlayed::UInt8 = effects.nonoverlayed) + nonoverlayed::UInt8 = effects.nonoverlayed, + nortcall::Bool = effects.nortcall) return Effects( consistent, effect_free, @@ -182,7 +192,8 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate, inaccessiblememonly, noub, - nonoverlayed) + nonoverlayed, + nortcall) end function is_better_effects(new::Effects, old::Effects) @@ -247,6 +258,11 @@ function is_better_effects(new::Effects, old::Effects) elseif new.nonoverlayed != old.nonoverlayed return false end + if new.nortcall + any_improved |= !old.nortcall + elseif new.nortcall != old.nortcall + return false + end return any_improved end @@ -259,7 +275,8 @@ function merge_effects(old::Effects, new::Effects) merge_effectbits(old.notaskstate, new.notaskstate), merge_effectbits(old.inaccessiblememonly, new.inaccessiblememonly), merge_effectbits(old.noub, new.noub), - merge_effectbits(old.nonoverlayed, new.nonoverlayed)) + merge_effectbits(old.nonoverlayed, new.nonoverlayed), + merge_effectbits(old.nortcall, new.nortcall)) end function merge_effectbits(old::UInt8, new::UInt8) @@ -279,16 +296,18 @@ is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAY 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 === ALWAYS_TRUE +is_nortcall(effects::Effects) = effects.nortcall # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here -is_foldable(effects::Effects) = +is_foldable(effects::Effects, check_rtcall::Bool=false) = is_consistent(effects) && (is_noub(effects) || is_noub_if_noinbounds(effects)) && is_effect_free(effects) && - is_terminates(effects) + is_terminates(effects) && + (!check_rtcall || is_nortcall(effects)) -is_foldable_nothrow(effects::Effects) = - is_foldable(effects) && +is_foldable_nothrow(effects::Effects, check_rtcall::Bool=false) = + is_foldable(effects, check_rtcall) && is_nothrow(effects) # TODO add `is_noub` here? @@ -318,7 +337,8 @@ function encode_effects(e::Effects) ((e.notaskstate % UInt32) << 7) | ((e.inaccessiblememonly % UInt32) << 8) | ((e.noub % UInt32) << 10) | - ((e.nonoverlayed % UInt32) << 12) + ((e.nonoverlayed % UInt32) << 12) | + ((e.nortcall % UInt32) << 14) end function decode_effects(e::UInt32) @@ -330,7 +350,8 @@ function decode_effects(e::UInt32) _Bool((e >> 7) & 0x01), UInt8((e >> 8) & 0x03), UInt8((e >> 10) & 0x03), - UInt8((e >> 12) & 0x03)) + UInt8((e >> 12) & 0x03), + _Bool((e >> 14) & 0x01)) end function encode_effects_override(eo::EffectsOverride) @@ -345,6 +366,7 @@ function encode_effects_override(eo::EffectsOverride) eo.noub && (e |= (0x0001 << 7)) eo.noub_if_noinbounds && (e |= (0x0001 << 8)) eo.consistent_overlay && (e |= (0x0001 << 9)) + eo.nortcall && (e |= (0x0001 << 10)) return e end @@ -359,7 +381,8 @@ function decode_effects_override(e::UInt16) !iszero(e & (0x0001 << 6)), !iszero(e & (0x0001 << 7)), !iszero(e & (0x0001 << 8)), - !iszero(e & (0x0001 << 9))) + !iszero(e & (0x0001 << 9)), + !iszero(e & (0x0001 << 10))) end decode_statement_effects_override(ssaflag::UInt32) = diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 9c89e8596d237..936b604d373a0 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -42,13 +42,16 @@ const IR_FLAG_NOUB = one(UInt32) << 8 const IR_FLAG_EFIIMO = one(UInt32) << 9 # This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 10 +# This statement is :nortcall +const IR_FLAG_NORTCALL = one(UInt32) << 11 # This statement has no users and may be deleted if flags get refined to IR_FLAGS_REMOVABLE -const IR_FLAG_UNUSED = one(UInt32) << 11 +const IR_FLAG_UNUSED = one(UInt32) << 12 -const NUM_IR_FLAGS = 12 # sync with julia.h +const NUM_IR_FLAGS = 13 # sync with julia.h const IR_FLAGS_EFFECTS = - IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_TERMINATES | IR_FLAG_NOUB + IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | + IR_FLAG_TERMINATES | IR_FLAG_NOUB | IR_FLAG_NORTCALL const IR_FLAGS_REMOVABLE = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_TERMINATES @@ -78,6 +81,9 @@ function flags_for_effects(effects::Effects) if is_noub(effects) flags |= IR_FLAG_NOUB end + if is_nortcall(effects) + flags |= IR_FLAG_NORTCALL + end return flags end @@ -583,26 +589,28 @@ mutable struct PostOptAnalysisState all_nothrow::Bool all_noub::Bool any_conditional_ub::Bool + nortcall::Bool function PostOptAnalysisState(result::InferenceResult, ir::IRCode) inconsistent = BitSetBoundedMinPrioritySet(length(ir.stmts)) tpdum = TwoPhaseDefUseMap(length(ir.stmts)) lazypostdomtree = LazyPostDomtree(ir) lazyagdomtree = LazyAugmentedDomtree(ir) return new(result, ir, inconsistent, tpdum, lazypostdomtree, lazyagdomtree, Int[], - true, true, nothing, true, true, false) + true, true, nothing, true, true, false, true) end end give_up_refinements!(sv::PostOptAnalysisState) = sv.all_retpaths_consistent = sv.all_effect_free = sv.effect_free_if_argmem_only = - sv.all_nothrow = sv.all_noub = false + sv.all_nothrow = sv.all_noub = sv.nortcall = false function any_refinable(sv::PostOptAnalysisState) effects = sv.result.ipo_effects return ((!is_consistent(effects) & sv.all_retpaths_consistent) | (!is_effect_free(effects) & sv.all_effect_free) | (!is_nothrow(effects) & sv.all_nothrow) | - (!is_noub(effects) & sv.all_noub)) + (!is_noub(effects) & sv.all_noub) | + (!is_nortcall(effects) & sv.nortcall)) end struct GetNativeEscapeCache{CodeCache} @@ -647,7 +655,8 @@ function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState) effect_free = sv.all_effect_free ? ALWAYS_TRUE : sv.effect_free_if_argmem_only === true ? EFFECT_FREE_IF_INACCESSIBLEMEMONLY : effects.effect_free, nothrow = sv.all_nothrow ? true : effects.nothrow, - noub = sv.all_noub ? (sv.any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub) + noub = sv.all_noub ? (sv.any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub, + nortcall = sv.nortcall ? true : effects.nortcall) return true end @@ -772,6 +781,13 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) sv.all_noub = false end end + if !has_flag(flag, IR_FLAG_NORTCALL) + # if a function call that might invoke `Core.Compiler.return_type` has been deleted, + # there's no need to taint with `:nortcall`, allowing concrete evaluation + if isexpr(stmt, :call) || isexpr(stmt, :invoke) + sv.nortcall = false + end + end end function scan_inconsistency!(inst::Instruction, sv::PostOptAnalysisState) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 82ca6e364f2fa..7d936a1688aba 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -1050,6 +1050,8 @@ function Base.show(io::IO, e::Effects) 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, ',') + printstyled(io, effectbits_letter(e, :nortcall, 'r'); color=effectbits_color(e, :nortcall)) print(io, ')') end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index b40f65ab3ca1d..9a4c761b4209b 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2871,7 +2871,7 @@ end # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, # while this assumes that it is an absolutely precise and accurate and exact model of both function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) - UNKNOWN = CallMeta(Type, Any, EFFECTS_THROWS, NoCallInfo()) + UNKNOWN = CallMeta(Type, Any, Effects(EFFECTS_THROWS; nortcall=false), NoCallInfo()) if !(2 <= length(argtypes) <= 3) return UNKNOWN end @@ -2899,8 +2899,12 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s return UNKNOWN end + # effects are not an issue if we know this statement will get removed, but if it does not get removed, + # then this could be recursively re-entering inference (via concrete-eval), which will not terminate + RT_CALL_EFFECTS = Effects(EFFECTS_TOTAL; nortcall=false) + if contains_is(argtypes_vec, Union{}) - return CallMeta(Const(Union{}), Union{}, EFFECTS_TOTAL, NoCallInfo()) + return CallMeta(Const(Union{}), Union{}, RT_CALL_EFFECTS, NoCallInfo()) end # Run the abstract_call without restricting abstract call @@ -2918,25 +2922,25 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s rt = widenslotwrapper(call.rt) if isa(rt, Const) # output was computed to be constant - return CallMeta(Const(typeof(rt.val)), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(typeof(rt.val)), Union{}, RT_CALL_EFFECTS, info) end rt = widenconst(rt) if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) # output cannot be improved so it is known for certain - return CallMeta(Const(rt), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) # conservatively express uncertainty of this result # in two ways: both as being a subtype of this, and # because of LimitedAccuracy causes - return CallMeta(Type{<:rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) elseif isa(tt, Const) || isconstType(tt) # input arguments were known for certain # XXX: this doesn't imply we know anything about rt - return CallMeta(Const(rt), Union{}, EFFECTS_TOTAL, info) + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) elseif isType(rt) - return CallMeta(Type{rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{rt}, Union{}, RT_CALL_EFFECTS, info) else - return CallMeta(Type{<:rt}, Union{}, EFFECTS_TOTAL, info) + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) end end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 85bdd881042dc..41fb774266f25 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -449,6 +449,9 @@ function adjust_effects(ipo_effects::Effects, def::Method) if is_effect_overridden(override, :consistent_overlay) ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY) end + if is_effect_overridden(override, :nortcall) + ipo_effects = Effects(ipo_effects; nortcall=true) + end return ipo_effects end diff --git a/base/essentials.jl b/base/essentials.jl index 50017b3d7927d..32c44a9571f23 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -202,7 +202,8 @@ macro _total_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -216,7 +217,8 @@ macro _foldable_meta() #=:inaccessiblememonly=#true, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#true)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -230,7 +232,8 @@ macro _terminates_locally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping) macro _terminates_globally_meta() @@ -244,7 +247,8 @@ macro _terminates_globally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping) macro _terminates_globally_notaskstate_meta() @@ -258,7 +262,8 @@ macro _terminates_globally_notaskstate_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping) macro _terminates_globally_noub_meta() @@ -272,7 +277,8 @@ macro _terminates_globally_noub_meta() #=:inaccessiblememonly=#false, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#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() @@ -286,7 +292,8 @@ macro _effect_free_terminates_locally_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping) macro _nothrow_noub_meta() @@ -300,7 +307,8 @@ macro _nothrow_noub_meta() #=:inaccessiblememonly=#false, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() @@ -314,7 +322,8 @@ macro _nothrow_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _noub_meta() @@ -342,7 +351,8 @@ macro _notaskstate_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping) macro _noub_if_noinbounds_meta() @@ -356,7 +366,8 @@ macro _noub_if_noinbounds_meta() #=:inaccessiblememonly=#false, #=:noub=#false, #=:noub_if_noinbounds=#true, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # another version of inlining that propagates an inbounds context diff --git a/base/expr.jl b/base/expr.jl index 7d723c3f940d7..c4f64b89de8b6 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -505,6 +505,7 @@ The following `setting`s are supported. - `:inaccessiblememonly` - `:noub` - `:noub_if_noinbounds` +- `:nortcall` - `:foldable` - `:removable` - `:total` @@ -673,6 +674,20 @@ The `:noub` setting asserts that the method will not execute any undefined behav 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. +--- +## `:nortcall` + +The `:nortcall` setting asserts that the method does not call `Core.Compiler.return_type`, +and that any other methods this method might call also do not call `Core.Compiler.return_type`. + +!!! note + To be precise, this assertion can be used when a call to `Core.Compiler.return_type` is + not made at runtime; that is, when the result of `Core.Compiler.return_type` is known + exactly at compile time and the call is eliminated by the optimizer. However, since + whether the result of `Core.Compiler.return_type` is folded at compile time depends + heavily on the compiler's implementation, it is generally risky to assert this if + the method in question uses `Core.Compiler.return_type` in any form. + --- ## `:foldable` @@ -683,6 +698,7 @@ currently equivalent to the following `setting`s: - `:effect_free` - `:terminates_globally` - `:noub` +- `:nortcall` !!! note This list in particular does not include `:nothrow`. The compiler will still @@ -716,6 +732,7 @@ the following other `setting`s: - `:notaskstate` - `:inaccessiblememonly` - `:noub` +- `:nortcall` !!! warning `:total` is a very strong assertion and will likely gain additional semantics @@ -794,17 +811,17 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin elseif setting === :noub_if_noinbounds return EffectsOverride(override; noub_if_noinbounds = val) elseif setting === :foldable - consistent = effect_free = terminates_globally = noub = val - return EffectsOverride(override; consistent, effect_free, terminates_globally, noub) + consistent = effect_free = terminates_globally = noub = nortcall = val + return EffectsOverride(override; consistent, effect_free, terminates_globally, noub, nortcall) elseif setting === :removable effect_free = nothrow = terminates_globally = val return EffectsOverride(override; effect_free, nothrow, terminates_globally) elseif setting === :total consistent = effect_free = nothrow = terminates_globally = notaskstate = - inaccessiblememonly = noub = val + inaccessiblememonly = noub = nortcall = val return EffectsOverride(override; consistent, effect_free, nothrow, terminates_globally, notaskstate, - inaccessiblememonly, noub) + inaccessiblememonly, noub, nortcall) end return nothing end diff --git a/src/julia.h b/src/julia.h index 2054a434577e7..e211f31c6512c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -271,12 +271,13 @@ typedef union __jl_purity_overrides_t { uint16_t ipo_noub : 1; uint16_t ipo_noub_if_noinbounds : 1; uint16_t ipo_consistent_overlay : 1; + uint16_t ipo_nortcall : 1; } overrides; uint16_t bits; } _jl_purity_overrides_t; -#define NUM_EFFECTS_OVERRIDES 10 -#define NUM_IR_FLAGS 12 +#define NUM_EFFECTS_OVERRIDES 11 +#define NUM_IR_FLAGS 13 // This type describes a single function body typedef struct _jl_code_info_t { diff --git a/src/method.c b/src/method.c index 549575286bc7e..d890489c390f9 100644 --- a/src/method.c +++ b/src/method.c @@ -491,6 +491,8 @@ jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ir) 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; + int8_t nortcall = jl_unbox_bool(jl_exprarg(ma, 10)); + if (nortcall) li->purity.overrides.ipo_nortcall = nortcall; } } else From 2e1235ea6523aac0ef45ac2a4e56eb73ecebd82d Mon Sep 17 00:00:00 2001 From: Haakon Ludvig Langeland Ervik <45243236+haakon-e@users.noreply.github.com> Date: Sat, 10 Aug 2024 15:35:20 -0700 Subject: [PATCH 051/548] Update asyncmap docs to clarify order of outputs (#54974) --- base/asyncmap.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/asyncmap.jl b/base/asyncmap.jl index c81afbb7e9115..02e515d2e0c6c 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -9,6 +9,8 @@ Uses multiple concurrent tasks to map `f` over a collection (or multiple equal length collections). For multiple collection arguments, `f` is applied elementwise. +The output is guaranteed to be the same order as the elements of the collection(s) `c`. + `ntasks` specifies the number of tasks to run concurrently. Depending on the length of the collections, if `ntasks` is unspecified, up to 100 tasks will be used for concurrent mapping. From d3022722fe19ae746f45d4c891ec3996056d02e3 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 11 Aug 2024 16:13:56 +0200 Subject: [PATCH 052/548] precompilepkgs: Handle when the terminal is very short (#55445) Fixes https://github.com/JuliaLang/Pkg.jl/issues/3935 1.10 counterpart https://github.com/JuliaLang/Pkg.jl/pull/3988 --- base/precompilation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index feee394588b19..aa70718eab9bc 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -710,7 +710,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; while !printloop_should_exit lock(print_lock) do term_size = Base.displaysize(io)::Tuple{Int,Int} - num_deps_show = term_size[1] - 3 + num_deps_show = max(term_size[1] - 3, 2) # show at least 2 deps pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) else From a3859ed209006aa83c143e169e664b33bbc6414c Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 11 Aug 2024 21:11:26 -0400 Subject: [PATCH 053/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?JuliaSyntaxHighlighting=20stdlib=20from=20a463611=20to=2004b232?= =?UTF-8?q?3=20(#55464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: JuliaSyntaxHighlighting URL: https://github.com/julialang/JuliaSyntaxHighlighting.jl.git Stdlib branch: main Julia branch: master Old commit: a463611 New commit: 04b2323 Julia version: 1.12.0-DEV JuliaSyntaxHighlighting version: 1.12.0 Bump invoked by: @tecosaur Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/julialang/JuliaSyntaxHighlighting.jl/compare/a463611e715c9ec546ac8463c38b6890d892e0c8...04b2323c41f6422464c838fe9045700e9ee75e95 ``` $ git log --oneline a463611..04b2323 04b2323 Support a syntax_errors keyword argument 3fba08b Use concrete refs in paren state struct ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/JuliaSyntaxHighlighting.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/sha512 diff --git a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 new file mode 100644 index 0000000000000..518e2705544ed --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 @@ -0,0 +1 @@ +956fe26df1daca727ec15bfbc175584f diff --git a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 new file mode 100644 index 0000000000000..786dd666f2927 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 @@ -0,0 +1 @@ +20c7990134634dd252909dfa2c43f7b77d427a77f1b726eefdc47781fc3ad46152e81e612d4091541ffb32323154cb5a696157cd24d7a71087d5883720e03728 diff --git a/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/md5 deleted file mode 100644 index 47635275f0364..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5f8e876204d20c02b8139e61c78caf44 diff --git a/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/sha512 deleted file mode 100644 index d353acb42a5ce..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-a463611e715c9ec546ac8463c38b6890d892e0c8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e701477f0f7210854603e9d10758e2ecd47c1afb0f4ae6eca07cd64490cef74bbea5f3792f40b754dabbeea03bda2df07072c635b63e5f8a3f7ebb6f3f03fdf0 diff --git a/stdlib/JuliaSyntaxHighlighting.version b/stdlib/JuliaSyntaxHighlighting.version index 705f16c785a39..b076cfa26b5aa 100644 --- a/stdlib/JuliaSyntaxHighlighting.version +++ b/stdlib/JuliaSyntaxHighlighting.version @@ -1,4 +1,4 @@ JULIASYNTAXHIGHLIGHTING_BRANCH = main -JULIASYNTAXHIGHLIGHTING_SHA1 = a463611e715c9ec546ac8463c38b6890d892e0c8 +JULIASYNTAXHIGHLIGHTING_SHA1 = 04b2323c41f6422464c838fe9045700e9ee75e95 JULIASYNTAXHIGHLIGHTING_GIT_URL := https://github.com/julialang/JuliaSyntaxHighlighting.jl.git JULIASYNTAXHIGHLIGHTING_TAR_URL = https://api.github.com/repos/julialang/JuliaSyntaxHighlighting.jl/tarball/$1 From 9d222b87d77d8a76f806fb296e33916dde8c9411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Mon, 12 Aug 2024 03:13:05 +0200 Subject: [PATCH 054/548] [OpenBLAS_jll] Upgrade to v0.3.28 (#55462) Memo to self: * update version number in `stdlib/OpenBLAS_jll/Project.toml` * update version number and sha in `deps/openblas.version` * refresh checksums with `make -f contrib/refresh_checksums.mk -j openblas` See the [release notes of v0.3.28](https://github.com/OpenMathLib/OpenBLAS/releases/tag/v0.3.28). --- deps/checksums/openblas | 188 +++++++++++++++---------------- deps/openblas.version | 6 +- stdlib/OpenBLAS_jll/Project.toml | 2 +- 3 files changed, 98 insertions(+), 98 deletions(-) diff --git a/deps/checksums/openblas b/deps/checksums/openblas index ad6b38dc075fa..51317261c82a0 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,94 @@ -OpenBLAS.v0.3.27+1.aarch64-apple-darwin-libgfortran5.tar.gz/md5/7bb5c7a169ec7660ec38fe73c74a89d7 -OpenBLAS.v0.3.27+1.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/97266fa0d786bac50f37d82e66da645dfa1b811975045d4aaad1f49361caf7945c06203cb728bf92e9071ec805dff2c75f2b45b346ae4f9cfe289d8f2215e68b -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran3.tar.gz/md5/ea42c557a49aa58172ea0e0f0f93c628 -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/baade18c9d8d91f3fb32e44609277a7a6cd827a6c9554e5b21f88d492a0c34e93d29041f691f6b0cd03ab609d5470b1a06e95121781e9622cce301812d6613de -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran4.tar.gz/md5/85a9cbbbf9fff65927a9ff96f17d0792 -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/7a0024c509a50c87c9318d209465e0d57fc2e0a8401740666f09d236678eb9d5a1b2fbbfd12c0c409006607a408f03f11c1465841417533010a7843c4af654c1 -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran5.tar.gz/md5/4e3c6a68a61b9749ebb55b20728bf0f1 -OpenBLAS.v0.3.27+1.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/dbf9fc5465f60a35849c391069c0a9d6d6fc8685b734d00088e297cf7a6c92fbed67f4264f2b2c164d3c6694d9c8f64b750faa248aa1fd44867d18a94211dc87 -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran3.tar.gz/md5/c25b607a4df84f9aeb112f24520cabb3 -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran3.tar.gz/sha512/a99fa75a3cfea19c84a4d455585e53f124e956dd5d4ee7ce0c38c0922b0bebb8b2c996079c3bc63e95444b531ddf9d1f003a22d7f6b55cf99db2334bb1c618ae -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran4.tar.gz/md5/3473d20c26f6ad50f3a0b635415858a5 -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran4.tar.gz/sha512/6e9100e0fcbe1b91c5a4461118739af9d4eca7edd7b8e6ee07a2052c0aaad0ea84c048f0e507ff88da81f47b10c102faf9fe735d13ae1cd35f44396d9a51a864 -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran5.tar.gz/md5/9ad49254a2827987e622a58a1b8c7b98 -OpenBLAS.v0.3.27+1.aarch64-linux-musl-libgfortran5.tar.gz/sha512/f8a3b9aa52920ce76f5d9550407aeefed5e2596d05b9f8f0643e1da221cf533a09de7a0454a04a2d59a3a2a2fb899a538a5e03b133746415a81415ff926826ba -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/c130634237846672f3a672f1d0e346d9 -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/c174d00870ce3944c553122606cba7b78312342a02dc4833a91ae105f05d85d06e665e86a79452bdb7d2b31c18936582d79427ec3976048cf09497011d8c77c8 -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/07c58a9399552e3b8362d9c1dd155693 -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/98570a4dae80f9b4366c08994911efc87bf6967e63e20b486240a3b2d7637fabbfcca3fe8340ae4d9bae7702be400f5976fc5aa0020f984157b097b02e08d23c -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/25a9af724bb8a5ca42be6277a726583e -OpenBLAS.v0.3.27+1.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/7afbc7453d1f22171523476e90882f67611374b03a3481bdb588722bc4816d081304b811a0dd452288ca972bea95bd2d2286644bda309dbe25fe721321298e85 -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/2f9494f7600729bfa00a0db96bd9349d -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/eae895a7ef4d9922bf9f6c454f56b2881fd5549e6c6a825e0e4d5b84defe9a97719a9f1e62f996dd545afdf372c1ab18bbee0a6cce8474d9adb2522b16678d35 -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/8f30a26bd56ced5d6edc88b1fae57beb -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/ad849216c9655dc160a0cd756904442a80d693121e60a2b33876ac347c79fe6e3e602faad0c64a45599f5a5e203c3d9e8316c6b20c41d81e666b7650dccfaa5c -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/272facb48c295ccfea2a291869e1817e -OpenBLAS.v0.3.27+1.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/7fd5c23046fa548f0bed6e7ce4f6fa809e56909b5595d2a4f348189ee99f234dc84989219ee63cdc004ce303b50fee2aa1fcb93589ff116a2191f8ef520d24be -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/c130634237846672f3a672f1d0e346d9 -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/c174d00870ce3944c553122606cba7b78312342a02dc4833a91ae105f05d85d06e665e86a79452bdb7d2b31c18936582d79427ec3976048cf09497011d8c77c8 -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/07c58a9399552e3b8362d9c1dd155693 -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/98570a4dae80f9b4366c08994911efc87bf6967e63e20b486240a3b2d7637fabbfcca3fe8340ae4d9bae7702be400f5976fc5aa0020f984157b097b02e08d23c -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/25a9af724bb8a5ca42be6277a726583e -OpenBLAS.v0.3.27+1.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/7afbc7453d1f22171523476e90882f67611374b03a3481bdb588722bc4816d081304b811a0dd452288ca972bea95bd2d2286644bda309dbe25fe721321298e85 -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/2f9494f7600729bfa00a0db96bd9349d -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/eae895a7ef4d9922bf9f6c454f56b2881fd5549e6c6a825e0e4d5b84defe9a97719a9f1e62f996dd545afdf372c1ab18bbee0a6cce8474d9adb2522b16678d35 -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/8f30a26bd56ced5d6edc88b1fae57beb -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/ad849216c9655dc160a0cd756904442a80d693121e60a2b33876ac347c79fe6e3e602faad0c64a45599f5a5e203c3d9e8316c6b20c41d81e666b7650dccfaa5c -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/272facb48c295ccfea2a291869e1817e -OpenBLAS.v0.3.27+1.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/7fd5c23046fa548f0bed6e7ce4f6fa809e56909b5595d2a4f348189ee99f234dc84989219ee63cdc004ce303b50fee2aa1fcb93589ff116a2191f8ef520d24be -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran3.tar.gz/md5/14cee2ac2cff0d9d8b614278e3f7a4ed -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran3.tar.gz/sha512/d81aa1c8ff70d8d24d2cf88adc568dbf6a77f191332aa0298fbc0faad1fda855c9a6c278d0556003cca315ef75e47cf7caa6963b4e16f4d883ba7c1b13a298bb -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran4.tar.gz/md5/559f96fb8a2a03df6689200173f2c1df -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran4.tar.gz/sha512/cc1e987b2ad7d47b474d39b0f93ee6f6e46a4e5d0760cea9e31a0d3c5336e6cfc88401122ab278c0b745c9e60b290f9c05edf39bef9e7e97c70f33dc7afac341 -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran5.tar.gz/md5/c572b06af06609e5e84dc8aee61babc1 -OpenBLAS.v0.3.27+1.i686-linux-gnu-libgfortran5.tar.gz/sha512/d799e280600970697701098f76e79d0bb72bf55cbe8d6c131bd26f6a67bdcb5ed307b26eae89bc6b7cc6b6eb25d2622f952b315f7850b7f231148f14cc09b769 -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran3.tar.gz/md5/4aa9f25b39088f79ea13aab1097c0c1f -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran3.tar.gz/sha512/126876d9de1c67302dc1b9b71a96fd2f5eb45745ebbcea6d4b7d4bdfac93088ef6b89e75a2bfcd83f1b32dc798b7ef824bb225e24e88e6443571d0576939bb05 -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran4.tar.gz/md5/4ffd9c16cd3c6535457dd654f95c62e6 -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran4.tar.gz/sha512/cc7fbe4949b5b51e5f1f5fdae537bbcc68ef4a59a02c290df2f6723bdeb52d98e699e4b23a879372d56279196295d8c938ba2221fe3a73cd1ef953059cdf694f -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran5.tar.gz/md5/7d6855b9a879259216c243dcfc75a2cc -OpenBLAS.v0.3.27+1.i686-linux-musl-libgfortran5.tar.gz/sha512/221d1ba0250802ae88daac384fd1b2c911c49f8e141efbf3c2668260f4018c5e5f1e21c459a1595652ca48ebc446fe43e54fbf732b47d68f20ecb1e280862570 -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran3.tar.gz/md5/646fdfccf16f12f23441723e13c12f58 -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran3.tar.gz/sha512/2692aae16acba199584da71275eb609071d6f7a6d644239f9b6307fe12fc875d6267b11d387b2cace1d5866bf50ab0db619510d02acd3c90696bfb0dfe958037 -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran4.tar.gz/md5/257e35006373e43fedb211c56b73315a -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran4.tar.gz/sha512/e4d8049a6e30763dbacba7646805bb72abad021f8810fb084a287d389137e30b96f12f04ad625c5ef322d127f7b603f388fee18a516e101761391d405ec58d2e -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran5.tar.gz/md5/68245d8b061c60f97f48fd4fde4492dd -OpenBLAS.v0.3.27+1.i686-w64-mingw32-libgfortran5.tar.gz/sha512/511f5fcb538067c04742ad578d2584ebb3cc54bd7c43b84b14d3597bcb84d303a729a48c79018afa119ef12e084bed5ce6fe3591774a1cd6a5b6bbe5df4a8753 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/883728fe99e27d1f066032e1465880b2 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/3363ad953d1d7b5ba233b5d6ff65411e51189adcc6e7a9b68e45388132b38701eba53745f826f896820a98bc5015a8787ab1257f1a25c0a55f0437707c451d20 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/672fb00c47939bfc1893c7bf630b6904 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/e1beb8be0b58402df60b14a81d4fefe13cb0a30450717c80f2670b3a7947a89574848e858f90e0efd5474c47cdb86ce5623645988f05f105df206abd888c2f58 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/32eeeeeb57ed38bb4123ea793faf6685 -OpenBLAS.v0.3.27+1.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/bc505de8d8378e5c0fd6b3092b7093ecae0cacd9d5f6fa94e6e01ead03ffd7abad31c8d75fa84cf6da4f4fd33dde33df968595ecdc818f5b891b82db1be2d1a1 -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran3.tar.gz/md5/e49f4562399b5d45d987e9820774f7c8 -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/0e5ade0c2112f01b3bde14ddb0fe500085d75fc86117d54bc66cc2da30f7251233387a90daca6203ebe457bc68e8bf3cff62c011b424a971ff9f7932974eaba4 -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran4.tar.gz/md5/26c9067086aa013de9c3b4001cd3f78a -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/e641bc045b96cb011183e26541730b46b8dfb401ef1223f10f19450de206d9971f3181d37c7687477d782238e580bbba4fddbcb2094a45761b55dcc93a9cacd4 -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran5.tar.gz/md5/a2cf4ac08dc296f6aaf109e8d1fff491 -OpenBLAS.v0.3.27+1.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/2210bc1dfa32b0b9b86ec84943b6673bc540a0822652274ececa0f394ed406d9f23f02909f2b8f97dd2a2bc114df2d0e9a6d868d29bc2d08a3da7176743a6d10 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran3.tar.gz/md5/1b501f18b00d1e051b4af955da81b3c9 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/1b5615dc63efd0166b206bbdc90801d0c623f93f537c320bac1af8bf41f9e3ae8ec33eb6b43a7bd9dc2d9ba526bc7bb200ff828f33ef36da920f9290fa4ff252 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran4.tar.gz/md5/079cebb72efd39454275a8199fc78c17 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/94bdd5db2546381e3cd15bb60b382c11d8ba879f8b88771a15d0d7cbf5a399f46aec60fc01e07258614ec039bf9bf73cbeffc9d2f29b03c9885e63704f0d2ab0 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran5.tar.gz/md5/cef6229311f1616c0db95cef84725cd4 -OpenBLAS.v0.3.27+1.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/297eda815134d5de59d1614b39f06a512f4ef544dc5abaffa015075a8bcba1506aa4825109213e54e94401cbf16d4292a1ec2b9b71b278cc8536379d80d96e46 -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran3.tar.gz/md5/b410edbbc651fd9f6589fda153b410da -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran3.tar.gz/sha512/718c22a940d998dcd8c754994f5a7d9bd3e3131d51beb1d8071ca0005e5c562bb2368924b0c4951839df0bc85a272962f87891b366a1bce1f735cc2b3495b834 -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran4.tar.gz/md5/af8f2dc642041d5e4eff98d6b20e7596 -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran4.tar.gz/sha512/48b88f703cc0e35d8f3b3bf7f395481a3225f5c3d1a4277a7b477815feab71df5c6b662313f4762bc8002f43c0f1bece0f383bc3920c09d383303b3927925ddf -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran5.tar.gz/md5/eba3e9322d39993d81d78486306b301f -OpenBLAS.v0.3.27+1.x86_64-linux-musl-libgfortran5.tar.gz/sha512/dc11716e4f7a53a396b8b8cd3e506bd66272e9e8c5533199dc972c91fed0cea5067ec8e14abf67da2b53af7f3189eafc5c188657d617eea3f55ed248d7ed38e4 -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/03f45c7c0276f58719235e5da3bcdc85 -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/836fbbdae1065393de8ad1410301afbecfb0bf60256322b754e17aa5b4edb20e409eeca2f66f9a2b9ffb5872479cd3cab9b721bd2fc9c3544f5e90e78c7e59c7 -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/dc6bc577a3ccd78364e9fcb98fec03dd -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/2c52880b287b0c4f48ed3539e4e0b24b6a05b46d47d7586eea7ca06ebc19c7f0d018fdd24e8da94249fa3b7dc54b85b27ebc530fc5cefb2d9b5457e00dee3529 -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/30a6a329d4d37dea7199dfcf264a2641 -OpenBLAS.v0.3.27+1.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/53be21e7a94033cd44b8e2d375b38606446800344698e4f365527d807f736b7b2b9a897138b5de5bd62ba9da104cd6f86bf59caebc18299c0abd98899c527988 -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/290a8fc0d1580aeed8cb7b793ff991bf -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/e30e4f666255982c1adbf73020bf88e2d499d2d26216a16c34b4a6b7ed0dc5b7d5374a978a7d0ef5735a82394b4ef06bd82491e2ddf7ec5775953b9183e9f601 -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/1b46471021b6914fc60401e3e1ffe78b -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/509d5138d8cd9621937f840b7f73949facec2f047676069403d3d7c482ea183766dc84536d9c2a291b18a2b89902e6f714665fa0b7a920727635530a3aa4aa17 -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/ae28948c5d496a3d0bba649c72822b2b -OpenBLAS.v0.3.27+1.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/8d66a65040e973947a8a7d4f4b1d47d63a77b75c0d5e04843de9188256aeec5a1c7e0d59bf5e1d5c262c4f1a4ff2aa36599840337e9d828fb77724c38c1fff4e -openblas-6c77e5e314474773a7749357b153caba4ec3817d.tar.gz/md5/4971eeb7adadee085d7c991db416fe7a -openblas-6c77e5e314474773a7749357b153caba4ec3817d.tar.gz/sha512/7b85c9fb7be54407ba627d77897f40de4395d6d307230aa7df83cf8e0a41f545e4af4ae0576abb40cc9e0c385e1c6a488100dff292ea307439a89587c07ba66f +OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/e3edc449afa805b3744eb153460b681f +OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/4f619ae720bc2a55c6d7d53b78bf0a15f66c5b3474c968b367f41d71c759b39028817e3e7ba3cebc4ee06f2176578a5a1bd2be7cf2f1461a396c418292fcf782 +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/e01dcbdbfd2c8f15d78efb0aa5673944 +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/33dee7c48f981b218792e150aea506989b7bbacfd540ebd1fefb150af3c33eae62cd523c329ef8f37c0b56643d480e105ed82e46ec5b3f683e006d05dda717ee +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/43662cb933b2aab820bd210edd4e994a +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/520abb2521a4b9ae71c86dafc7de4155d51df09ff119a3b1d25a9bac3fb73aceaf38b7805888d4858b96e73c0d1cf80d8953b9db954df4d0e6c164894d07d715 +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/5c693f190d26194353c1e096b40568bc +OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/3c3e11ba038e59002d23e885e22129dda13453469dad79f39f9cddceadbf1d39e61487894f5112b2fcb5265cd98075103d99eff2a83f79407aafa545b03e9f9c +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/2892710a8545b4573014024222bb8dff +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/6a628c9f7eab2a34198028846a6aec7bb13af4425e1b73ba5b58d326c1eb0741b5dc08fff3db565c92cbc0e2718b62fa6dedac5fa0bdb2f35561663f36f4dfbe +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/fbec5f47685d4bb36956cd4aee34f1e5 +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/ac69a9ed17900de79c6da0ff08a97f3397860de92ce1888f77c8c8fada08fab15fff1b19868c95865ad4a387701c2ffe74e695d6949d8ba02534f91aca2a5ca3 +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/6a0a1a1cad6452ac687e24147128f027 +OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/0ea2b7f829b4e406276911db743706b17d7902899d4492e18b9676fed9b27d976d586e38505c52932e27f194c9806d6cb53182cb128baab41898605af7c346b5 +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c +OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 +OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c +OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 +OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/md5/36f76f7588ad5bc48c2f68daee49da57 +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/2184cac67657fb58afc42fff46069084ffbcbc67938e7e74e9e5a926cc83733c702cacf16ca320381f5bb1f219cbea764ae8cdb9c445f7224ac0cd0beab822ff +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/md5/ef8501cc6babf8be3b8b649da2a7c692 +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/98543cfd34a185644cebc33cd82ebfb663c92f1fa8349121e6d34f86b1d10f4f37688b84b22182f9e29daa74664a469ddc67408827e8bc7fddb1a7311d918532 +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/md5/598c07efb122e75e6e99ba7fc0c4fb4b +OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/b7caa20a36d6806895f3efb02830017c3ca8037c5af3a29df00f9fe34945324c34181a945b1dbe8a8ca43c7f792667d7640c23b5c2fa4fd93564f1da78561190 +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/md5/e7667d215442ac0db83969d41a678774 +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/sha512/3b22dd658b5948d6867b3e57fe53976eef59339d2714709746098b96f13766d86e918a139929aa60672be91c50c7f739c5c0db372f07a71ae2447588db3685e4 +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/md5/91d95572ce67a21d107b9fbcd3aba11d +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/sha512/7727d24fec0a333a943de3f9d6dd5c698e4f3b9099fd838b8b5652f6216f7b9fe4a2d8f014a4f0b3b7ad7fe05b81a9079e570454d456f0462e7d04f66e264ecb +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/md5/2d56a5cfeae0a6afa2d2b8efa1ab22c5 +OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/sha512/e81207bee11f89669837db08b57b63813056730f68412345421539399c12a675ed01942558ebd42045959c29a2b774a75f28c4a6b14549b7373b54a5e93e8933 +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/5b741b5fec8b564ba8b24435b5d885ae +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/97e72a4b9b39d4889c4e36eff85186fcbabfff2930185e69b3c259b43cdbaa5fab51bf0ed4024d1ddd3c780edaf501c4f5f7534e0a2edb802d580987fbd026ab +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/d5f059fc960b7dc84ee4b92c431d87b4 +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/f1e8f31f89274ff5b466931f8941de55fb27d2ee773d87e7e0b992deeec7d921358b10520cc0f47975022536b5e9d7b1cc9acc481b95f83cc2096d7cb7494616 +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 +OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 +OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/24c915a3156983745662ff99e5d1b040 +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/47fb327281c903eebb0a261458fc10f09bac317d7e135dff72a112c868a2525fa542f93f22da083c13454fc241352d39a8e8463085685aa77e055ffcadf451c8 +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/93041d21ad3f95e6d9cbac6cd6730363 +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/17cd2302860eeee884f97d87eaf0ad12cdc7361734cfaa77b543119c58103a5da107b478e7ecfcb135d2e5beffd6a3907108b2911a095a3cbc1d16f32371ac1b +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/98a8c6c8c80c11e8b6d127959c9b3414 +OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/d26a51785391d81efcaefcf647fcf0348ad68ff01845ab3547778903d2ab5c5c1cdb2a562ae5cf7f12878f3345c46321719ea82fb87ef655d303a4c0c9803377 +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/41f7fdc10d8cab0965da95e00e2269ba +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/0a47ef77f9b2b70f424e00957f676c53d19c5dffbbcd5a743ab24bbc8608c5e8ad3cb3afefd8cab60a3c51970a63dd47c97868ecc0ef3532b83278c41a8daf96 +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/8453e7a5e5285e770fde7592582bc0e2 +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/a37edfe68c85a6312d7918f1b58d6ac4bafc13081dbd327c830631913350a54bbf8bea57115b4f242d5f93c6b0a8f4995b5ef544a0de06e76c66287ff092e74c +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/6df24890be7a4899f35a2949f9f21d65 +OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/66601632f91689fe72afecd2e4d4ef3ad3b7653874228d5509c7732f2e6d63640f35d176ce2219b732632e0daeb76dc3ba11d3e776639985359b21b313056883 +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/d35df8d213c55bc1f9f765e0ba8c7b4e +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/5d5de73150a2e796dc29425dbf415ff7aa443264d767d4e688de07335961ee39835c94b7d68900d49b70bf3ac08d356f3ae00c6d651eed64e504b02c9351edcb +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/384f9173b3804e6f9c73bcde9dacb545 +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/64d3abeca401cee06575915161458408e9fb51e26f759129e1c7a9c27f68729d66e75f0654b977e842844650698c4b1627a18e495d91202a8c0483ef1b35bafc +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/0bd296248e1337fac054b9e0993fea82 +OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/fdb9ce296228f92c112bbeb346a2900a8d5a73e21a313a217cf7135fd77484cdeed53c86382ee5550f1b624eb6ed99d06b739229add7364217ca68fefedd04c4 +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/524a2481331fdd3933f06b40e63433f1 +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/06831cc855e9801dbf2248a0da123c345b6731c830f39d3166b8d8e7de8d649b6d9900e534ec6c1113a227203f6a9aa8171fcf548cfd56a4a67b6037c105ecf5 +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/82f2b8f31f7b718f6ea743c996acbe4d +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/fd1ccab964ae9410238d29b38cfe8c2ccca3fda3d78b4294bb4a54ab8abfd6bdaa80cadc0aeadf054bf99138c5dc3cac9370920b0b16cb8793630ab21d5bf667 +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/b91add21ba0e2a0f28a9e4d347111cc3 +OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/8ed1d9e327529ee067d975c5c96dac3eabab5a88ed7b1b6e1b030f96bbd2418e3173cacd70e9976d619245757f2a34cc9527aafef1626fd288f14918c9b13eaa +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 +OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff +openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/md5/f7a1fe86cefbf7d4f2608843c7833ca7 +openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/sha512/5f6020e958967a12a3c5b18bde13331f9c0602bd073563f35cd7cec848c92b45f30ca362819b12cd16989c0e4641ee3e63db8322d1092f61b31ba2e4068dd7a7 diff --git a/deps/openblas.version b/deps/openblas.version index 527764e3f8603..09dcdc45af1ef 100644 --- a/deps/openblas.version +++ b/deps/openblas.version @@ -3,9 +3,9 @@ OPENBLAS_JLL_NAME := OpenBLAS ## source build -OPENBLAS_VER := 0.3.27 -OPENBLAS_BRANCH=v0.3.27 -OPENBLAS_SHA1=6c77e5e314474773a7749357b153caba4ec3817d +OPENBLAS_VER := 0.3.28 +OPENBLAS_BRANCH=v0.3.28 +OPENBLAS_SHA1=5ef8b1964658f9cb6a6324a06f6a1a022609b0c5 # LAPACK, source-only LAPACK_VER := 3.9.0 diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index 95dc40e6a0c2b..dfca282c74704 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" +version = "0.3.28+0" [deps] # See note in `src/OpenBLAS_jll.jl` about this dependency. From 829fb7cf58c379ba2d3821950fb3f79f3116d2d4 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 2 Jul 2024 16:31:10 -0400 Subject: [PATCH 055/548] TOML: Improve type-stability This changes the output of the TOML parser to provide specialize `Vector{T}` less aggressively, so that combinatorially expensive types like `Vector{Vector{Float64}}` or `Vector{Union{Float64,Int64}}` are instead returned as `Vector{Any}` Vectors of homogeneous leaf types, like `Vector{Float64}` are still supported as before. This change makes the TOML parser fully type-stable, except for its dynamic usage of Dates. Co-authored-by: Gabriel Baraldi --- base/loading.jl | 1 - base/toml_parser.jl | 100 ++++++++++++++++++++----------------- stdlib/TOML/test/values.jl | 2 +- 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 4dc735f0099d8..e0a2563dd0178 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -269,7 +269,6 @@ struct TOMLCache{Dates} d::Dict{String, CachedTOMLDict} end TOMLCache(p::TOML.Parser) = TOMLCache(p, Dict{String, CachedTOMLDict}()) -# TODO: Delete this converting constructor once Pkg stops using it TOMLCache(p::TOML.Parser, d::Dict{String, Dict{String, Any}}) = TOMLCache(p, convert(Dict{String, CachedTOMLDict}, d)) const TOML_CACHE = TOMLCache(TOML.Parser{nothing}()) diff --git a/base/toml_parser.jl b/base/toml_parser.jl index cc1455f61928b..4d07cfed05d8a 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -84,9 +84,6 @@ mutable struct Parser{Dates} # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} - - # Optionally populate with the Dates stdlib to change the type of Date types returned - Dates::Union{Module, Nothing} # TODO: remove once Pkg is updated end function Parser{Dates}(str::String; filepath=nothing) where {Dates} @@ -106,8 +103,7 @@ function Parser{Dates}(str::String; filepath=nothing) where {Dates} IdSet{Any}(), # static_arrays IdSet{TOMLDict}(), # defined_tables root, - filepath, - nothing + filepath ) startup(l) return l @@ -495,8 +491,10 @@ function recurse_dict!(l::Parser, d::Dict, dotted_keys::AbstractVector{String}, d = d::TOMLDict key = dotted_keys[i] d = get!(TOMLDict, d, key) - if d isa Vector + if d isa Vector{Any} d = d[end] + elseif d isa Vector + return ParserError(ErrKeyAlreadyHasValue) end check && @try check_allowed_add_key(l, d, i == length(dotted_keys)) end @@ -537,7 +535,7 @@ function parse_array_table(l)::Union{Nothing, ParserError} end d = @try recurse_dict!(l, l.root, @view(table_key[1:end-1]), false) k = table_key[end] - old = get!(() -> [], d, k) + old = get!(() -> Any[], d, k) if old isa Vector if old in l.static_arrays return ParserError(ErrAddArrayToStaticArray) @@ -546,7 +544,7 @@ function parse_array_table(l)::Union{Nothing, ParserError} return ParserError(ErrArrayTreatedAsDictionary) end d_new = TOMLDict() - push!(old, d_new) + push!(old::Vector{Any}, d_new) push!(l.defined_tables, d_new) l.active_table = d_new @@ -668,41 +666,20 @@ end # Array # ######### -function push!!(v::Vector, el) - # Since these types are typically non-inferable, they are a big invalidation risk, - # and since it's used by the package-loading infrastructure the cost of invalidation - # is high. Therefore, this is written to reduce the "exposed surface area": e.g., rather - # than writing `T[el]` we write it as `push!(Vector{T}(undef, 1), el)` so that there - # is no ambiguity about what types of objects will be created. - T = eltype(v) - t = typeof(el) - if el isa T || t === T - push!(v, el::T) - return v - elseif T === Union{} - out = Vector{t}(undef, 1) - out[1] = el - return out - else - if T isa Union - newT = Any - else - newT = Union{T, typeof(el)} - end - new = Array{newT}(undef, length(v)) - copy!(new, v) - return push!(new, el) +function copyto_typed!(a::Vector{T}, b::Vector) where T + for i in 1:length(b) + a[i] = b[i]::T end + return nothing end -function parse_array(l::Parser)::Err{Vector} +function parse_array(l::Parser{Dates})::Err{Vector} where Dates skip_ws_nl(l) - array = Vector{Union{}}() + array = Vector{Any}() empty_array = accept(l, ']') while !empty_array v = @try parse_value(l) - # TODO: Worth to function barrier this? - array = push!!(array, v) + array = push!(array, v) # There can be an arbitrary number of newlines and comments before a value and before the closing bracket. skip_ws_nl(l) comma = accept(l, ',') @@ -712,8 +689,40 @@ function parse_array(l::Parser)::Err{Vector} return ParserError(ErrExpectedCommaBetweenItemsArray) end end - push!(l.static_arrays, array) - return array + # check for static type throughout array + T = !isempty(array) ? typeof(array[1]) : Union{} + for el in array + if typeof(el) != T + T = Any + break + end + end + if T === Any + new = array + elseif T === String + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Bool + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Int64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === UInt64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Float64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Union{} + new = Any[] + elseif (T === TOMLDict) || (T == BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) || + (T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime) + # do nothing, leave as Vector{Any} + new = array + else @assert false end + push!(l.static_arrays, new) + return new end @@ -1025,10 +1034,9 @@ function parse_datetime(l) end function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.DateTime(year, month, day, h, m, s, ms) + return Dates.DateTime(year, month, day, h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1039,10 +1047,9 @@ function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) wh end function try_return_date(p::Parser{Dates}, year, month, day) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.Date(year, month, day) + return Dates.Date(year, month, day) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1062,10 +1069,9 @@ function parse_local_time(l::Parser) end function try_return_time(p::Parser{Dates}, h, m, s, ms) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.Time(h, m, s, ms) + return Dates.Time(h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() diff --git a/stdlib/TOML/test/values.jl b/stdlib/TOML/test/values.jl index 4fc49d47fc98d..53be1b04708b3 100644 --- a/stdlib/TOML/test/values.jl +++ b/stdlib/TOML/test/values.jl @@ -172,6 +172,6 @@ end @testset "Array" begin @test testval("[1,2,3]", Int64[1,2,3]) @test testval("[1.0, 2.0, 3.0]", Float64[1.0, 2.0, 3.0]) - @test testval("[1.0, 2.0, 3]", Union{Int64, Float64}[1.0, 2.0, Int64(3)]) + @test testval("[1.0, 2.0, 3]", Any[1.0, 2.0, Int64(3)]) @test testval("[1.0, 2, \"foo\"]", Any[1.0, Int64(2), "foo"]) end From c907192a4ba1ec1c4a2b00303f1b3f19188c4b5e Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 12 Aug 2024 06:51:19 -0400 Subject: [PATCH 056/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?SparseArrays=20stdlib=20from=20e61663a=20to=2055976a6=20(#55469?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: SparseArrays URL: https://github.com/JuliaSparse/SparseArrays.jl.git Stdlib branch: main Julia branch: master Old commit: e61663a New commit: 55976a6 Julia version: 1.12.0-DEV SparseArrays version: 1.12.0 Bump invoked by: @ViralBShah Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaSparse/SparseArrays.jl/compare/e61663ad0a79a48906b0b12d53506e731a614ab8...55976a6e4f883a32e3d3658af50c49879b98fce0 ``` $ git log --oneline e61663a..55976a6 55976a6 Keep sparse solvers docs as before (#552) 95fd7ff Missing space in error message (#554) b8a13ef implement in-place `ldiv!` for Cholesky factorization (#547) 1527014 Do not use nested dissection by default. (#550) ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 new file mode 100644 index 0000000000000..401900c9f2739 --- /dev/null +++ b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 @@ -0,0 +1 @@ +7b16893f5a49cee4a4d9219afbb57577 diff --git a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 new file mode 100644 index 0000000000000..e8f1bc3c4aaaf --- /dev/null +++ b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 @@ -0,0 +1 @@ +398028ee8f23ee0b4a5144f5dd03d756cd7575a0b806f351d76339e9521830f75c0c96806a3ee5cebe04853feab51c75c7bcdaaa623acc649bc1a24395df5581 diff --git a/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/md5 b/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/md5 deleted file mode 100644 index d35cbc567faec..0000000000000 --- a/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -19f6d3bcbeec7a123e8dde983ef66a9a diff --git a/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/sha512 b/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/sha512 deleted file mode 100644 index f2c8db80327ce..0000000000000 --- a/deps/checksums/SparseArrays-e61663ad0a79a48906b0b12d53506e731a614ab8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8cef45d83047eba97edcaed04bb49f5aabdd96ec951baaa772c7da0402259e9578cfa383ab882440f685338ed14f797afe776a14e6aeea9df2428aa1592fcabf diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 7c99b8ba52d7c..2dc7bd8f5b3b9 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = e61663ad0a79a48906b0b12d53506e731a614ab8 +SPARSEARRAYS_SHA1 = 55976a6e4f883a32e3d3658af50c49879b98fce0 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From cf4c30accd92755bc39d52364ae6549c490a4bc8 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Mon, 12 Aug 2024 09:10:03 -0400 Subject: [PATCH 057/548] Add push! implementation for AbstractArray depending only on resize! (#55470) Fix #55459 In Julia 1.10, `push!` and `append!` would be functional for `AbstractVector` implementations if `resize!` and `setindex!` were defined. As of #51903 by @vtjnash as in Julia 1.11.0-rc2, `append!` now depends on an implementation of `sizehint!` and `push!`. Since `push!` also depends on `append!`, a stack overflow situation can easily be created. To avoid this, this pull request defines the following * Add generic versions of `push!(a::AbstractVector, x)` which do not depend on `append!` * Add default implementation of `sizehint!` that is a no-op The implementation of `push!(a::AbstractVector, x)` is a generic version based on the implementation of `push!(a::Vector, x)` without depending on internals. # Example for SimpleArray Consider the `SimpleArray` example from test/abstractarray.jl: ```julia mutable struct SimpleArray{T} <: AbstractVector{T} els::Vector{T} end Base.size(sa::SimpleArray) = size(sa.els) Base.getindex(sa::SimpleArray, idx...) = getindex(sa.els, idx...) Base.setindex!(sa::SimpleArray, v, idx...) = setindex!(sa.els, v, idx...) Base.resize!(sa::SimpleArray, n) = resize!(sa.els, n) Base.copy(sa::SimpleArray) = SimpleArray(copy(sa.els)) ``` Note that `setindex!` and `resize!` are implemented for `SimpleArray`. ## Julia 1.10.4: push! is functional On Julia 1.10.4, `push!` has a functional implementation for `SimpleArray` ```julia-repl julia> push!(SimpleArray{Int}(zeros(Int,5)), 6) 6-element SimpleArray{Int64}: 0 0 0 0 0 6 ``` ## Julia 1.11.0-rc2 and nightly: push! requires sizehint! and is prone to stack overflow Before this pull request, on Julia 1.11.0-rc2 and nightly, `push!` fails for want of `sizehint!`. ```julia-repl julia> push!(SimpleArray{Int}(zeros(Int,5)), 6) ERROR: MethodError: no method matching sizehint!(::SimpleArray{Int64}, ::Int64) The function `sizehint!` exists, but no method is defined for this combination of argument types. ... ``` After implementing `sizehint!`, `push!` still fails with a stack overflow. ```julia-repl julia> Base.sizehint!(a::SimpleArray, x) = a julia> push!(SimpleArray{Int}(zeros(Int, 5)), 6) Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable. ERROR: StackOverflowError: Stacktrace: [1] _append! @ ./array.jl:1344 [inlined] [2] append! @ ./array.jl:1335 [inlined] [3] push!(a::SimpleArray{Int64}, iter::Int64) @ Base ./array.jl:1336 --- the above 3 lines are repeated 79982 more times --- [239950] _append! @ ./array.jl:1344 [inlined] [239951] append! @ ./array.jl:1335 [inlined] ``` This is because the new implementation of `append!` depends on `push!`. ## After this pull request, push! is functional. After this pull request, there is a functional `push!` for `SimpleArray` again as in Julia 1.10.4: ```julia-repl julia> push!(SimpleArray{Int}(zeros(Int, 5), 6) 6-element SimpleArray{Int64}: 0 0 0 0 0 6 ``` --- base/abstractarray.jl | 32 ++++++++++++++++++++++++++++++++ test/abstractarray.jl | 9 +++++++++ 2 files changed, 41 insertions(+) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 77aae63399ec8..3f8886e14940c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3525,6 +3525,35 @@ julia> map(+, [1 2; 3 4], [1,10,100,1000], zeros(3,1)) # iterates until 3rd is """ map(f, it, iters...) = collect(Generator(f, it, iters...)) +# Generic versions of push! for AbstractVector +# These are specialized further for Vector for faster resizing and setindexing +function push!(a::AbstractVector{T}, item) where T + # convert first so we don't grow the array if the assignment won't work + itemT = item isa T ? item : convert(T, item)::T + new_length = length(a) + 1 + resize!(a, new_length) + a[new_length] = itemT + return a +end + +# specialize and optimize the single argument case +function push!(a::AbstractVector{Any}, @nospecialize x) + new_length = length(a) + 1 + resize!(a, new_length) + a[new_length] = x + return a +end +function push!(a::AbstractVector{Any}, @nospecialize x...) + @_terminates_locally_meta + na = length(a) + nx = length(x) + resize!(a, na + nx) + for i = 1:nx + a[na+i] = x[i] + end + return a +end + # multi-item push!, pushfirst! (built on top of type-specific 1-item version) # (note: must not cause a dispatch loop when 1-item case is not defined) push!(A, a, b) = push!(push!(A, a), b) @@ -3532,6 +3561,9 @@ push!(A, a, b, c...) = push!(push!(A, a, b), c...) pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a) pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) +# sizehint! does not nothing by default +sizehint!(a::AbstractVector, _) = a + ## hashing AbstractArray ## const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5 diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8efe2e64b606c..8b4a1d9113940 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1436,6 +1436,15 @@ using .Main.OffsetArrays end end +@testset "Check push!($a, $args...)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) + orig = copy(a) + push!(a, args...) + @test length(a) == length(orig) + length(args) + @test all(a[end-length(args)+1:end] .== args) +end + @testset "splatting into hvcat" begin t = (1, 2) @test [t...; 3 4] == [1 2; 3 4] From a23aee8c66aaf2c2b35ecb63fe938ae8519f49d9 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:20:03 -0400 Subject: [PATCH 058/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?StyledStrings=20stdlib=20from=20d7496d2=20to=20f6035eb=20(#5546?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: StyledStrings URL: https://github.com/JuliaLang/StyledStrings.jl.git Stdlib branch: main Julia branch: master Old commit: d7496d2 New commit: f6035eb Julia version: 1.12.0-DEV StyledStrings version: 1.11.0(Does not match) Bump invoked by: @LilithHafner Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/StyledStrings.jl/compare/d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa...f6035eb97b516862b16e36cab2ecc6ea8adc3d7c ``` $ git log --oneline d7496d2..f6035eb f6035eb Replace accidental Int64s with Ints 4fcd8bb Use const fields in parser State instead of refs 35a3cdf Load user-customisations lazily 9802b6c Load ScopedValues symbols from their source 9b9cf71 Use branches when choosing how to merge face attrs eada2dc Avoid needlessly creating a new Face in get calls c647af9 Avoid boxing mergedface by making it toplevel a117008 Avoid creating strings for ansi_4bit_color_code 6863348 Improve type inference of face merging f588218 Quick fix for 4d04102adf0d (Optimised SimpleColor) 4d04102 Optimise creation of a SimpleColor from a UInt32 6d3f44d Actually overload Base's escape_string 58507e5 Fully qualify method overloads, avoid importing fc686f3 Explicitly test eachregion c417262 Refactor eachregion to be O(n log n) not O(n^2) f7af623 Use concrete refs in macro parser state struct 41b2446 Check for underline term capability flag 987f776 Treat printing as more than a nothing-return write 43fb018 Add types to some comprehensions d3aa7e1 Improve inference with a function over a closure 6901610 Mention the importance of semantic names in docs 0be209b Better hint at the package capabilities in readme 37b9e4b Load no faces.toml when the DEPOT_PATH is empty ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/StyledStrings.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/md5 delete mode 100644 deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/sha512 create mode 100644 deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 create mode 100644 deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 diff --git a/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/md5 b/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/md5 deleted file mode 100644 index 3a5fccdec0fba..0000000000000 --- a/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a02cd2c8bedd83b74917cf3821c89f46 diff --git a/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/sha512 b/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/sha512 deleted file mode 100644 index a042e4f306275..0000000000000 --- a/deps/checksums/StyledStrings-d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2e86daa832533f0369e66e359d7d8f47002f93525f83233c809007a13dfd05a201bcd273b3cb4f3eba2586e98cc9afa43c242f67dc18b91fc898d98a0bd8fde9 diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 new file mode 100644 index 0000000000000..0d39747d275ba --- /dev/null +++ b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 @@ -0,0 +1 @@ +bf7c157df6084942b794fbe5b768a643 diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 new file mode 100644 index 0000000000000..d0a8d6cec08cf --- /dev/null +++ b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 @@ -0,0 +1 @@ +ba2f6b91494662208842dec580ea9410d8d6ba4e57315c72e872227f5e2f68cc970fcf5dbd9c8a03920f93b6adabdeaab738fff04f9ca7b5da5cd6b89759e7f6 diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index 2067083aec74b..83fbece4c8bc0 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = d7496d24d3f05536bce6a7eb4cd8ca05a75c02aa +STYLEDSTRINGS_SHA1 = f6035eb97b516862b16e36cab2ecc6ea8adc3d7c STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 From ac425a597460e8188918dbe1e98a789d85e3d284 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Mon, 12 Aug 2024 15:01:03 -0400 Subject: [PATCH 059/548] Bump SparseArrays for SuiteSparse 7.8.0 (#55472) --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + deps/checksums/suitesparse | 70 +++++++++---------- deps/libsuitesparse.version | 4 +- stdlib/SparseArrays.version | 2 +- stdlib/SuiteSparse_jll/Project.toml | 4 +- 10 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/md5 b/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/md5 new file mode 100644 index 0000000000000..7182cc71f7b35 --- /dev/null +++ b/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/md5 @@ -0,0 +1 @@ +2db86c7030acc973d5b46a87f32f7e99 diff --git a/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/sha512 b/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/sha512 new file mode 100644 index 0000000000000..a9e18eac9bfaa --- /dev/null +++ b/deps/checksums/SparseArrays-0dd8d45d55b305458d0d3d3451057589b684f72f.tar.gz/sha512 @@ -0,0 +1 @@ +0d3f54e7e75b48966e1816608d6ddf62175b92a0c778813a562df20750c6ecef9e4ccc24f9f3fffe4051d4b6765332add8c289fcdc598c320f400cec57a223a3 diff --git a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 deleted file mode 100644 index 401900c9f2739..0000000000000 --- a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7b16893f5a49cee4a4d9219afbb57577 diff --git a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 b/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 deleted file mode 100644 index e8f1bc3c4aaaf..0000000000000 --- a/deps/checksums/SparseArrays-55976a6e4f883a32e3d3658af50c49879b98fce0.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -398028ee8f23ee0b4a5144f5dd03d756cd7575a0b806f351d76339e9521830f75c0c96806a3ee5cebe04853feab51c75c7bcdaaa623acc649bc1a24395df5581 diff --git a/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/md5 b/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/md5 new file mode 100644 index 0000000000000..2f81a0d9191b5 --- /dev/null +++ b/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/md5 @@ -0,0 +1 @@ +46541001073d1c3c85e18d910f8308f3 diff --git a/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/sha512 b/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/sha512 new file mode 100644 index 0000000000000..e2eb44845e276 --- /dev/null +++ b/deps/checksums/SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/sha512 @@ -0,0 +1 @@ +f7470a447b934ca9315e216a07b97e363f11bc93186f9aa057b20b2d05092c58ae4f1b733de362de4a0730861c00be4ca5588d0b3ba65f018c1798b9122b9672 diff --git a/deps/checksums/suitesparse b/deps/checksums/suitesparse index eec27cb539d0f..acec99b39879c 100644 --- a/deps/checksums/suitesparse +++ b/deps/checksums/suitesparse @@ -1,36 +1,34 @@ -SuiteSparse-7.7.0.tar.gz/md5/e659373ed5e9b961d2fcb6d67d250783 -SuiteSparse-7.7.0.tar.gz/sha512/aa62dae81ae423ce7162ae83b46e5cf606d95482e6c6bb7ae6d61e15987761119d9418ef3a96648e6ba2327871a2847eef8ace197aa375279d71c80329d6f451 -SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/md5/46541001073d1c3c85e18d910f8308f3 -SuiteSparse-e8285dd13a6d5b5cf52d8124793fc4d622d07554.tar.gz/sha512/f7470a447b934ca9315e216a07b97e363f11bc93186f9aa057b20b2d05092c58ae4f1b733de362de4a0730861c00be4ca5588d0b3ba65f018c1798b9122b9672 -SuiteSparse.v7.7.0+0.aarch64-apple-darwin.tar.gz/md5/276f7355e36eeab2911a141e5570dede -SuiteSparse.v7.7.0+0.aarch64-apple-darwin.tar.gz/sha512/72aa979c3a4f6d2fa65f4d16ab106a7b306f5e84da91bf04a7a11bd863f71a8386ca5248b7e3fde83347cf912fae8ec3c87617db09f6bfadf12c476061855d28 -SuiteSparse.v7.7.0+0.aarch64-linux-gnu.tar.gz/md5/4c3ab9c8c451198420516bd84fdd079f -SuiteSparse.v7.7.0+0.aarch64-linux-gnu.tar.gz/sha512/7afb088a9b117f79531d828a458419e0e8901daa635eeb1b5c753d60c26784496095f2bf70c5c3dedfc5a1c8dd04c56cd8408667fedcbd06abcec0a41a1171bb -SuiteSparse.v7.7.0+0.aarch64-linux-musl.tar.gz/md5/e12af599488fa7578fb8f2018969f4c5 -SuiteSparse.v7.7.0+0.aarch64-linux-musl.tar.gz/sha512/c9e1c2938754dc3b7704e373f36cc876b592acac06c945860958e56f26e09b7be6ce58c4a9184d3528bcc1458d1f7ab9bd605b9a11083419e849e9fa2cc93f2b -SuiteSparse.v7.7.0+0.armv6l-linux-gnueabihf.tar.gz/md5/a3912a6af26ff19d3fcd166d8426f1ff -SuiteSparse.v7.7.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/5f724f5cfb526f2db7d184976f1711f09f77d548593ef9c28ae98a15b6927303864535761929fcd729448d9ece8a7f599cf82d0a83a7668966bdd8b6b62b641f -SuiteSparse.v7.7.0+0.armv6l-linux-musleabihf.tar.gz/md5/24ab4184bf83e59e029cf950be56f1c5 -SuiteSparse.v7.7.0+0.armv6l-linux-musleabihf.tar.gz/sha512/9f1b05c48b051b3c0440e7075f84105a5c5e8e2c8685d93fac847e1cbbf5427ba623ecde16d9b2293b0c286326bfbce07f8d2906a892065fa9fe3d36a4c0386b -SuiteSparse.v7.7.0+0.armv7l-linux-gnueabihf.tar.gz/md5/8433d1206bc72053c1936a1e5f76ea30 -SuiteSparse.v7.7.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/d5f3249db8bb3a4f216d3abef0416e090c1b4d0a847d814df03f3585159602a31b8e4edffae36c3cc39b5c79691c15d51a085b746f03b86d9a0a9b18d00332d9 -SuiteSparse.v7.7.0+0.armv7l-linux-musleabihf.tar.gz/md5/8651a96c9b5617287c917b07d9f6fb16 -SuiteSparse.v7.7.0+0.armv7l-linux-musleabihf.tar.gz/sha512/3e3f21083a8cd26919d6592be41f531ce4293a9e05a84d5298a4d6c3c222892d6d364c30c75558a1461020ac5446fd51e88a333d03118d74eb28ea33a3386d3b -SuiteSparse.v7.7.0+0.i686-linux-gnu.tar.gz/md5/184c95889dfb07319b9ee51e2ff12d0e -SuiteSparse.v7.7.0+0.i686-linux-gnu.tar.gz/sha512/5424a42418e033c67e0868dd7878990158a9f099f2e3ed04aed45c6ceff0a828080df6eae004e10a3784136f66ac13da46df0b3bb3c96fc32c7bdf02830af41f -SuiteSparse.v7.7.0+0.i686-linux-musl.tar.gz/md5/0bde5fe930ec4e2e90945b6bfd78e8d2 -SuiteSparse.v7.7.0+0.i686-linux-musl.tar.gz/sha512/1ff4c8e578146cca72c1bd74cddbba5999053e5729fdb217b0e4f1c0d5cbcae5a73f466e72a52e92979e5f8cc2549b1c5222c7ca32b628db0b71e129a2d22714 -SuiteSparse.v7.7.0+0.i686-w64-mingw32.tar.gz/md5/5439e41ed1909ffe4ba28669eb45ef43 -SuiteSparse.v7.7.0+0.i686-w64-mingw32.tar.gz/sha512/380999433f0a2c1d65a1bf6ea48da60e6cead831cfc31ab3df0ba122afbc32b2e14fb3d8d578a909b9f39f2763923816a691863756996ea064a595e58a788b98 -SuiteSparse.v7.7.0+0.powerpc64le-linux-gnu.tar.gz/md5/ea08ebbd5aaae629a194450c25a77d2e -SuiteSparse.v7.7.0+0.powerpc64le-linux-gnu.tar.gz/sha512/cfe6675e6a6b7790de8a6a3de2dbf561770fa63113c66890a3f888fba71e20c77edaa89b23cdf0038f3a870be9bd5e351aa84b774e7da833c9c0c90e05c0e9fb -SuiteSparse.v7.7.0+0.x86_64-apple-darwin.tar.gz/md5/314a033b51d6d239e29a91fcca911260 -SuiteSparse.v7.7.0+0.x86_64-apple-darwin.tar.gz/sha512/77147381738484d147ce529b4e9d3dff9bccbe5ed07071b5df647a785f118e46792f739f145d597ef78c871d75759348109ad3e08125fb58dd12b8a6813a8fcc -SuiteSparse.v7.7.0+0.x86_64-linux-gnu.tar.gz/md5/f62f17fc50b15e0a4a117f77c52b35f3 -SuiteSparse.v7.7.0+0.x86_64-linux-gnu.tar.gz/sha512/0ba022a5d0039b1348a09521cc2bd366df8c6603a7d3de4bf7d1b15504add8607bf5fa2bcf7d95b2b48cb676c17cc516903323615b6a668e53310363a3f6b242 -SuiteSparse.v7.7.0+0.x86_64-linux-musl.tar.gz/md5/d9b77034590bb0511f2ea2d726303f94 -SuiteSparse.v7.7.0+0.x86_64-linux-musl.tar.gz/sha512/a1149ec6f50b978b1bad91662035d8d131d431459e1910b2cd9fe0307f50d23ca15148f1af522db04327e8cc9cc7c04f85852ddb606ac82fa346b4ab70d28752 -SuiteSparse.v7.7.0+0.x86_64-unknown-freebsd.tar.gz/md5/7b7f00672f0880e397a5182da084c334 -SuiteSparse.v7.7.0+0.x86_64-unknown-freebsd.tar.gz/sha512/06696d78cd7e385906e2fbfbd8ec804de5a4a3d8134d30bc105f713eb915742204e4226229b33a93740f30a3ff24d48dde651e64a78bc6d937e84ce484f6dd74 -SuiteSparse.v7.7.0+0.x86_64-w64-mingw32.tar.gz/md5/91b2e33ead8c2898881475ddfe202987 -SuiteSparse.v7.7.0+0.x86_64-w64-mingw32.tar.gz/sha512/cb5f2caff872ba2ab66f1285e264b4c28ec0a05a4a0fea3964c22aa167195b57a9d9de2c9b9289438459c6b1c1b9f047807414b3e1305e87642edabd22973bd6 +SuiteSparse-7.8.0.tar.gz/md5/ad42a80d28bb56a1fce15f6e7332e04e +SuiteSparse-7.8.0.tar.gz/sha512/91aff0aee26e938ba88a8f92db15b0db0ecc6ada3b60153bb299f53a45ccda131db4bc66f890c220034c900180d0bb3a5fb3e2686fec7d6174f5900a3ee64424 +SuiteSparse.v7.8.0+0.aarch64-apple-darwin.tar.gz/md5/38379e14a53663a9c23f32ed56801676 +SuiteSparse.v7.8.0+0.aarch64-apple-darwin.tar.gz/sha512/3f2a7aa7778a22d150bad9ecb8d03edfa75707a07545e65660c8ccc4b0a9fb058ccab29e21e4728741d40d390d28922d521d3841e16258cf8e26acacadfc1fbd +SuiteSparse.v7.8.0+0.aarch64-linux-gnu.tar.gz/md5/bc52c7df0a442c0fb9aafb83d60878f4 +SuiteSparse.v7.8.0+0.aarch64-linux-gnu.tar.gz/sha512/436e79ea0774d6ffb571b513e385ef48d9cc70b72010cffdc23d606ad6c8984c8b49e2422ce8881def0722f3f608e4ecb87e6752dd80cf7988addd330c5ded13 +SuiteSparse.v7.8.0+0.aarch64-linux-musl.tar.gz/md5/87e4c2588efc39723621ac5010ddf2e5 +SuiteSparse.v7.8.0+0.aarch64-linux-musl.tar.gz/sha512/17115826716bb48f16e4593941be275d47012d112e54d8826c75fde119ffc9f66accd02353b309365b59779d7af3ac220f31ab7cf7eea165b209a93ecdc4102f +SuiteSparse.v7.8.0+0.armv6l-linux-gnueabihf.tar.gz/md5/b1490603aa129942d8e4c9581853cd0a +SuiteSparse.v7.8.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/e23c3532784e295ae72b811d285c3729c3f8ac1b5ee1621e831b6b2824a5b357e4bfa49e09174de7763fc3ebcab6b84ef16536bc1cf6f4bc0543b1b229209178 +SuiteSparse.v7.8.0+0.armv6l-linux-musleabihf.tar.gz/md5/f8199358882f76dd30bcce741b837de1 +SuiteSparse.v7.8.0+0.armv6l-linux-musleabihf.tar.gz/sha512/2c8d4ec21bfe253d3d32a5f5f09601b9b2864149f63f53067b157f5f7315fb04236bf5b19a1e5b4569e2c73127dcbb1703d56c7d06fc3ab9ae155902b7a1c2a9 +SuiteSparse.v7.8.0+0.armv7l-linux-gnueabihf.tar.gz/md5/cc3aa1a013cc91e7076dddf20fba9f60 +SuiteSparse.v7.8.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/a6b8cfbc345a089f12e55d8d44061dcce30f94c2d79fc520d6c5dfe433ac2e362d049fac72278cb59d4b3760ca08d5e350b7e2658fa5e8c77ce8608f67c2c4c4 +SuiteSparse.v7.8.0+0.armv7l-linux-musleabihf.tar.gz/md5/0d7797d31c30c53bf219cdc0a48e64dc +SuiteSparse.v7.8.0+0.armv7l-linux-musleabihf.tar.gz/sha512/a7df8938ee6a04f62169bedd29c8408951cf33a43e0f529fb4d1e360bdad6462a50b2af297adb5f51fd726e1ced1fc8fcda7feeeafbeb44000bfe02a8e29c29e +SuiteSparse.v7.8.0+0.i686-linux-gnu.tar.gz/md5/e48fa3d2e00f210e964c21e4ff27efae +SuiteSparse.v7.8.0+0.i686-linux-gnu.tar.gz/sha512/3088c2af476285eb8549cf6aa56381156d49513a274348f86fbf01aa9ce0712961471f83fa50b261f3f365a302b88eb20ef0bb35b58c07a2cfb5dc337fdb72c1 +SuiteSparse.v7.8.0+0.i686-linux-musl.tar.gz/md5/e55202dbeca107a0c25a4f09d5d68915 +SuiteSparse.v7.8.0+0.i686-linux-musl.tar.gz/sha512/0f4de2e62016914b4d1bcb9b13bd8cb2bebefc5f0a532e103948b9aae79a20462ac7b74a3e968d4f99076c37dbbafb747699cd151e831ff89d297f78478fb84f +SuiteSparse.v7.8.0+0.i686-w64-mingw32.tar.gz/md5/cb971bc1042196e527f95015c8bc5ef8 +SuiteSparse.v7.8.0+0.i686-w64-mingw32.tar.gz/sha512/d445a7790e3ac5392f75c9f4ec30cd1c812354b04388b4c6c6cea2423d2f0dac7173b17a8a2b7a7f4af10321601f96819a7702f9beac0397d85916d99493bc39 +SuiteSparse.v7.8.0+0.powerpc64le-linux-gnu.tar.gz/md5/12058f122b548a37070770d1847f3ce9 +SuiteSparse.v7.8.0+0.powerpc64le-linux-gnu.tar.gz/sha512/f375feeb8448ea90ce8d9f31c7e1230f6868316f06094ba0155069dded4f8da2e1b54d462ef9cfc77abd76147740d4066236dcf1fcea91f8a7141819962ad0ae +SuiteSparse.v7.8.0+0.x86_64-apple-darwin.tar.gz/md5/1bd473f2a25f1ebcea8acc858e2594b4 +SuiteSparse.v7.8.0+0.x86_64-apple-darwin.tar.gz/sha512/034af137deee5bf0ebf3746745d09ad50ce135cd4768a2049bb9811478ff90e6ed8e2c990e277b4c3b38a3a5e9eaa856938eb86239ca445fa64b6dab6af7e996 +SuiteSparse.v7.8.0+0.x86_64-linux-gnu.tar.gz/md5/c58a86d9f25e6705941105d9e41f084c +SuiteSparse.v7.8.0+0.x86_64-linux-gnu.tar.gz/sha512/56447062802f01815ffb014624423c6fd3ab6e16b642b2fe37972a151b02865965c95ca3d1a455c6d51cd31633aea8a732b235b55d68e6779c17b293c488fa43 +SuiteSparse.v7.8.0+0.x86_64-linux-musl.tar.gz/md5/ba6e10ba61c209df94f18ab51fe2dd90 +SuiteSparse.v7.8.0+0.x86_64-linux-musl.tar.gz/sha512/3b8fc504cfb4a3b628d5b955a482bad08c85e09e529f833855a84b847721247aaa469f96adef6b218a1ba5896cde91664cc819ba33115e3cc309e72140841ca3 +SuiteSparse.v7.8.0+0.x86_64-unknown-freebsd.tar.gz/md5/a50c69142a42c14edac4ce94b86b138a +SuiteSparse.v7.8.0+0.x86_64-unknown-freebsd.tar.gz/sha512/963be0dccd1a594df08fe5135ef4ac13e1d707841c3e97d31ba5477d0d6ec26bad9be1c52d9fd78f199740a53950353adbdd767469f3bf01ea1e3ee843eb6c1a +SuiteSparse.v7.8.0+0.x86_64-w64-mingw32.tar.gz/md5/7ca11ba89bd09183cc5a9320d6e8a4a7 +SuiteSparse.v7.8.0+0.x86_64-w64-mingw32.tar.gz/sha512/e1d5def1103bbf0bb29c08cdd3bf21ba60456353694985c66f8e55a31d54a32c5b891e56e1ffe30f9e1223c49283d267e483e2f1b999f566099c239b3eed1d78 diff --git a/deps/libsuitesparse.version b/deps/libsuitesparse.version index 3131908a4a298..6f841190cebc7 100644 --- a/deps/libsuitesparse.version +++ b/deps/libsuitesparse.version @@ -4,5 +4,5 @@ LIBSUITESPARSE_JLL_NAME := SuiteSparse ## source build -LIBSUITESPARSE_VER := 7.7.0 -LIBSUITESPARSE_SHA1=13806726cbf470914d012d132a85aea1aff9ee77 +LIBSUITESPARSE_VER := 7.8.0 +LIBSUITESPARSE_SHA1=58e6558408f6a51c08e35a5557d5e68cae32147e diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 2dc7bd8f5b3b9..019306a3e9f65 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 55976a6e4f883a32e3d3658af50c49879b98fce0 +SPARSEARRAYS_SHA1 = 0dd8d45d55b305458d0d3d3451057589b684f72f SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 diff --git a/stdlib/SuiteSparse_jll/Project.toml b/stdlib/SuiteSparse_jll/Project.toml index 314208ffc344c..39b8447138a2d 100644 --- a/stdlib/SuiteSparse_jll/Project.toml +++ b/stdlib/SuiteSparse_jll/Project.toml @@ -1,6 +1,6 @@ name = "SuiteSparse_jll" uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" +version = "7.8.0+0" [deps] libblastrampoline_jll = "8e850b90-86db-534c-a0d3-1478176c7d93" @@ -8,7 +8,7 @@ Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] -julia = "1.11" +julia = "1.12" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From 96fd25a764c316e9e09dfd7de8e84d854a73b2cd Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 12 Aug 2024 23:07:40 -0400 Subject: [PATCH 060/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?JuliaSyntaxHighlighting=20stdlib=20from=2004b2323=20to=20b89dd9?= =?UTF-8?q?9=20(#55474)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: JuliaSyntaxHighlighting URL: https://github.com/julialang/JuliaSyntaxHighlighting.jl.git Stdlib branch: main Julia branch: master Old commit: 04b2323 New commit: b89dd99 Julia version: 1.12.0-DEV JuliaSyntaxHighlighting version: 1.12.0 Bump invoked by: @tecosaur Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/julialang/JuliaSyntaxHighlighting.jl/compare/04b2323c41f6422464c838fe9045700e9ee75e95...b89dd99db56700c47434df6106b6c6afd1c9ed01 ``` $ git log --oneline 04b2323..b89dd99 b89dd99 Actually use the syntax_errors argument fee6aa5 Use a mutable type instead of ref fields ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/JuliaSyntaxHighlighting.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 diff --git a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 deleted file mode 100644 index 518e2705544ed..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -956fe26df1daca727ec15bfbc175584f diff --git a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 deleted file mode 100644 index 786dd666f2927..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-04b2323c41f6422464c838fe9045700e9ee75e95.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -20c7990134634dd252909dfa2c43f7b77d427a77f1b726eefdc47781fc3ad46152e81e612d4091541ffb32323154cb5a696157cd24d7a71087d5883720e03728 diff --git a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 new file mode 100644 index 0000000000000..cbcb8097d1673 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 @@ -0,0 +1 @@ +3dc1387ed88ba3c0df04d05a86d804d0 diff --git a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 new file mode 100644 index 0000000000000..2e58061d16058 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 @@ -0,0 +1 @@ +fe30ed73b257e6928097cb7baca5b82a9a60b2f9b9f219fbcf570c5ed513447f0fda2a48da06b57e381516a69278f7f8519764d00e9e4fb5683a5411e245ef45 diff --git a/stdlib/JuliaSyntaxHighlighting.version b/stdlib/JuliaSyntaxHighlighting.version index b076cfa26b5aa..280db66afe5f9 100644 --- a/stdlib/JuliaSyntaxHighlighting.version +++ b/stdlib/JuliaSyntaxHighlighting.version @@ -1,4 +1,4 @@ JULIASYNTAXHIGHLIGHTING_BRANCH = main -JULIASYNTAXHIGHLIGHTING_SHA1 = 04b2323c41f6422464c838fe9045700e9ee75e95 +JULIASYNTAXHIGHLIGHTING_SHA1 = b89dd99db56700c47434df6106b6c6afd1c9ed01 JULIASYNTAXHIGHLIGHTING_GIT_URL := https://github.com/julialang/JuliaSyntaxHighlighting.jl.git JULIASYNTAXHIGHLIGHTING_TAR_URL = https://api.github.com/repos/julialang/JuliaSyntaxHighlighting.jl/tarball/$1 From 5eda5972269558cb811ec695e399eb14579535c8 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 13 Aug 2024 16:44:41 +0200 Subject: [PATCH 061/548] make the previous active module in the REPL a non-global (#55418) The intent of the active module seems to have been for it to be REPL specific but this global kind of breaks that. Co-authored-by: KristofferC --- stdlib/REPL/src/LineEdit.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 1f6a782a23397..5af03e0df9b6d 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -66,6 +66,7 @@ show(io::IO, x::Prompt) = show(io, string("Prompt(\"", prompt_string(x.prompt), mutable struct MIState interface::ModalInterface active_module::Module + previous_active_module::Module current_mode::TextInterface aborted::Bool mode_state::IdDict{TextInterface,ModeState} @@ -78,7 +79,7 @@ mutable struct MIState async_channel::Channel{Function} end -MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}()) +MIState(i, mod, c, a, m) = MIState(i, mod, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}()) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} @@ -1510,13 +1511,11 @@ end current_word_with_dots(s::MIState) = current_word_with_dots(buffer(s)) -previous_active_module::Module = Main - function activate_module(s::MIState) word = current_word_with_dots(s); empty = isempty(word) mod = if empty - previous_active_module + s.previous_active_module else try Base.Core.eval(Base.active_module(), Base.Meta.parse(word)) @@ -1532,7 +1531,7 @@ function activate_module(s::MIState) if Base.active_module() == Main || mod == Main # At least one needs to be Main. Disallows toggling between two non-Main modules because it's # otherwise hard to get back to Main - global previous_active_module = Base.active_module() + s.previous_active_module = Base.active_module() end REPL.activate(mod) edit_clear(s) From 881be64de02e690f0753fb404647d216d839153d Mon Sep 17 00:00:00 2001 From: matthias314 <56549971+matthias314@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:45:19 -0400 Subject: [PATCH 062/548] fix hierarchy level of "API reference" in `Dates` documentation (#55483) Currently, "API reference" is at the same level as "Dates" although it is a subsection of it. This looks particularly weird in the PDF version of the manual: Section 67 is "Dates" and Section 68 is "API reference". Note that I didn't change the nesting level of the subsection "Constants" at the end of the file. As a result, it is now at the same level as "Dates and Time Types" and "Dates Functions". Before it was a subsection of the latter, which appears wrong to me. --- stdlib/Dates/docs/src/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index 545dbd90775df..38b4f7ae86d29 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -684,9 +684,9 @@ value in the days field is uncertain. See the [API reference](@ref stdlib-dates-api) for additional information on methods exported from the `Dates` module. -# [API reference](@id stdlib-dates-api) +## [API reference](@id stdlib-dates-api) -## Dates and Time Types +### Dates and Time Types ```@docs Dates.Period @@ -701,7 +701,7 @@ Dates.TimeZone Dates.UTC ``` -## Dates Functions +### Dates Functions ```@docs Dates.DateTime(::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64, ::Int64) @@ -730,7 +730,7 @@ Dates.now(::Type{Dates.UTC}) Base.eps(::Union{Type{DateTime}, Type{Date}, Type{Time}, TimeType}) ``` -### Accessor Functions +#### Accessor Functions ```@docs Dates.year @@ -758,7 +758,7 @@ Dates.monthday Dates.yearmonthday ``` -### Query Functions +#### Query Functions ```@docs Dates.dayname @@ -777,7 +777,7 @@ Dates.quarterofyear Dates.dayofquarter ``` -### Adjuster Functions +#### Adjuster Functions ```@docs Base.trunc(::Dates.TimeType, ::Type{Dates.Period}) @@ -797,7 +797,7 @@ Dates.tonext(::Function, ::Dates.TimeType) Dates.toprev(::Function, ::Dates.TimeType) ``` -### Periods +#### Periods ```@docs Dates.Period(::Any) @@ -808,7 +808,7 @@ Dates.default Dates.periods ``` -### Rounding Functions +#### Rounding Functions `Date` and `DateTime` values can be rounded to a specified resolution (e.g., 1 month or 15 minutes) with `floor`, `ceil`, or `round`. @@ -837,7 +837,7 @@ Dates.date2epochdays Dates.datetime2epochms ``` -### Conversion Functions +#### Conversion Functions ```@docs Dates.today From b7aa5e37402e2a0c81f9f2b80e89cf0f85ff6da6 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Tue, 13 Aug 2024 11:46:04 -0400 Subject: [PATCH 063/548] simplify complex atanh and remove singularity perturbation (#55268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes https://github.com/JuliaLang/julia/issues/55266, and use `inv(z)` rather than `1/z` and use `muladd` in a couple places. --------- Co-authored-by: Mosè Giordano --- base/complex.jl | 17 +++++++---------- test/complex.jl | 6 ++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/base/complex.jl b/base/complex.jl index 8ac126d2c6532..095c842795d38 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -1037,24 +1037,22 @@ end function atanh(z::Complex{T}) where T z = float(z) Tf = float(T) - Ω = prevfloat(typemax(Tf)) - θ = sqrt(Ω)/4 - ρ = 1/θ x, y = reim(z) ax = abs(x) ay = abs(y) + θ = sqrt(floatmax(Tf))/4 if ax > θ || ay > θ #Prevent overflow if isnan(y) if isinf(x) return Complex(copysign(zero(x),x), y) else - return Complex(real(1/z), y) + return Complex(real(inv(z)), y) end end if isinf(y) return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y)) end - return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) + return Complex(real(inv(z)), copysign(oftype(y,pi)/2, y)) end β = copysign(one(Tf), x) z *= β @@ -1064,16 +1062,15 @@ function atanh(z::Complex{T}) where T ξ = oftype(x, Inf) η = y else - ym = ay+ρ - ξ = log(sqrt(sqrt(4+y*y))/sqrt(ym)) - η = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2 + ξ = log(sqrt(sqrt(muladd(y, y, 4)))/sqrt(ay)) + η = copysign(oftype(y,pi)/2 + atan(ay/2), y)/2 end else #Normal case - ysq = (ay+ρ)^2 + ysq = ay^2 if x == 0 ξ = x else - ξ = log1p(4x/((1-x)^2 + ysq))/4 + ξ = log1p(4x/(muladd(1-x, 1-x, ysq)))/4 end η = angle(Complex((1-x)*(1+x)-ysq, 2y))/2 end diff --git a/test/complex.jl b/test/complex.jl index d798cfe16489c..63304652ee7d8 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -1215,3 +1215,9 @@ end @test !iseven(7+0im) && isodd(7+0im) @test !iseven(6+1im) && !isodd(7+1im) end + +@testset "issue #55266" begin + for T in (Float16, Float32, Float64) + @test isapprox(atanh(1+im*floatmin(T)), Complex{T}(atanh(1+im*big(floatmin(T))))) + end +end From 2a4e2b1d95c15654eea7965f123f72890b84e594 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 14 Aug 2024 11:34:06 -0400 Subject: [PATCH 064/548] fix Event to use normal Condition variable (#55441) ThreadSynchronizer is only for things that are very trivial, as there are a lot of things they are forbidden from doing (such as waiting for a Task to set it). Happened to notice while reviewing https://github.com/JuliaLang/julia/pull/55439#pullrequestreview-2231026949 that this was still using the pre-v1.2 style lock, which makes this mostly useless in v1.4+ --- base/lock.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index 7cbb023a78ee4..b69f3c5c03638 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -498,10 +498,10 @@ This provides an acquire & release memory ordering on notify/wait. The `autoreset` functionality and memory ordering guarantee requires at least Julia 1.8. """ mutable struct Event - const notify::ThreadSynchronizer + const notify::Threads.Condition const autoreset::Bool @atomic set::Bool - Event(autoreset::Bool=false) = new(ThreadSynchronizer(), autoreset, false) + Event(autoreset::Bool=false) = new(Threads.Condition(), autoreset, false) end function wait(e::Event) From c7309d05c16ba9a42a60eee3a03650d3d42a158b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 14 Aug 2024 17:05:05 -0400 Subject: [PATCH 065/548] subtyping: fast path for lhs union and rhs typevar (#55413) Fixes #55230 --- src/subtype.c | 16 +++++++++++- test/subtype.jl | 67 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 6f6520c5df980..4118bbeab649b 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1312,7 +1312,21 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e); static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { if (jl_is_uniontype(x)) { - if (x == y) return 1; + if (obviously_egal(x, y)) + return 1; + if (e->Runions.depth == 0 && jl_is_typevar(y) && !jl_has_free_typevars(x) && !jl_has_free_typevars(((jl_tvar_t*)y)->ub)) { + // Similar to fast path for repeated elements: if there have been no outer + // unions on the right, and the right side is a typevar, then we can handle the + // typevar first before picking a union element, under the theory that it may + // be easy to match or reject this whole union in comparing and setting the lb + // and ub of the variable binding, without needing to examine each element. + // However, if x contains any free typevars, then each element with a free + // typevar must be handled separately from the union of all elements without + // free typevars, since the typevars presence might lead to those elements + // getting eliminated (omit_bad_union) or degenerate (Union{Ptr{T}, Ptr}) or + // combined (Union{T, S} where {T, S <: T}). + return subtype_var((jl_tvar_t*)y, x, e, 1, param); + } x = pick_union_element(x, e, 0); } if (jl_is_uniontype(y)) { diff --git a/test/subtype.jl b/test/subtype.jl index af023ef8ca72f..7be869107b432 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -707,16 +707,17 @@ macro testintersect(a, b, result) a = esc(a) b = esc(b) result = esc(result) - Base.remove_linenums!(quote + # use a manual macrocall expression since Test will examine this __source__ value + return quote # test real intersect - @test $cmp(_type_intersect($a, $b), $result) - @test $cmp(_type_intersect($b, $a), $result) + $(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($a, $b), $result)))) + $(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($b, $a), $result)))) # test simplified intersect if !($result === Union{}) - @test typeintersect($a, $b) != Union{} - @test typeintersect($b, $a) != Union{} + $(Expr(:macrocall, :var"@test", __source__, :(typeintersect($a, $b) != Union{}))) + $(Expr(:macrocall, :var"@test", __source__, :(typeintersect($b, $a) != Union{}))) end - end) + end end abstract type IT4805_2{N, T} end @@ -2267,31 +2268,46 @@ let S = Tuple{Integer, U} where {II<:Array, U<:Tuple{Vararg{II, 1}}} @testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Any,Any,Vararg{Any,N}}}, Union{}) end +function equal_envs(env1, env2) + length(env1) == length(env2) || return false + for i = 1:length(env1) + a = env1[i] + b = env2[i] + if a isa TypeVar + if !(b isa TypeVar && a.name == b.name && a.lb == b.lb && a.ub == b.ub) + return false + end + elseif !(a == b) + return false + end + end + return true +end + # issue #43064 let - env_tuple(@nospecialize(x), @nospecialize(y)) = (intersection_env(x, y)[2]...,) - all_var(x::UnionAll) = (x.var, all_var(x.body)...) - all_var(x::DataType) = () + env_tuple(@nospecialize(x), @nospecialize(y)) = intersection_env(x, y)[2] TT0 = Tuple{Type{T},Union{Real,Missing,Nothing}} where {T} TT1 = Union{Type{Int8},Type{Int16}} @test env_tuple(Tuple{TT1,Missing}, TT0) === env_tuple(Tuple{TT1,Nothing}, TT0) === - env_tuple(Tuple{TT1,Int}, TT0) === all_var(TT0) + env_tuple(Tuple{TT1,Int}, TT0) === + Core.svec(TT0.var) TT0 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T1,T2} TT1 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T2,T1} TT2 = Tuple{Union{Int,Int8},Union{Int,Int8},Int} TT3 = Tuple{Int,Union{Int,Int8},Int} - @test env_tuple(TT2, TT0) === all_var(TT0) - @test env_tuple(TT2, TT1) === all_var(TT1) - @test env_tuple(TT3, TT0) === Base.setindex(all_var(TT0), Int, 1) - @test env_tuple(TT3, TT1) === Base.setindex(all_var(TT1), Int, 2) + @test equal_envs(env_tuple(TT2, TT0), Core.svec(TypeVar(:T1, Union{Int, Int8}), TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), TypeVar(:T1, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT3, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT3, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int)) TT0 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T1,T2} TT1 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T2,T1} TT2 = Tuple{Int,Union{Int,Int8},Int,Int} - @test env_tuple(TT2, TT0) === Base.setindex(all_var(TT0), Int, 1) - @test env_tuple(TT2, TT1) === Base.setindex(all_var(TT1), Int, 2) + @test equal_envs(env_tuple(TT2, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8}))) + @test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int)) end #issue #46735 @@ -2686,3 +2702,22 @@ let S = Tuple{Val{<:T}, Union{Int,T}} where {T}, @testintersect(S, T, !Union{}) @test !Base.has_free_typevars(typeintersect(S, T)) end + +#issue 55230 +let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}} + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Val} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Real} + @test !(T1 <: T2) + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{Val,Real}} + @test T1 <: T2 + T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{String,Real}} + @test !(T1 <: T2) + T2 = Tuple{<:Union{Val,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test T1 <: T2 + T2 = Tuple{<:Union{String,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any} + @test !(T1 <: T2) + @test Tuple{Union{Val{1},Val{2}}} <: Tuple{S} where {T, S<:Val{T}} +end From e1aefebe1e3c62339be4b46043625170ec538137 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Wed, 14 Aug 2024 21:23:43 -0500 Subject: [PATCH 066/548] Do not load `ScopedValues` with `using` (#55452) Stop loading `ScopedValues` with `using` so folks use `ScopedValues.with` or `using ScopedValues` rather than `Base.with`. Implements https://github.com/JuliaLang/julia/pull/55095#issuecomment-2272334437 ~Have to bump the StyledStrings stdlib to include https://github.com/JuliaLang/StyledStrings.jl/pull/80~ Done --------- Co-authored-by: Dilum Aluthge --- base/Base.jl | 1 - base/logging/logging.jl | 2 +- base/mpfr.jl | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 221ab90d8d2a9..082fb55a00ef0 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -424,7 +424,6 @@ include("weakkeydict.jl") # ScopedValues include("scopedvalues.jl") -using .ScopedValues # metaprogramming include("meta.jl") diff --git a/base/logging/logging.jl b/base/logging/logging.jl index c50f581db89ba..5cf3882a300ec 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -3,7 +3,7 @@ module CoreLogging import Base: isless, +, -, convert, show -import Base: ScopedValue, with, @with +import Base.ScopedValues: ScopedValue, with, @with export AbstractLogger, diff --git a/base/mpfr.jl b/base/mpfr.jl index ed3ea5937ce87..d393469aa26a1 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -109,9 +109,9 @@ end tie_breaker_is_to_even(::MPFRRoundingMode) = true const ROUNDING_MODE = Ref{MPFRRoundingMode}(MPFRRoundNearest) -const CURRENT_ROUNDING_MODE = Base.ScopedValue{MPFRRoundingMode}() +const CURRENT_ROUNDING_MODE = Base.ScopedValues.ScopedValue{MPFRRoundingMode}() const DEFAULT_PRECISION = Ref{Clong}(256) -const CURRENT_PRECISION = Base.ScopedValue{Clong}() +const CURRENT_PRECISION = Base.ScopedValues.ScopedValue{Clong}() # Basic type and initialization definitions # Warning: the constants are MPFR implementation details from @@ -162,7 +162,7 @@ significand_limb_count(x::BigFloat) = div(sizeof(x._d), sizeof(Limb), RoundToZer rounding_raw(::Type{BigFloat}) = something(Base.ScopedValues.get(CURRENT_ROUNDING_MODE), ROUNDING_MODE[]) setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r function setrounding_raw(f::Function, ::Type{BigFloat}, r::MPFRRoundingMode) - Base.@with(CURRENT_ROUNDING_MODE => r, f()) + Base.ScopedValues.@with(CURRENT_ROUNDING_MODE => r, f()) end @@ -1109,7 +1109,7 @@ Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by The `base` keyword requires at least Julia 1.8. """ function setprecision(f::Function, ::Type{BigFloat}, prec::Integer; base::Integer=2) - Base.@with(CURRENT_PRECISION => _convert_precision_from_base(prec, base), f()) + Base.ScopedValues.@with(CURRENT_PRECISION => _convert_precision_from_base(prec, base), f()) end setprecision(f::Function, prec::Integer; base::Integer=2) = setprecision(f, BigFloat, prec; base) From b4ebb0018e4dcfbbb9c840fb7097e7d7a5e0ad5e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 15 Aug 2024 07:34:30 -0400 Subject: [PATCH 067/548] build: add missing dependencies for expmap (#55492) I was confused why https://github.com/JuliaLang/julia/issues/49121 was re-occuring locally, until I noticed this file was not getting rebuilt. --- cli/Makefile | 2 +- src/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/Makefile b/cli/Makefile index bbe722f6f4816..7b8d3587f5386 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -152,7 +152,7 @@ $(build_bindir)/julia$(EXE): $(EXE_OBJS) $(build_shlibdir)/libjulia.$(SHLIB_EXT) $(build_bindir)/julia-debug$(EXE): $(EXE_DOBJS) $(build_shlibdir)/libjulia-debug.$(SHLIB_EXT) | $(build_bindir) @$(call PRINT_LINK, $(CC) $(LOADER_CFLAGS) $(DEBUGFLAGS) $(EXE_DOBJS) -o $@ $(LOADER_LDFLAGS) $(RPATH) -ljulia-debug) -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION sed <'$<' >'$@' -e 's/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/' clean: | $(CLEAN_TARGETS) diff --git a/src/Makefile b/src/Makefile index 6f78f4a8b6aa1..52e673aa6cc1a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -385,7 +385,7 @@ $(BUILDDIR)/julia_version.h: $(JULIAHOME)/VERSION CXXLD = $(CXX) -shared -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION $(LLVM_CONFIG_ABSOLUTE) sed <'$<' >'$@' -e "s/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/" \ -e "s/@LLVM_SHLIB_SYMBOL_VERSION@/$(LLVM_SHLIB_SYMBOL_VERSION)/" From 67c1723ffbb1ab31d15e72a61bf8856e83079311 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 15 Aug 2024 08:57:26 -0400 Subject: [PATCH 068/548] handle async termination better (#55440) Fixes #55235 Disables the assertion failure in the scheduler, so that we are more likely to be able to report the underlying failure and run atexit handlers successfully. This should clean up some of the error messages that occur on timeout. ``` julia> sleep(5) ^\ [89829] signal 3: Quit: 3 in expression starting at REPL[1]:1 kevent at /usr/lib/system/libsystem_kernel.dylib (unknown line) unknown function (ip: 0x0) Allocations: 830502 (Pool: 830353; Big: 149); GC: 1 Quit: 3 ``` --- base/Base.jl | 2 +- base/condition.jl | 2 +- base/initdefs.jl | 5 +++++ base/stream.jl | 4 ++-- base/task.jl | 34 +++++++++++++++++++--------------- src/scheduler.c | 15 +++++++++++++++ src/signal-handling.c | 15 ++++++++++----- stdlib/Sockets/src/Sockets.jl | 2 +- stdlib/Sockets/src/addrinfo.jl | 4 ++-- 9 files changed, 56 insertions(+), 27 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 082fb55a00ef0..081426fa94d67 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -626,7 +626,7 @@ function start_profile_listener() # this will prompt any ongoing or pending event to flush also close(cond) # error-propagation is not needed, since the errormonitor will handle printing that better - _wait(t) + t === current_task() || _wait(t) end finalizer(cond) do c # if something goes south, still make sure we aren't keeping a reference in C to this diff --git a/base/condition.jl b/base/condition.jl index 52781f348eb0d..bc14b17b3ac6b 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -138,7 +138,7 @@ function wait(c::GenericCondition; first::Bool=false) try return wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) rethrow() finally relockall(c.lock, token) diff --git a/base/initdefs.jl b/base/initdefs.jl index aa2ea67528da9..707c96a2444d6 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -438,6 +438,11 @@ function atexit(f::Function) end function _atexit(exitcode::Cint) + # this current task shouldn't be scheduled anywhere, but if it was (because + # this exit came from a signal for example), then try to clear that state + # to minimize scheduler issues later + ct = current_task() + q = ct.queue; q === nothing || list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) # Don't hold the lock around the iteration, just in case any other thread executing in # parallel tries to register a new atexit hook while this is running. We don't want to # block that thread from proceeding, and we can allow it to register its hook which we diff --git a/base/stream.jl b/base/stream.jl index a45307b883da8..93aeead79eb9c 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -462,7 +462,7 @@ function closewrite(s::LibuvStream) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we won't get spurious notifications later @@ -1076,7 +1076,7 @@ function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later diff --git a/base/task.jl b/base/task.jl index ae99a71585c85..5e4af6747f128 100644 --- a/base/task.jl +++ b/base/task.jl @@ -320,6 +320,7 @@ end # just wait for a task to be done, no error propagation function _wait(t::Task) + t === current_task() && Core.throw(ConcurrencyViolationError("deadlock detected: cannot wait on current task")) if !istaskdone(t) donenotify = t.donenotify::ThreadSynchronizer lock(donenotify) @@ -374,7 +375,6 @@ in an error, thrown as a [`TaskFailedException`](@ref) which wraps the failed ta Throws a `ConcurrencyViolationError` if `t` is the currently running task, to prevent deadlocks. """ function wait(t::Task; throw=true) - t === current_task() && Core.throw(ConcurrencyViolationError("deadlock detected: cannot wait on current task")) _wait(t) if throw && istaskfailed(t) Core.throw(TaskFailedException(t)) @@ -813,12 +813,15 @@ macro sync_add(expr) end end -throwto_repl_task(@nospecialize val) = throwto(getfield(active_repl_backend, :backend_task)::Task, val) - -function is_repl_running() - return isdefined(Base, :active_repl_backend) && - (getfield(active_repl_backend, :backend_task)::Task)._state === task_state_runnable && - getfield(active_repl_backend, :in_eval) +function repl_backend_task() + @isdefined(active_repl_backend) || return + backend = active_repl_backend + isdefined(backend, :backend_task) || return + backend_task = getfield(active_repl_backend, :backend_task)::Task + if backend_task._state === task_state_runnable && getfield(backend, :in_eval) + return backend_task + end + return end # runtime system hook called when a task finishes @@ -842,8 +845,9 @@ function task_done_hook(t::Task) end if err && !handled && Threads.threadid() == 1 - if isa(result, InterruptException) && isempty(Workqueue) && is_repl_running() - throwto_repl_task(result) + if isa(result, InterruptException) && isempty(Workqueue) + backend = repl_backend_task() + backend isa Task && throwto(backend, result) end end # Clear sigatomic before waiting @@ -854,11 +858,11 @@ function task_done_hook(t::Task) # If an InterruptException happens while blocked in the event loop, try handing # the exception to the REPL task since the current task is done. # issue #19467 - if Threads.threadid() == 1 && isa(e, InterruptException) && isempty(Workqueue) && is_repl_running() - throwto_repl_task(e) - else - rethrow() + if Threads.threadid() == 1 && isa(e, InterruptException) && isempty(Workqueue) + backend = repl_backend_task() + backend isa Task && throwto(backend, e) end + rethrow() # this will terminate the program end end @@ -1032,7 +1036,7 @@ function schedule(t::Task, @nospecialize(arg); error=false) # schedule a task to be (re)started with the given value or exception t._state === task_state_runnable || Base.error("schedule: Task not runnable") if error - t.queue === nothing || Base.list_deletefirst!(t.queue::IntrusiveLinkedList{Task}, t) + q = t.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, t) setfield!(t, :result, arg) setfield!(t, :_isexception, true) else @@ -1056,7 +1060,7 @@ function yield() try wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) rethrow() end end diff --git a/src/scheduler.c b/src/scheduler.c index 3505e935afcf6..3cf97ba108873 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -199,6 +199,21 @@ static int sleep_check_after_threshold(uint64_t *start_cycles) JL_NOTSAFEPOINT return 0; } +void surprise_wakeup(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + // equivalent to wake_thread, without the assert on wasrunning + int8_t state = jl_atomic_load_relaxed(&ptls->sleep_check_state); + if (state == sleeping) { + if (jl_atomic_cmpswap_relaxed(&ptls->sleep_check_state, &state, not_sleeping)) { + // this notification will never be consumed, so we may have now + // introduced some inaccuracy into the count, but that is + // unavoidable with any asynchronous interruption + jl_atomic_fetch_add_relaxed(&n_threads_running, 1); + } + } +} + + static int set_not_sleeping(jl_ptls_t ptls) JL_NOTSAFEPOINT { if (jl_atomic_load_relaxed(&ptls->sleep_check_state) != not_sleeping) { diff --git a/src/signal-handling.c b/src/signal-handling.c index febf05b653662..6835f5fa364c5 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -427,6 +427,8 @@ void jl_show_sigill(void *_ctx) #endif } +void surprise_wakeup(jl_ptls_t ptls) JL_NOTSAFEPOINT; + // make it invalid for a task to return from this point to its stack // this is generally quite an foolish operation, but does free you up to do // arbitrary things on this stack now without worrying about corrupt state that @@ -439,15 +441,17 @@ void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT ct->eh = NULL; ct->world_age = 1; // Force all locks to drop. Is this a good idea? Of course not. But the alternative would probably deadlock instead of crashing. - small_arraylist_t *locks = &ct->ptls->locks; + jl_ptls_t ptls = ct->ptls; + small_arraylist_t *locks = &ptls->locks; for (size_t i = locks->len; i > 0; i--) jl_mutex_unlock_nogc((jl_mutex_t*)locks->items[i - 1]); locks->len = 0; - ct->ptls->in_pure_callback = 0; - ct->ptls->in_finalizer = 0; - ct->ptls->defer_signal = 0; + ptls->in_pure_callback = 0; + ptls->in_finalizer = 0; + ptls->defer_signal = 0; // forcibly exit GC (if we were in it) or safe into unsafe, without the mandatory safepoint - jl_atomic_store_release(&ct->ptls->gc_state, JL_GC_STATE_UNSAFE); + jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_UNSAFE); + surprise_wakeup(ptls); // allow continuing to use a Task that should have already died--unsafe necromancy! jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE); } @@ -461,6 +465,7 @@ void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *c size_t i, n = ct ? *bt_size : 0; if (sig) { // kill this task, so that we cannot get back to it accidentally (via an untimely ^C or jlbacktrace in jl_exit) + // and also resets the state of ct and ptls so that some code can run on this task again jl_task_frame_noreturn(ct); #ifndef _OS_WINDOWS_ sigset_t sset; diff --git a/stdlib/Sockets/src/Sockets.jl b/stdlib/Sockets/src/Sockets.jl index 5baf8826cc883..3c30b214305fb 100644 --- a/stdlib/Sockets/src/Sockets.jl +++ b/stdlib/Sockets/src/Sockets.jl @@ -450,7 +450,7 @@ function send(sock::UDPSocket, ipaddr::IPAddr, port::Integer, msg) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later diff --git a/stdlib/Sockets/src/addrinfo.jl b/stdlib/Sockets/src/addrinfo.jl index 4ee9e07a58430..866a1684c85a1 100644 --- a/stdlib/Sockets/src/addrinfo.jl +++ b/stdlib/Sockets/src/addrinfo.jl @@ -90,7 +90,7 @@ function getalladdrinfo(host::String) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later @@ -223,7 +223,7 @@ function getnameinfo(address::Union{IPv4, IPv6}) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later From 015c2cb60d968f8cd9474b3c3c26176a27e7e756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 16 Aug 2024 00:37:48 +0200 Subject: [PATCH 069/548] [OpenBLAS_jll] Upgrade to new build to fix bug in threads buffers (#55496) --- deps/checksums/openblas | 184 +++++++++--------- deps/openblas.mk | 7 +- ...enblas-memory-buffer-multi-threading.patch | 49 +++++ stdlib/OpenBLAS_jll/Project.toml | 2 +- 4 files changed, 148 insertions(+), 94 deletions(-) create mode 100644 deps/patches/openblas-memory-buffer-multi-threading.patch diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 51317261c82a0..08bd98646c24b 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,94 @@ -OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/e3edc449afa805b3744eb153460b681f -OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/4f619ae720bc2a55c6d7d53b78bf0a15f66c5b3474c968b367f41d71c759b39028817e3e7ba3cebc4ee06f2176578a5a1bd2be7cf2f1461a396c418292fcf782 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/e01dcbdbfd2c8f15d78efb0aa5673944 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/33dee7c48f981b218792e150aea506989b7bbacfd540ebd1fefb150af3c33eae62cd523c329ef8f37c0b56643d480e105ed82e46ec5b3f683e006d05dda717ee -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/43662cb933b2aab820bd210edd4e994a -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/520abb2521a4b9ae71c86dafc7de4155d51df09ff119a3b1d25a9bac3fb73aceaf38b7805888d4858b96e73c0d1cf80d8953b9db954df4d0e6c164894d07d715 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/5c693f190d26194353c1e096b40568bc -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/3c3e11ba038e59002d23e885e22129dda13453469dad79f39f9cddceadbf1d39e61487894f5112b2fcb5265cd98075103d99eff2a83f79407aafa545b03e9f9c -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/2892710a8545b4573014024222bb8dff -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/6a628c9f7eab2a34198028846a6aec7bb13af4425e1b73ba5b58d326c1eb0741b5dc08fff3db565c92cbc0e2718b62fa6dedac5fa0bdb2f35561663f36f4dfbe -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/fbec5f47685d4bb36956cd4aee34f1e5 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/ac69a9ed17900de79c6da0ff08a97f3397860de92ce1888f77c8c8fada08fab15fff1b19868c95865ad4a387701c2ffe74e695d6949d8ba02534f91aca2a5ca3 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/6a0a1a1cad6452ac687e24147128f027 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/0ea2b7f829b4e406276911db743706b17d7902899d4492e18b9676fed9b27d976d586e38505c52932e27f194c9806d6cb53182cb128baab41898605af7c346b5 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/md5/36f76f7588ad5bc48c2f68daee49da57 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/2184cac67657fb58afc42fff46069084ffbcbc67938e7e74e9e5a926cc83733c702cacf16ca320381f5bb1f219cbea764ae8cdb9c445f7224ac0cd0beab822ff -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/md5/ef8501cc6babf8be3b8b649da2a7c692 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/98543cfd34a185644cebc33cd82ebfb663c92f1fa8349121e6d34f86b1d10f4f37688b84b22182f9e29daa74664a469ddc67408827e8bc7fddb1a7311d918532 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/md5/598c07efb122e75e6e99ba7fc0c4fb4b -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/b7caa20a36d6806895f3efb02830017c3ca8037c5af3a29df00f9fe34945324c34181a945b1dbe8a8ca43c7f792667d7640c23b5c2fa4fd93564f1da78561190 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/md5/e7667d215442ac0db83969d41a678774 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/sha512/3b22dd658b5948d6867b3e57fe53976eef59339d2714709746098b96f13766d86e918a139929aa60672be91c50c7f739c5c0db372f07a71ae2447588db3685e4 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/md5/91d95572ce67a21d107b9fbcd3aba11d -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/sha512/7727d24fec0a333a943de3f9d6dd5c698e4f3b9099fd838b8b5652f6216f7b9fe4a2d8f014a4f0b3b7ad7fe05b81a9079e570454d456f0462e7d04f66e264ecb -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/md5/2d56a5cfeae0a6afa2d2b8efa1ab22c5 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/sha512/e81207bee11f89669837db08b57b63813056730f68412345421539399c12a675ed01942558ebd42045959c29a2b774a75f28c4a6b14549b7373b54a5e93e8933 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/5b741b5fec8b564ba8b24435b5d885ae -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/97e72a4b9b39d4889c4e36eff85186fcbabfff2930185e69b3c259b43cdbaa5fab51bf0ed4024d1ddd3c780edaf501c4f5f7534e0a2edb802d580987fbd026ab -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/d5f059fc960b7dc84ee4b92c431d87b4 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/f1e8f31f89274ff5b466931f8941de55fb27d2ee773d87e7e0b992deeec7d921358b10520cc0f47975022536b5e9d7b1cc9acc481b95f83cc2096d7cb7494616 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/24c915a3156983745662ff99e5d1b040 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/47fb327281c903eebb0a261458fc10f09bac317d7e135dff72a112c868a2525fa542f93f22da083c13454fc241352d39a8e8463085685aa77e055ffcadf451c8 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/93041d21ad3f95e6d9cbac6cd6730363 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/17cd2302860eeee884f97d87eaf0ad12cdc7361734cfaa77b543119c58103a5da107b478e7ecfcb135d2e5beffd6a3907108b2911a095a3cbc1d16f32371ac1b -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/98a8c6c8c80c11e8b6d127959c9b3414 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/d26a51785391d81efcaefcf647fcf0348ad68ff01845ab3547778903d2ab5c5c1cdb2a562ae5cf7f12878f3345c46321719ea82fb87ef655d303a4c0c9803377 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/41f7fdc10d8cab0965da95e00e2269ba -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/0a47ef77f9b2b70f424e00957f676c53d19c5dffbbcd5a743ab24bbc8608c5e8ad3cb3afefd8cab60a3c51970a63dd47c97868ecc0ef3532b83278c41a8daf96 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/8453e7a5e5285e770fde7592582bc0e2 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/a37edfe68c85a6312d7918f1b58d6ac4bafc13081dbd327c830631913350a54bbf8bea57115b4f242d5f93c6b0a8f4995b5ef544a0de06e76c66287ff092e74c -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/6df24890be7a4899f35a2949f9f21d65 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/66601632f91689fe72afecd2e4d4ef3ad3b7653874228d5509c7732f2e6d63640f35d176ce2219b732632e0daeb76dc3ba11d3e776639985359b21b313056883 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/d35df8d213c55bc1f9f765e0ba8c7b4e -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/5d5de73150a2e796dc29425dbf415ff7aa443264d767d4e688de07335961ee39835c94b7d68900d49b70bf3ac08d356f3ae00c6d651eed64e504b02c9351edcb -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/384f9173b3804e6f9c73bcde9dacb545 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/64d3abeca401cee06575915161458408e9fb51e26f759129e1c7a9c27f68729d66e75f0654b977e842844650698c4b1627a18e495d91202a8c0483ef1b35bafc -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/0bd296248e1337fac054b9e0993fea82 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/fdb9ce296228f92c112bbeb346a2900a8d5a73e21a313a217cf7135fd77484cdeed53c86382ee5550f1b624eb6ed99d06b739229add7364217ca68fefedd04c4 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/524a2481331fdd3933f06b40e63433f1 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/06831cc855e9801dbf2248a0da123c345b6731c830f39d3166b8d8e7de8d649b6d9900e534ec6c1113a227203f6a9aa8171fcf548cfd56a4a67b6037c105ecf5 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/82f2b8f31f7b718f6ea743c996acbe4d -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/fd1ccab964ae9410238d29b38cfe8c2ccca3fda3d78b4294bb4a54ab8abfd6bdaa80cadc0aeadf054bf99138c5dc3cac9370920b0b16cb8793630ab21d5bf667 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/b91add21ba0e2a0f28a9e4d347111cc3 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/8ed1d9e327529ee067d975c5c96dac3eabab5a88ed7b1b6e1b030f96bbd2418e3173cacd70e9976d619245757f2a34cc9527aafef1626fd288f14918c9b13eaa -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff +OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/md5/312aa603d089d680205dad7d5da58195 +OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/ffb0069561f52f8ac2f8affe937a00592e0c5d75c6d64bb0d5c93d1c925c93a46b763638031c88818b9dcef4a7b149ee3f15792a812e87f57a8ad086604164c4 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/md5/7c43d9e9ac07820130a3d5faefdef882 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/3ade0f098796148c37b118f9c052bad4e40431b4792f001043f040f8b1e4b7c3bae512f56ea21e6c0111246b2200e7720fe720a56a19dd11d1fba789344f29e3 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/md5/cd2fe87dac703c8bfa25406aa732b88a +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/2aea68bd8f1db2ac920951c8d9a47ce8c071f3736ee8aad8d185a09be25234a0ffd11b9f9640015b82770ba3b3fad9aa511cc43501c1bb5a3a44f1fb7ccd5692 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/md5/e3db2bf2f1f38aeee8530c78f3ec049a +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/a0ccb92e818650ac3cbc292d5af1a000ee9b123953cc3eb16e2479e926af3f2be0ed9858e3c0c1075b1b9dd70ec1e51b9dce2c9d45b999d296aa050d257a3cb1 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/md5/5bb605738930037259e773ebdb4a7041 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/sha512/967e0f33be7b743d9617627a947a802286962a46c7c3b2418aaa1504cffc5f311b01e1702b35ded18ae3686b1914c6085213b03fa8a51e0a7ca16dc4cfee8504 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/md5/ce175e82b9c6597c546552e79a43f934 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/sha512/8ff5dff293d9786fc4f541b209b35afcbe325c13ddd0f9c8f9bfca8ba5c318c7890152260a5441b9e9088751ce03b1ff8f0f5d6fd4f142fae34bdb7390d1952c +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/md5/cae6aabbdccf31fb78b234785b52d48a +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/sha512/ac842023e5db243fcfada22adca051bd2ffa04fca496454539931eede159e5d0490d444c338684c2d178c3367b23b8f3d76c544e30f1897bbed181f56237619f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/md5/53087cc770708c57d2654fd0095b64df +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/sha512/90961448ae40b0445bf881d0815aec54d2096ad235dc8e3db8d698a72961ef9a97e7fcd08f79c83cd1f7c5a341464f52a90351d927d5f1c3e9c8ee32b17970db +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/md5/ee910e19faa961bde11fdf90c211df9d +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/sha512/f5cfecfe965991cfd7843eff71efa71d6842058565bb63657e909b2942e58a8c7506aa66335308961e59f392da16e1177d79542ae509795566a14122f67a1782 +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/md5/fe52ba7ca8e16f37aa04b79248e0471d +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/sha512/79b5108886d60f12424709a841e359dc1cf23cef21bb0ee6d1a48043ac48a35dac1637e43c8ebf3f2e10dd34721993a7a12c5776f2975dd5bd7b6e29e1a9adc3 +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/md5/88d8ff421d29456f1d7670ceaf8867ca +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/sha512/91c1bd8142845d11fecba87a719315a14218e3863955ddd2ed82cecd4a2c177a48c660b6aac374ee9a11008245c0ced1bae70eaf5a1a6e3114db02e09a96396f +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/md5/3035066a53032b551e49f56b323e941d +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/sha512/f218e152a1c92bd374599814612add8010aedc78113cbe06465e8a1ee7f66892bb654cad687aa55555e74f3a65d74608692d41c9f0ce6c0bc63475ef62ab55b7 +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/md5/f7cf36ac9a0cbb535952ec73f2e6c9ea +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/sha512/00ab052d9fa4a72a640545782019f24ed6017b36aa89c5e659ce73b1e821817f560c09f71b26c027c0a05bd13567c71a6d7f5995d1c39ab233bec56cd3a7fd9e +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/md5/b65414bb15539e5aa2f5f1c7984edb94 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/sha512/847ada020bb92fe6ea81dfffaf855707a529c9c0f7e246e802b9521e5c7d4aa36104d04279c09a905a797184cdf05a6fabf84711b7661ecb14e9ac2fba251f61 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/md5/0b626ebb8b3fc49b946723a9a2a21a91 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/sha512/b5bba23878399fc1ff20abc2e2eb4acb9691ce982f290e33384732452774a0b447bd0fb01ee696d10ad8b03d99eec905662af92bd3b499d9fe6db419e05d2573 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/md5/27c24775af446a44a72a28ffd197696d +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/2af8caa33bee88efff84653f3932b04e8fd4aabb1bf16d49fa73657b0ec13c9457fde7ab3f953fc9b01da5c2841c3c9b588e3b0f559b89df0e6268468d1f7cc8 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/md5/414e701d918d5fba08a12de6979db4b5 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/949886d388b80e19b944d102852f2bb58ffa03c42e624986dd9dc076797c996634d4a8fc0f04544451d6848c2079969816979e1f68a999b2747e9dd5472be7a6 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/md5/29fcf62c0280cc10f91d22189a2e8de8 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/02e75d4ecf9cd922157a72c0ca2e713cf336b125df3982cd5f7cc4f2a04367ad4c2b1190ca2a0a9df8b639c7ebcfc9783066e99dd0b13acde7b02038391e8567 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/md5/147d5e8eb2ec78fc8a31bdb091fab001 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/2319eda568800c0b1f2d96a8a36c59b1bbd792c06de1d740aea3f1e49798242426ea8d10c100c42c3c281702e2b4f5b673b6ab5252b276d48542e875bcaa3094 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/md5/448857d9c4b2e95afc12a14c75b24055 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/3e7c8cd55e0b15a30992b1e0b48a6e2ae36fd9babf689fa5595c7de94aec401de1d7821d45a22bf14cd5c45c708bc8fa3511d34d732dadd4daaca3f49e200bdb +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/md5/3aaf417685b44e0e505208f7b31b981a +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/f7b1d123e48ede93fe624a79d9535a8915bfa3441d7a6f9c6643467027414c9f2538e299858ea98bbb49d4e6d385a6a491063cb1878ac3b0b3d6a8f7ff0a48df +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/md5/5723136deaaf4b2e5960fb0774943288 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/sha512/127ea8b2b0d8d4586a23a2b8ecbf148d512efe68626e89b0688c3c9e29ed9420b45ae86755c1467313c565f9f3835762051d7086a815b813dbe6e9eb05fb4be1 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/md5/80b1b9cf5346916edda653174a987aa2 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/sha512/77e1387ec969bbed4945d2a598a1cd04d258265c4b2d5c43af92118eb32e0c69e40619a20ea1835f277febcfea068b241343d44932afef832bdcfd2e9f618f0a +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/md5/44dcedf01c938d1a1c67dd3bc90ab61d +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/sha512/e490d49b8d41d73ab3e71aca8c691ca58704f0fc6930cbfcc203f97b8db8d83144bad597a2c53ff0c0c4f7c40316d975a1b589a3603873d508f6beeb75970c5b +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/0e8a7e88b54cb836292c289d1c456fa9 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/0e9b3af6839b9c41c950bb4d8b739f0243a890af7092ef9f3a00e4931f2acc3820afb78e40c7bfef716dcd3230c1d0acc7b0b37f30eb47441b476bd7540745e6 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/5fc47ad55780c99ef9cab7ef1b26d9c0 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/c531201e4abddd652efeb5801658f5c1e4891578f181e99d6e41fc0d3bc6347b82e5e928ff8a717ee1e75bb0a6a765260bf7c99fce44aa24c21f1c5a5e3c1e3b +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/dc127f3ab984b5d47b325d5701ab73cd +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/50850911703320894a2e1e996c5de4613b5f9e3012f5cbf591f3677799599c45d9cc4c42cf310bdc6ba91ef550e52f6424bbbabdf47f96748d4669d94e6b46a4 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/md5/f7a1fe86cefbf7d4f2608843c7833ca7 openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/sha512/5f6020e958967a12a3c5b18bde13331f9c0602bd073563f35cd7cec848c92b45f30ca362819b12cd16989c0e4641ee3e63db8322d1092f61b31ba2e4068dd7a7 diff --git a/deps/openblas.mk b/deps/openblas.mk index 1bc068d2859d9..affd1c7a7aa55 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -90,7 +90,12 @@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied: $(BUILDDIR)/$(OP patch -p1 -f < $(SRCDIR)/patches/openblas-winexit.patch echo 1 > $@ -$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-memory-buffer-multi-threading.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-memory-buffer-multi-threading.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-memory-buffer-multi-threading.patch-applied cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ patch -p1 -f < $(SRCDIR)/patches/openblas-ofast-power.patch echo 1 > $@ diff --git a/deps/patches/openblas-memory-buffer-multi-threading.patch b/deps/patches/openblas-memory-buffer-multi-threading.patch new file mode 100644 index 0000000000000..9693b5cf61597 --- /dev/null +++ b/deps/patches/openblas-memory-buffer-multi-threading.patch @@ -0,0 +1,49 @@ +From 23b5d66a86417a071bba9a96a0573192237981b6 Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Wed, 14 Aug 2024 10:35:44 +0200 +Subject: [PATCH 1/2] Ensure a memory buffer has been allocated for each thread + before invoking it + +--- + driver/others/blas_server.c | 2 ++ + 1 file changed, 2 insertions(+) + +From d24b3cf39392a99e81ed47a5f093fbd074d4b39b Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Thu, 15 Aug 2024 15:32:58 +0200 +Subject: [PATCH 2/2] properly fix buffer allocation and assignment + +--- + driver/others/blas_server.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) +diff --git a/driver/others/blas_server.c b/driver/others/blas_server.c +index 765511d8c7..b9a7674c17 100644 +--- a/driver/others/blas_server.c ++++ b/driver/others/blas_server.c +@@ -1076,6 +1076,8 @@ fprintf(STDERR, "Server[%2ld] Calculation started. Mode = 0x%03x M = %3ld N=%3l + main_status[cpu] = MAIN_RUNNING1; + #endif + ++if (buffer == NULL) blas_thread_buffer[cpu] = blas_memory_alloc(2); ++ + //For target LOONGSON3R5, applying an offset to the buffer is essential + //for minimizing cache conflicts and optimizing performance. + #if defined(ARCH_LOONGARCH64) && !defined(NO_AFFINITY) + +diff --git a/driver/others/blas_server.c b/driver/others/blas_server.c +index b9a7674c17..29f8a5e646 100644 +--- a/driver/others/blas_server.c ++++ b/driver/others/blas_server.c +@@ -1076,7 +1076,11 @@ fprintf(STDERR, "Server[%2ld] Calculation started. Mode = 0x%03x M = %3ld N=%3l + main_status[cpu] = MAIN_RUNNING1; + #endif + +-if (buffer == NULL) blas_thread_buffer[cpu] = blas_memory_alloc(2); ++if (buffer == NULL) { ++ blas_thread_buffer[cpu] = blas_memory_alloc(2); ++ buffer = blas_thread_buffer[cpu]; ++} ++ + + //For target LOONGSON3R5, applying an offset to the buffer is essential + //for minimizing cache conflicts and optimizing performance. diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index dfca282c74704..a9a1a04facff5 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.28+0" +version = "0.3.28+2" [deps] # See note in `src/OpenBLAS_jll.jl` about this dependency. From 6916eb742055b47a0c52de855f5d0ecd4d0769ef Mon Sep 17 00:00:00 2001 From: Zentrik Date: Fri, 16 Aug 2024 00:07:21 +0100 Subject: [PATCH 070/548] Use same toolchain throughout pgo+bolt build (#55460) Also, I added the pgo flags to the `finish_stage2` target in case they are or become useful there. --- contrib/pgo-lto-bolt/Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/contrib/pgo-lto-bolt/Makefile b/contrib/pgo-lto-bolt/Makefile index fa88cdcd3d6a7..2114b14991184 100644 --- a/contrib/pgo-lto-bolt/Makefile +++ b/contrib/pgo-lto-bolt/Makefile @@ -83,7 +83,7 @@ TOOLCHAIN_FLAGS = $\ "RANLIB=$(STAGE0_TOOLS)llvm-ranlib" $\ "CFLAGS+=$(PGO_CFLAGS)" $\ "CXXFLAGS+=$(PGO_CXXFLAGS)" $\ - "LDFLAGS+=$(PGO_LDFLAGS)" + "LDFLAGS+=-fuse-ld=lld $(PGO_LDFLAGS)" $(STAGE0_BUILD) $(STAGE1_BUILD) $(STAGE2_BUILD): $(MAKE) -C $(JULIA_ROOT) O=$@ configure @@ -99,7 +99,7 @@ stage0: | $(STAGE0_BUILD) $(STAGE1_BUILD): stage0 stage1: PGO_CFLAGS:=-fprofile-generate=$(PGO_PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) stage1: PGO_CXXFLAGS:=-fprofile-generate=$(PGO_PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) -stage1: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-generate=$(PGO_PROFILE_DIR) +stage1: PGO_LDFLAGS:=-flto=thin -fprofile-generate=$(PGO_PROFILE_DIR) stage1: export USE_BINARYBUILDER_LLVM=0 stage1: | $(STAGE1_BUILD) $(MAKE) -C $(STAGE1_BUILD) $(TOOLCHAIN_FLAGS) && touch $@ @@ -107,7 +107,7 @@ stage1: | $(STAGE1_BUILD) stage2: PGO_CFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) stage2: PGO_CXXFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) -stage2: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe +stage2: PGO_LDFLAGS:=-flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe stage2: export USE_BINARYBUILDER_LLVM=0 stage2: $(PGO_PROFILE_FILE) | $(STAGE2_BUILD) $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) $(BOLT_FLAGS) julia-src-release julia-symlink julia-libccalltest \ @@ -135,10 +135,13 @@ bolt_instrument: copy_originals # We don't want to rebuild julia-src as then we lose the bolt instrumentation # So we have to manually build the sysimage and package image +finish_stage2: PGO_CFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) +finish_stage2: PGO_CXXFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) +finish_stage2: PGO_LDFLAGS:=-flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe finish_stage2: stage2 - $(MAKE) -C $(STAGE2_BUILD) julia-base-cache && \ - $(MAKE) -C $(STAGE2_BUILD) -f sysimage.mk sysimg-release && \ - $(MAKE) -C $(STAGE2_BUILD) -f pkgimage.mk release + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) julia-base-cache && \ + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f sysimage.mk sysimg-release && \ + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f pkgimage.mk release merge_data: bolt_instrument for file in $(FILES_TO_OPTIMIZE); do \ From 5230d27de950165475892acfabded59713c8cd3e Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Fri, 16 Aug 2024 03:33:22 -0400 Subject: [PATCH 071/548] Fix push! for OffsetVectors, add tests for push! and append! on AbstractVector (#55480) Per https://github.com/JuliaLang/julia/pull/55470#discussion_r1714000529, the `push!(::AbstractArray, ...)` array implementation assumed one-based indexing and did not account for an `OffsetVector` scenario. Here we add tests for `push!(::AbstractArray, ...)` and `append(::AbstractArray, ...)` including using `@invoke` to test the effect on `OffsetVector`. cc: @fredrikekre --- base/abstractarray.jl | 7 ++++--- test/abstractarray.jl | 18 +++++++++++++++++- test/offsetarray.jl | 29 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 3f8886e14940c..754ab20660ab8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3532,7 +3532,7 @@ function push!(a::AbstractVector{T}, item) where T itemT = item isa T ? item : convert(T, item)::T new_length = length(a) + 1 resize!(a, new_length) - a[new_length] = itemT + a[end] = itemT return a end @@ -3540,7 +3540,7 @@ end function push!(a::AbstractVector{Any}, @nospecialize x) new_length = length(a) + 1 resize!(a, new_length) - a[new_length] = x + a[end] = x return a end function push!(a::AbstractVector{Any}, @nospecialize x...) @@ -3548,8 +3548,9 @@ function push!(a::AbstractVector{Any}, @nospecialize x...) na = length(a) nx = length(x) resize!(a, na + nx) + e = lastindex(a) - nx for i = 1:nx - a[na+i] = x[i] + a[e+i] = x[i] end return a end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8b4a1d9113940..f655d9abe423f 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1437,14 +1437,30 @@ using .Main.OffsetArrays end @testset "Check push!($a, $args...)" for - a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) orig = copy(a) push!(a, args...) @test length(a) == length(orig) + length(args) + @test a[axes(orig,1)] == orig @test all(a[end-length(args)+1:end] .== args) end +@testset "Check append!($a, $args)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) + orig = copy(a) + append!(a, args) + @test length(a) == length(orig) + length(args) + @test a[axes(orig,1)] == orig + @test all(a[end-length(args)+1:end] .== args) +end + +@testset "Check sizehint!($a)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)) + @test sizehint!(a, 10) === a +end + @testset "splatting into hvcat" begin t = (1, 2) @test [t...; 3 4] == [1 2; 3 4] diff --git a/test/offsetarray.jl b/test/offsetarray.jl index c50f38c382385..5ee918e85faf7 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -383,6 +383,18 @@ v2 = copy(v) @test v2[end-1] == 2 @test v2[end] == 1 +# push!(v::AbstractVector, x...) +v2 = copy(v) +@test @invoke(push!(v2::AbstractVector, 3)) === v2 +@test v2[axes(v,1)] == v +@test v2[end] == 3 +@test v2[begin] == v[begin] == v[-2] +v2 = copy(v) +@test @invoke(push!(v2::AbstractVector, 5, 6)) == v2 +@test v2[axes(v,1)] == v +@test v2[end-1] == 5 +@test v2[end] == 6 + # append! from array v2 = copy(v) @test append!(v2, [2, 1]) === v2 @@ -399,6 +411,23 @@ v2 = copy(v) @test v2[axes(v, 1)] == v @test v2[lastindex(v)+1:end] == [2, 1] +# append!(::AbstractVector, ...) +# append! from array +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, [2, 1]::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] +# append! from HasLength iterator +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, (v for v in [2, 1])::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] +# append! from SizeUnknown iterator +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, (v for v in [2, 1] if true)::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] + # other functions v = OffsetArray(v0, (-3,)) @test lastindex(v) == 1 From ddecfe74ffdfbbdfe0c2e78cee059470982e0ab9 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 16 Aug 2024 14:14:36 -0400 Subject: [PATCH 072/548] fix overlapping definitions of `Base.active_module` and `REPL.active_module` (#55316) also avoid calling `active_module` from low-level printing functions fix #54888 --- base/Enums.jl | 2 +- base/show.jl | 44 +++++++++++++++++------------------------ stdlib/REPL/src/REPL.jl | 21 ++++++++------------ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 6e9efd8ccde38..d4094945853ec 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -44,7 +44,7 @@ Base.print(io::IO, x::Enum) = print(io, _symbol(x)) function Base.show(io::IO, x::Enum) sym = _symbol(x) if !(get(io, :compact, false)::Bool) - from = get(io, :module, Base.active_module()) + from = get(io, :module, Main) def = parentmodule(typeof(x)) if from === nothing || !Base.isvisible(sym, def, from) show(io, def) diff --git a/base/show.jl b/base/show.jl index fa66a198aef4d..0a2976e7ebe42 100644 --- a/base/show.jl +++ b/base/show.jl @@ -514,24 +514,16 @@ function _show_default(io::IO, @nospecialize(x)) end function active_module() - REPL = REPL_MODULE_REF[] - REPL === Base && return Main - return invokelatest(REPL.active_module)::Module + if ccall(:jl_is_in_pure_context, Bool, ()) + error("active_module() should not be called from a pure context") + end + if !@isdefined(active_repl) || active_repl === nothing + return Main + end + return invokelatest(active_module, active_repl)::Module end -# Check if a particular symbol is exported from a standard library module -function is_exported_from_stdlib(name::Symbol, mod::Module) - !isdefined(mod, name) && return false - orig = getfield(mod, name) - while !(mod === Base || mod === Core) - activemod = active_module() - parent = parentmodule(mod) - if mod === activemod || mod === parent || parent === activemod - return false - end - mod = parent - end - return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig +module UsesCoreAndBaseOnly end function show_function(io::IO, f::Function, compact::Bool, fallback::Function) @@ -544,13 +536,13 @@ function show_function(io::IO, f::Function, compact::Bool, fallback::Function) print(io, mt.name) elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && getfield(mt.module, mt.name) === f - mod = active_module() - if is_exported_from_stdlib(mt.name, mt.module) || mt.module === mod - show_sym(io, mt.name) - else + # this used to call the removed internal function `is_exported_from_stdlib`, which effectively + # just checked for exports from Core and Base. + mod = get(io, :module, UsesCoreAndBaseOnly) + if !(isvisible(mt.name, mt.module, mod) || mt.module === mod) print(io, mt.module, ".") - show_sym(io, mt.name) end + show_sym(io, mt.name) else fallback(io, f) end @@ -737,9 +729,9 @@ end function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector) if !(get(io, :compact, false)::Bool) # Print module prefix unless alias is visible from module passed to - # IOContext. If :module is not set, default to Main (or current active module). + # IOContext. If :module is not set, default to Main. # nothing can be used to force printing prefix. - from = get(io, :module, active_module()) + from = get(io, :module, Main) if (from === nothing || !isvisible(name.name, name.mod, from)) show(io, name.mod) print(io, ".") @@ -1053,9 +1045,9 @@ function show_type_name(io::IO, tn::Core.TypeName) quo = false if !(get(io, :compact, false)::Bool) # Print module prefix unless type is visible from module passed to - # IOContext If :module is not set, default to Main (or current active module). + # IOContext If :module is not set, default to Main. # nothing can be used to force printing prefix - from = get(io, :module, active_module()) + from = get(io, :module, Main) if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from::Module)) show(io, tn.module) print(io, ".") @@ -2535,7 +2527,7 @@ function show_signature_function(io::IO, @nospecialize(ft), demangle=false, farg uw = unwrap_unionall(ft) if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && _isself(uw) uwmod = parentmodule(uw) - if qualified && !is_exported_from_stdlib(uw.name.mt.name, uwmod) && uwmod !== Main + if qualified && !isexported(uwmod, uw.name.mt.name) && uwmod !== Main print_within_stacktrace(io, uwmod, '.', bold=true) end s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.mt.name), context=io) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 67f5860082c8a..585ff1aa775b7 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -329,7 +329,7 @@ function warn_on_non_owning_accesses(current_mod, ast) end return ast end -warn_on_non_owning_accesses(ast) = warn_on_non_owning_accesses(REPL.active_module(), ast) +warn_on_non_owning_accesses(ast) = warn_on_non_owning_accesses(Base.active_module(), ast) const repl_ast_transforms = Any[softscope, warn_on_non_owning_accesses] # defaults for new REPL backends @@ -497,7 +497,7 @@ end function display(d::REPLDisplay, mime::MIME"text/plain", x) x = Ref{Any}(x) with_repl_linfo(d.repl) do io - io = IOContext(io, :limit => true, :module => active_module(d)::Module) + io = IOContext(io, :limit => true, :module => Base.active_module(d)::Module) if d.repl isa LineEditREPL mistate = d.repl.mistate mode = LineEdit.mode(mistate) @@ -527,7 +527,7 @@ show_repl(io::IO, ::MIME"text/plain", ex::Expr) = function print_response(repl::AbstractREPL, response, show_value::Bool, have_color::Bool) repl.waserror = response[2] with_repl_linfo(repl) do io - io = IOContext(io, :module => active_module(repl)::Module) + io = IOContext(io, :module => Base.active_module(repl)::Module) print_response(io, response, show_value, have_color, specialdisplay(repl)) end return nothing @@ -628,7 +628,7 @@ function run_repl(repl::AbstractREPL, @nospecialize(consumer = x -> nothing); ba Core.println(Core.stderr, e) Core.println(Core.stderr, catch_backtrace()) end - get_module = () -> active_module(repl) + get_module = () -> Base.active_module(repl) if backend_on_current_task t = @async run_frontend(repl, backend_ref) errormonitor(t) @@ -760,14 +760,9 @@ REPLCompletionProvider() = REPLCompletionProvider(LineEdit.Modifiers()) mutable struct ShellCompletionProvider <: CompletionProvider end struct LatexCompletions <: CompletionProvider end -function active_module() # this method is also called from Base - isdefined(Base, :active_repl) || return Main - Base.active_repl === nothing && return Main - return active_module(Base.active_repl::AbstractREPL) -end -active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module -active_module(::AbstractREPL) = Main -active_module(d::REPLDisplay) = active_module(d.repl) +Base.active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module +Base.active_module(::AbstractREPL) = Main +Base.active_module(d::REPLDisplay) = Base.active_module(d.repl) setmodifiers!(c::CompletionProvider, m::LineEdit.Modifiers) = nothing @@ -1206,7 +1201,7 @@ enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v function contextual_prompt(repl::LineEditREPL, prompt::Union{String,Function}) function () - mod = active_module(repl) + mod = Base.active_module(repl) prefix = mod == Main ? "" : string('(', mod, ") ") pr = prompt isa String ? prompt : prompt() prefix * pr From 5a633b7c8400cf3ae36cfebab62c5a316fc46649 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 16 Aug 2024 15:30:48 -0300 Subject: [PATCH 073/548] Fix fast getptls ccall lowering. (#55507) --- src/ccall.cpp | 3 +-- src/julia_threads.h | 3 --- test/compiler/codegen.jl | 3 +++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 97315d46b6ead..36808e13fdbf9 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1671,9 +1671,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); return ghostValue(ctx, jl_nothing_type); } - else if (is_libjulia_func("jl_get_ptls_states")) { + else if (is_libjulia_func(jl_get_ptls_states)) { ++CCALL_STAT(jl_get_ptls_states); - assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, get_current_ptls(ctx), retboxed, rt, unionall, static_rt); diff --git a/src/julia_threads.h b/src/julia_threads.h index 3486c5b969383..9a2a8cec375f5 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -206,10 +206,7 @@ typedef struct _jl_tls_states_t { #endif } jl_tls_states_t; -#ifndef JL_LIBRARY_EXPORTS -// deprecated (only for external consumers) JL_DLLEXPORT void *jl_get_ptls_states(void); -#endif // Update codegen version in `ccall.cpp` after changing either `pause` or `wake` #ifdef __MIC__ diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index c74dfbb29d3dd..cb983d7ab515e 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -966,3 +966,6 @@ end let x = Incomplete55396(55396) @test x.x === (55396,) end + +# Core.getptls() special handling +@test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall From f2fc2d903558fecee55d7aebf3536692ff278fed Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:58:08 -0400 Subject: [PATCH 074/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=207aef1f044=20to=20d1d2fc986=20(#55511)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 | 1 - .../Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 | 1 - .../Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 | 1 + .../Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 create mode 100644 deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 deleted file mode 100644 index 218260c77ea07..0000000000000 --- a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -832f88c404516179ece213581cd227f8 diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 deleted file mode 100644 index fc763c8d86f40..0000000000000 --- a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4355bb51a7f83bde489e587527e1e3a9c70799a5c0d27cd7f42b4227a5fbca2a1200a83db0317a75c582ee997bec72e9e8afafb059c395bd46e2aa015f481dca diff --git a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 new file mode 100644 index 0000000000000..097013569ceae --- /dev/null +++ b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 @@ -0,0 +1 @@ +725181b382adb22ad4f1f5e78db526ed diff --git a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 new file mode 100644 index 0000000000000..d6d8155431023 --- /dev/null +++ b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 @@ -0,0 +1 @@ +9ab56f368d5075a6f514ab8d2169239b439610c9bc9aca67a45a8a834b4d4ae7988910de3c78a687e40623fcd8bc9ba4aeee64ae7edf2cc84f1945b7e543a559 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 964c43dfcc786..cc38c67021224 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 7aef1f044f3483e8b07d33fb4cfe918be554de69 +PKG_SHA1 = d1d2fc986e7249909b450979acc4d359aacfc88e PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 0a26e908e77f147a5a964735ad02f74d047fe47b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 16 Aug 2024 20:58:30 +0100 Subject: [PATCH 075/548] update precompile progress bar to match Pkg (#55512) --- base/precompilation.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index aa70718eab9bc..6997ce12c8d01 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -304,9 +304,10 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere to_print = sprint(; context=io) do io print(io, " "^p.indent) printstyled(io, p.header, color=p.color, bold=true) - print(io, " [") - print(io, "="^n_filled, ">") - print(io, " "^n_left, "] ", ) + print(io, " ") + printstyled(io, "━"^n_filled; color=p.color) + printstyled(io, perc >= 95 ? "━" : "╸"; color=p.color) + printstyled(io, "━"^n_left, " "; color=:light_black) print(io, progress_text) carriagereturn && print(io, "\r") end From 8a19b74b5f849f6832a0bfcb689f6407300e9a80 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 17 Aug 2024 19:15:58 +0530 Subject: [PATCH 076/548] Update symmetric docstring to reflect the type of uplo (#55504) This brings the docstring closer to the actual implementation. In particular, following the current docstring and defining ```julia symmetric(::MyMatrix, uplo=:U) ``` leads to a method ambiguity, as `LinearAlgebra` defines `symmetric(::AbstractMatrix, uplo::Symbol=:U)`. --- stdlib/LinearAlgebra/src/symmetric.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index d801158232673..55630595f6fb2 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -12,7 +12,7 @@ struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Symmetric(A, uplo=:U) + Symmetric(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -63,7 +63,7 @@ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) end """ - symmetric(A, uplo=:U) + symmetric(A, uplo::Symbol=:U) Construct a symmetric view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -105,7 +105,7 @@ struct Hermitian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Hermitian(A, uplo=:U) + Hermitian(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -153,7 +153,7 @@ function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) end """ - hermitian(A, uplo=:U) + hermitian(A, uplo::Symbol=:U) Construct a hermitian view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -998,7 +998,7 @@ function cbrt(A::HermOrSym{<:Real}) end """ - hermitianpart(A, uplo=:U) -> Hermitian + hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a [`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part @@ -1014,7 +1014,7 @@ See also [`hermitianpart!`](@ref) for the corresponding in-place operation. hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) """ - hermitianpart!(A, uplo=:U) -> Hermitian + hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return [`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric From faa6095e983fed777007274f4efb9911154b1f3d Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sat, 17 Aug 2024 17:00:26 +0200 Subject: [PATCH 077/548] Demote(B)Float16 pass: only keep enabled for PPC. (#55486) LLVM should handle this properly now for everything but PPC (where BFoat16 isn't supported anyway). --- src/llvm-demote-float16.cpp | 47 ++++++++++-------------- test/llvmpasses/fastmath.jl | 26 -------------- test/llvmpasses/float16.ll | 71 +++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 57 deletions(-) diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index 5d0d9f5d37c40..7f1b076897fc8 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -49,37 +49,28 @@ extern JuliaOJIT *jl_ExecutionEngine; namespace { -static bool have_fp16(Function &caller, const Triple &TT) { - Attribute FSAttr = caller.getFnAttribute("target-features"); - StringRef FS = ""; - if (FSAttr.isValid()) - FS = FSAttr.getValueAsString(); - else if (jl_ExecutionEngine) - FS = jl_ExecutionEngine->getTargetFeatureString(); - // else probably called from opt, just do nothing - if (TT.isAArch64()) { - if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){ - return true; - } - } else if (TT.getArch() == Triple::x86_64) { - if (FS.find("+avx512fp16") != llvm::StringRef::npos){ - return true; - } - } - if (caller.hasFnAttribute("julia.hasfp16")) { - return true; - } - return false; +static bool have_fp16(Function &F, const Triple &TT) { + // for testing purposes + Attribute Attr = F.getFnAttribute("julia.hasfp16"); + if (Attr.isValid()) + return Attr.getValueAsBool(); + + // llvm/llvm-project#97975: on some platforms, `half` uses excessive precision + if (TT.isPPC()) + return false; + + return true; } -static bool have_bf16(Function &caller, const Triple &TT) { - if (caller.hasFnAttribute("julia.hasbf16")) { - return true; - } +static bool have_bf16(Function &F, const Triple &TT) { + // for testing purposes + Attribute Attr = F.getFnAttribute("julia.hasbf16"); + if (Attr.isValid()) + return Attr.getValueAsBool(); - // there's no targets that fully support bfloat yet;, - // AVX512BF16 only provides conversion and dot product instructions. - return false; + // https://github.com/llvm/llvm-project/issues/97975#issuecomment-2218770199: + // on current versions of LLVM, bf16 always uses TypeSoftPromoteHalf + return true; } static bool demoteFloat16(Function &F) diff --git a/test/llvmpasses/fastmath.jl b/test/llvmpasses/fastmath.jl index dd0892be56a0b..3c4c1d491ec28 100644 --- a/test/llvmpasses/fastmath.jl +++ b/test/llvmpasses/fastmath.jl @@ -16,29 +16,3 @@ import Base.FastMath # CHECK: call fast float @llvm.sqrt.f32(float %"x::Float32") emit(FastMath.sqrt_fast, Float32) - - -# Float16 operations should be performed as Float32, unless @fastmath is specified -# TODO: this is not true for platforms that natively support Float16 - -foo(x::T,y::T) where T = x-y == zero(T) -# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{ -# CHECK-DAG: %[[XEXT:[0-9]+]] = fpext half %[[X]] to float -# CHECK-DAG: %[[YEXT:[0-9]+]] = fpext half %[[Y]] to float -# CHECK: %[[DIFF:[0-9]+]] = fsub float %[[XEXT]], %[[YEXT]] -# CHECK: %[[TRUNC:[0-9]+]] = fptrunc float %[[DIFF]] to half -# CHECK: %[[DIFFEXT:[0-9]+]] = fpext half %[[TRUNC]] to float -# CHECK: %[[CMP:[0-9]+]] = fcmp oeq float %[[DIFFEXT]], 0.000000e+00 -# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8 -# CHECK: ret i8 %[[ZEXT]] -# CHECK: } -emit(foo, Float16, Float16) - -@fastmath foo(x::T,y::T) where T = x-y == zero(T) -# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{ -# CHECK: %[[DIFF:[0-9]+]] = fsub fast half %[[X]], %[[Y]] -# CHECK: %[[CMP:[0-9]+]] = fcmp fast oeq half %[[DIFF]], 0xH0000 -# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8 -# CHECK: ret i8 %[[ZEXT]] -# CHECK: } -emit(foo, Float16, Float16) diff --git a/test/llvmpasses/float16.ll b/test/llvmpasses/float16.ll index 33069c71179ed..d1dfb6aca11dd 100644 --- a/test/llvmpasses/float16.ll +++ b/test/llvmpasses/float16.ll @@ -99,7 +99,7 @@ top: ret half %13 } -define bfloat @demote_bfloat_test(bfloat %a, bfloat %b) { +define bfloat @demote_bfloat_test(bfloat %a, bfloat %b) #2 { top: ; CHECK-LABEL: @demote_bfloat_test( ; CHECK-NEXT: top: @@ -160,5 +160,70 @@ top: ret bfloat %13 } -attributes #0 = { "target-features"="-avx512fp16" } -attributes #1 = { "target-features"="+avx512fp16" } +define bfloat @native_bfloat_test(bfloat %a, bfloat %b) #3 { +top: +; CHECK-LABEL: @native_bfloat_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %0 = fadd bfloat %a, %b +; CHECK-NEXT: %1 = fadd bfloat %0, %b +; CHECK-NEXT: %2 = fadd bfloat %1, %b +; CHECK-NEXT: %3 = fmul bfloat %2, %b +; CHECK-NEXT: %4 = fdiv bfloat %3, %b +; CHECK-NEXT: %5 = insertelement <2 x bfloat> undef, bfloat %a, i32 0 +; CHECK-NEXT: %6 = insertelement <2 x bfloat> %5, bfloat %b, i32 1 +; CHECK-NEXT: %7 = insertelement <2 x bfloat> undef, bfloat %b, i32 0 +; CHECK-NEXT: %8 = insertelement <2 x bfloat> %7, bfloat %b, i32 1 +; CHECK-NEXT: %9 = fadd <2 x bfloat> %6, %8 +; CHECK-NEXT: %10 = extractelement <2 x bfloat> %9, i32 0 +; CHECK-NEXT: %11 = extractelement <2 x bfloat> %9, i32 1 +; CHECK-NEXT: %12 = fadd bfloat %10, %11 +; CHECK-NEXT: %13 = fadd bfloat %12, %4 +; CHECK-NEXT: ret bfloat %13 +; + %0 = fadd bfloat %a, %b + %1 = fadd bfloat %0, %b + %2 = fadd bfloat %1, %b + %3 = fmul bfloat %2, %b + %4 = fdiv bfloat %3, %b + %5 = insertelement <2 x bfloat> undef, bfloat %a, i32 0 + %6 = insertelement <2 x bfloat> %5, bfloat %b, i32 1 + %7 = insertelement <2 x bfloat> undef, bfloat %b, i32 0 + %8 = insertelement <2 x bfloat> %7, bfloat %b, i32 1 + %9 = fadd <2 x bfloat> %6, %8 + %10 = extractelement <2 x bfloat> %9, i32 0 + %11 = extractelement <2 x bfloat> %9, i32 1 + %12 = fadd bfloat %10, %11 + %13 = fadd bfloat %12, %4 + ret bfloat %13 +} + +define i1 @fast_half_test(half %0, half %1) #0 { +top: +; CHECK-LABEL: @fast_half_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %2 = fsub fast half %0, %1 +; CHECK-NEXT: %3 = fcmp fast oeq half %2, 0xH0000 +; CHECK-NEXT: ret i1 %3 +; + %2 = fsub fast half %0, %1 + %3 = fcmp fast oeq half %2, 0xH0000 + ret i1 %3 +} + +define i1 @fast_bfloat_test(bfloat %0, bfloat %1) #2 { +top: +; CHECK-LABEL: @fast_bfloat_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %2 = fsub fast bfloat %0, %1 +; CHECK-NEXT: %3 = fcmp fast oeq bfloat %2, 0xR0000 +; CHECK-NEXT: ret i1 %3 +; + %2 = fsub fast bfloat %0, %1 + %3 = fcmp fast oeq bfloat %2, 0xR0000 + ret i1 %3 +} + +attributes #0 = { "julia.hasfp16"="false" } +attributes #1 = { "julia.hasfp16"="true" } +attributes #2 = { "julia.hasbf16"="false" } +attributes #3 = { "julia.hasbf16"="true" } From 4aa9dfa25bde12be7daae2680445eb477eab5da9 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sat, 17 Aug 2024 17:25:11 -0400 Subject: [PATCH 078/548] Some small tests for transcode (#55436) This logic appears to be [uncovered](https://app.codecov.io/gh/JuliaLang/julia/blob/master/base%2Fstrings%2Fcstring.jl#L232) although it does have a doctest! Running the tests locally managed to trigger these functions. --- test/strings/basic.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 87d812c5bf201..d8ca4d204b6f4 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1388,3 +1388,10 @@ end end end end + +@testset "transcode" begin + str = "αβγ" + @test transcode(String, transcode(UInt16, str)) == str + @test transcode(String, transcode(UInt16, transcode(UInt8, str))) == str + @test transcode(String, transcode(UInt8, transcode(UInt16, str))) == str +end From dff0f55e6154ef85c1e8e422e8553d3a3c0729be Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 18 Aug 2024 09:50:06 +0530 Subject: [PATCH 079/548] Avoid using zero for the eltype in `tr(::Matrix)` (#55519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us compute the `tr` for `Matrix`es where the `eltype` does not have a zero, but we may sum over the diagonal. E.g. the following works after this: ```julia julia> M = fill([1 2; 3 4], 2, 2) 2×2 Matrix{Matrix{Int64}}: [1 2; 3 4] [1 2; 3 4] [1 2; 3 4] [1 2; 3 4] julia> tr(M) 2×2 Matrix{Int64}: 2 4 6 8 ``` Also, using linear indexing over Cartesian appears to provide a slight speed-up for small to mid-sized matrices: ```julia julia> A = rand(1000,1000); julia> @btime tr($A); 1.796 μs (0 allocations: 0 bytes) # nightly 1.524 μs (0 allocations: 0 bytes) # This PR ``` --- stdlib/LinearAlgebra/src/dense.jl | 9 +++------ stdlib/LinearAlgebra/test/dense.jl | 12 ++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 94a075ffaf24d..545801b065fb5 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -371,12 +371,9 @@ diagm(v::AbstractVector) = diagm(0 => v) diagm(m::Integer, n::Integer, v::AbstractVector) = diagm(m, n, 0 => v) function tr(A::Matrix{T}) where T - n = checksquare(A) - t = zero(T) - @inbounds @simd for i in 1:n - t += A[i,i] - end - t + checksquare(A) + isempty(A) && return zero(T) + reduce(+, (A[i] for i in diagind(A))) end _kronsize(A::AbstractMatrix, B::AbstractMatrix) = map(*, size(A), size(B)) diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index afc1df817a544..b9af413ad8319 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -1285,4 +1285,16 @@ end @test eltype(A) == eltype(T) end +@testset "tr" begin + @testset "block matrices" begin + S = [1 2; 3 4] + M = fill(S, 3, 3) + @test tr(M) == 3S + end + @testset "avoid promotion" begin + A = Int8[1 3; 2 4] + @test tr(A) === Int8(5) + end +end + end # module TestDense From 306cee71560e24d26585fd1143a2aacac41b5508 Mon Sep 17 00:00:00 2001 From: matthias314 <56549971+matthias314@users.noreply.github.com> Date: Sun, 18 Aug 2024 07:32:11 -0400 Subject: [PATCH 080/548] remove redundant `print` method for `Splat` (#55494) also a related bugfix by Jameson Nash --- base/operators.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 2c8070b44d704..d01902e302359 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -1339,8 +1339,7 @@ struct Splat{F} <: Function Splat(f) = new{Core.Typeof(f)}(f) end (s::Splat)(args) = s.f(args...) -print(io::IO, s::Splat) = print(io, "splat(", s.f, ')') -show(io::IO, s::Splat) = print(io, s) +show(io::IO, s::Splat) = (print(io, "splat("); show(io, s.f); print(io, ")")) ## in and related operators From 9738bc79854df1f0de41745d06edf2a9ba5ad8a6 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 19 Aug 2024 19:29:44 +0530 Subject: [PATCH 081/548] Fix tr for Symmetric/Hermitian block matrices (#55522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `Symmetric` and `Hermitian` symmetrize the diagonal elements of the parent, we can't forward `tr` to the parent unless it is already symmetric. This limits the existing `tr` methods to matrices of `Number`s, which is the common use-case. `tr` for `Symmetric` block matrices would now use the fallback implementation that explicitly computes the `diag`. This resolves the following discrepancy: ```julia julia> S = Symmetric(fill([1 2; 3 4], 3, 3)) 3×3 Symmetric{AbstractMatrix, Matrix{Matrix{Int64}}}: [1 2; 2 4] [1 2; 3 4] [1 2; 3 4] [1 3; 2 4] [1 2; 2 4] [1 2; 3 4] [1 3; 2 4] [1 3; 2 4] [1 2; 2 4] julia> tr(S) 2×2 Matrix{Int64}: 3 6 9 12 julia> sum(diag(S)) 2×2 Symmetric{Int64, Matrix{Int64}}: 3 6 6 12 ``` --- stdlib/LinearAlgebra/src/symmetric.jl | 4 ++-- stdlib/LinearAlgebra/test/symmetric.jl | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index 55630595f6fb2..c336785792588 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -449,8 +449,8 @@ Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Base.copy(A::Transpose{<:Any,<:Hermitian}) = Hermitian(copy(transpose(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) -tr(A::Symmetric) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) -tr(A::Hermitian) = real(tr(A.data)) +tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) +tr(A::Hermitian{<:Number}) = real(tr(A.data)) Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo(A.uplo)) Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo(A.uplo)) diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 89e9ca0d6a51d..5f1293ab2cdd7 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1116,4 +1116,15 @@ end end end +@testset "tr for block matrices" begin + m = [1 2; 3 4] + for b in (m, m * (1 + im)) + M = fill(b, 3, 3) + for ST in (Symmetric, Hermitian) + S = ST(M) + @test tr(S) == sum(diag(S)) + end + end +end + end # module TestSymmetric From d17b5acf9d534d57b938735fab22078d00af7fd0 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 19 Aug 2024 19:30:17 +0530 Subject: [PATCH 082/548] Faster trace for `StridedMatrix`es (#55523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR generalizes the `tr(::Matrix)` method to accept `StridedMatrix` types. As a consequence: ```julia julia> A = rand(1000,1000); julia> V = view(A, axes(A)...); julia> @btime tr($V); 1.990 μs (3 allocations: 7.88 KiB) # nightly v"1.12.0-DEV.1063" 999.455 ns (0 allocations: 0 bytes) # this PR ``` --- stdlib/LinearAlgebra/src/dense.jl | 4 ++-- stdlib/LinearAlgebra/test/dense.jl | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 545801b065fb5..62096cbb172f2 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -370,10 +370,10 @@ julia> diagm([1,2,3]) diagm(v::AbstractVector) = diagm(0 => v) diagm(m::Integer, n::Integer, v::AbstractVector) = diagm(m, n, 0 => v) -function tr(A::Matrix{T}) where T +function tr(A::StridedMatrix{T}) where T checksquare(A) isempty(A) && return zero(T) - reduce(+, (A[i] for i in diagind(A))) + reduce(+, (A[i] for i in diagind(A, IndexStyle(A)))) end _kronsize(A::AbstractMatrix, B::AbstractMatrix) = map(*, size(A), size(B)) diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index b9af413ad8319..1d43d76899392 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -1290,10 +1290,14 @@ end S = [1 2; 3 4] M = fill(S, 3, 3) @test tr(M) == 3S + @test tr(view(M, :, :)) == 3S + @test tr(view(M, axes(M)...)) == 3S end @testset "avoid promotion" begin A = Int8[1 3; 2 4] @test tr(A) === Int8(5) + @test tr(view(A, :, :)) === Int8(5) + @test tr(view(A, axes(A)...)) === Int8(5) end end From fc7b40e7fc7dc020d763fe070eed747e75f0c970 Mon Sep 17 00:00:00 2001 From: Sebastian Pfitzner Date: Mon, 19 Aug 2024 16:35:41 +0200 Subject: [PATCH 083/548] Add JULIA_PKG_GC_AUTO to docs (#51583) --- doc/src/manual/environment-variables.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index 84f36144304aa..30f2263904f40 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -267,6 +267,14 @@ versions of packages already installed as possible. !!! compat "Julia 1.9" This only affects Julia 1.9 and above. +### [`JULIA_PKG_GC_AUTO`](@id JULIA_PKG_GC_AUTO) + +If set to `false`, automatic garbage collection of packages and artifacts will be disabled; +see [`Pkg.gc`](https://pkgdocs.julialang.org/v1/api/#Pkg.gc) for more details. + +!!! compat "Julia 1.12" + This environment variable is only supported on Julia 1.12 and above. + ## Network transport ### [`JULIA_NO_VERIFY_HOSTS`](@id JULIA_NO_VERIFY_HOSTS) From 39eaa3cfc1a861cf898d89fbe320ffa858f41939 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 19 Aug 2024 11:38:52 -0400 Subject: [PATCH 084/548] Add test for upper/lower/titlecase and fix missing import (#55443) --- base/strings/annotated.jl | 2 +- base/strings/unicode.jl | 2 +- test/strings/annotated.jl | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index f077c577237d0..be4c6887d4a6d 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -384,7 +384,7 @@ a vector of region–annotation tuples. In accordance with the semantics documented in [`AnnotatedString`](@ref), the order of annotations returned matches the order in which they were applied. -See also: `annotate!`. +See also: [`annotate!`](@ref). """ annotations(s::AnnotatedString) = s.annotations diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 3c6710025077c..ad047514c85a6 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -6,7 +6,7 @@ module Unicode import Base: show, ==, hash, string, Symbol, isless, length, eltype, convert, isvalid, ismalformed, isoverlong, iterate, AnnotatedString, AnnotatedChar, annotated_chartransform, - @assume_effects + @assume_effects, annotations # whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index e985c0b421a51..c8fa0680113a7 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -64,6 +64,9 @@ end @testset "AnnotatedChar" begin chr = Base.AnnotatedChar('c') @test chr == Base.AnnotatedChar(chr.char, Pair{Symbol, Any}[]) + @test uppercase(chr) == Base.AnnotatedChar('C') + @test titlecase(chr) == Base.AnnotatedChar('C') + @test lowercase(Base.AnnotatedChar('C')) == chr str = Base.AnnotatedString("hmm", [(1:1, :attr => "h0h0"), (1:2, :attr => "h0m1"), (2:3, :attr => "m1m2")]) From 62e7705845f9dbf0c22b291c3d63e3cc6af57df4 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 19 Aug 2024 12:10:29 -0700 Subject: [PATCH 085/548] Set `.jl` sources as read-only during installation (#55524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This sets all `.jl` files in `$(prefix)/base` and `$(prefix)/test` to have `0444` permissions, to better match how `Pkg` installs packages (and sets them to be read-only). Fixes https://github.com/JuliaLang/juliaup/issues/865 --------- Co-authored-by: Mosè Giordano <765740+giordano@users.noreply.github.com> --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 3d8bf5436b476..735d342a79eb5 100644 --- a/Makefile +++ b/Makefile @@ -382,6 +382,11 @@ endif cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base cp -R -L $(JULIAHOME)/test/* $(DESTDIR)$(datarootdir)/julia/test cp -R -L $(build_datarootdir)/julia/* $(DESTDIR)$(datarootdir)/julia + + # Set .jl sources as read-only to match package directories + find $(DESTDIR)$(datarootdir)/julia/base -type f -name \*.jl -exec chmod 0444 '{}' \; + find $(DESTDIR)$(datarootdir)/julia/test -type f -name \*.jl -exec chmod 0444 '{}' \; + # Copy documentation cp -R -L $(BUILDROOT)/doc/_build/html $(DESTDIR)$(docdir)/ # Remove various files which should not be installed From 23132607d0b87a2cdd59245dc70eb7aa905df26b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 19 Aug 2024 16:30:10 -0400 Subject: [PATCH 086/548] atomics: fix setonce runtime intrinsic (#55530) --- src/datatype.c | 2 +- test/atomics.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datatype.c b/src/datatype.c index 5d35a7b55057d..fe457f8180dc9 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -2188,7 +2188,7 @@ inline int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *parent, jl_valu } else { char *px = lock(p, parent, needlock, isatomic); - success = undefref_check(rty, (jl_value_t*)px) != NULL; + success = undefref_check(rty, (jl_value_t*)px) == NULL; if (success) memassign_safe(hasptr, px, rhs, fsz); unlock(p, parent, needlock, isatomic); diff --git a/test/atomics.jl b/test/atomics.jl index 165c8dc4e2dfc..adfe4c87138cd 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -319,6 +319,7 @@ test_field_orderings(ARefxy{Union{Nothing,Missing}}(nothing, missing), nothing, test_field_orderings(ARefxy{Union{Nothing,Int}}(nothing, 123_1), nothing, 123_1) test_field_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) test_field_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) +test_field_orderings(Complex{Rational{Integer}}(10, 30), Complex{Rational{Integer}}(20, 40)) test_field_orderings(10.0, 20.0) test_field_orderings(NaN, Inf) From 4a229bc40eb075a82498e21a59e135fd7a5f0c6b Mon Sep 17 00:00:00 2001 From: Ken Williams Date: Mon, 19 Aug 2024 17:02:09 -0500 Subject: [PATCH 087/548] Add behavior for even values to docs for new invmod(n, T) functions (#55531) As discussed in https://discourse.julialang.org/t/the-new-invmod-n-t-function-and-even-arguments/118377 --- base/intfuncs.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index f72ac6ee08d4d..e44ea8eed2463 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -263,14 +263,16 @@ end invmod(n::T) where {T <: Base.BitInteger} Compute the modular inverse of `n` in the integer ring of type `T`, i.e. modulo -`2^N` where `N = 8*sizeof(T)` (e.g. `N = 32` for `Int32`). In other words these +`2^N` where `N = 8*sizeof(T)` (e.g. `N = 32` for `Int32`). In other words, these methods satisfy the following identities: ``` n * invmod(n) == 1 (n * invmod(n, T)) % T == 1 (n % T) * invmod(n, T) == 1 ``` -Note that `*` here is modular multiplication in the integer ring, `T`. +Note that `*` here is modular multiplication in the integer ring, `T`. This will +throw an error if ``n`` is even, because then it is not relatively prime with `2^N` +and thus has no such inverse. Specifying the modulus implied by an integer type as an explicit value is often inconvenient since the modulus is by definition too big to be represented by the From bec4702fa1eb76942e75285ac9ca8aa020b39c31 Mon Sep 17 00:00:00 2001 From: Nathan Boyer <65452054+nathanrboyer@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:05:42 -0400 Subject: [PATCH 088/548] Improve `walkdir` docstring (#55476) I was not able to understand how to use `walkdir` with the current docstring. It was not clear to me that `root` changes each iteration. I thought `root` would stay fixed to the input and `dirs` would iterate. --- base/file.jl | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/base/file.jl b/base/file.jl index 3987029d5f74f..7fa4a288c1d10 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1093,7 +1093,11 @@ end walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) Return an iterator that walks the directory tree of a directory. -The iterator returns a tuple containing `(rootpath, dirs, files)`. + +The iterator returns a tuple containing `(path, dirs, files)`. +Each iteration `path` will change to the next directory in the tree; +then `dirs` and `files` will be vectors containing the directories and files +in the current `path` directory. The directory tree can be traversed top-down or bottom-up. If `walkdir` or `stat` encounters a `IOError` it will rethrow the error by default. A custom error handling function can be provided through `onerror` keyword argument. @@ -1103,14 +1107,14 @@ See also: [`readdir`](@ref). # Examples ```julia -for (root, dirs, files) in walkdir(".") - println("Directories in \$root") +for (path, dirs, files) in walkdir(".") + println("Directories in \$path") for dir in dirs - println(joinpath(root, dir)) # path to directories + println(joinpath(path, dir)) # path to directories end - println("Files in \$root") + println("Files in \$path") for file in files - println(joinpath(root, file)) # path to files + println(joinpath(path, file)) # path to files end end ``` @@ -1120,18 +1124,18 @@ julia> mkpath("my/test/dir"); julia> itr = walkdir("my"); -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my", ["test"], String[]) -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my/test", ["dir"], String[]) -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my/test/dir", String[], String[]) ``` """ -function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) - function _walkdir(chnl, root) +function walkdir(path; topdown=true, follow_symlinks=false, onerror=throw) + function _walkdir(chnl, path) tryf(f, p) = try f(p) catch err @@ -1143,7 +1147,7 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) end return end - entries = tryf(_readdirx, root) + entries = tryf(_readdirx, path) entries === nothing && return dirs = Vector{String}() files = Vector{String}() @@ -1157,17 +1161,17 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) end if topdown - push!(chnl, (root, dirs, files)) + push!(chnl, (path, dirs, files)) end for dir in dirs - _walkdir(chnl, joinpath(root, dir)) + _walkdir(chnl, joinpath(path, dir)) end if !topdown - push!(chnl, (root, dirs, files)) + push!(chnl, (path, dirs, files)) end nothing end - return Channel{Tuple{String,Vector{String},Vector{String}}}(chnl -> _walkdir(chnl, root)) + return Channel{Tuple{String,Vector{String},Vector{String}}}(chnl -> _walkdir(chnl, path)) end function unlink(p::AbstractString) From a218e82ad3a4b5c44351decf7a734b2b30985ce3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 19 Aug 2024 21:57:38 -0400 Subject: [PATCH 089/548] handle Type{Union{}} like typeof(Union{}) more (#55508) We could try to make them both pointers (by setting mayinlinealloc=false on Core.TypeofBottom), but let's try to make them both equivalent representations of the typeof Union{} as a singleton value. Fixes #55208 --- src/cgutils.cpp | 9 +++++++-- src/codegen.cpp | 10 ++++++---- src/datatype.c | 4 ++++ src/jltypes.c | 1 + test/compiler/codegen.jl | 15 +++++++++++++++ test/core.jl | 2 ++ 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 0969f78f10bb4..f911ef17eea38 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -650,7 +650,7 @@ static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl { // this function converts a Julia Type into the equivalent LLVM type if (isboxed) *isboxed = false; - if (jt == (jl_value_t*)jl_bottom_type) + if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super) return getVoidTy(ctxt); if (jl_is_concrete_immutable(jt)) { if (jl_datatype_nbits(jt) == 0) @@ -760,7 +760,7 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, // use this where C-compatible (unboxed) structs are desired // use julia_type_to_llvm directly when you want to preserve Julia's type semantics if (isboxed) *isboxed = false; - if (jt == (jl_value_t*)jl_bottom_type) + if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super) return getVoidTy(ctxt); if (jl_is_primitivetype(jt)) return bitstype_to_llvm(jt, ctxt, llvmcall); @@ -948,6 +948,9 @@ static bool for_each_uniontype_small( allunbox &= for_each_uniontype_small(f, ((jl_uniontype_t*)ty)->b, counter); return allunbox; } + else if (ty == (jl_value_t*)jl_typeofbottom_type->super) { + f(++counter, jl_typeofbottom_type); // treat Tuple{union{}} as identical to typeof(Union{}) + } else if (jl_is_pointerfree(ty)) { f(++counter, (jl_datatype_t*)ty); return true; @@ -1691,6 +1694,8 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, if (intersected_type == (jl_value_t*)jl_bottom_type) known_isa = false; } + if (intersected_type == (jl_value_t*)jl_typeofbottom_type->super) + intersected_type = (jl_value_t*)jl_typeofbottom_type; // swap abstract Type{Union{}} for concrete typeof(Union{}) if (known_isa) { if (!*known_isa && !msg.isTriviallyEmpty()) { emit_type_error(ctx, x, literal_pointer_val(ctx, type), msg); diff --git a/src/codegen.cpp b/src/codegen.cpp index 6551f13ea566e..e9a58d25e3e94 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2240,6 +2240,8 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_value_t *typ) // replace T::Type{T} with T, by assuming that T must be a leaftype of some sort jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); constant.constant = jl_tparam0(typ); + if (typ == (jl_value_t*)jl_typeofbottom_type->super) + constant.isghost = true; return constant; } return jl_cgval_t(typ); @@ -2252,7 +2254,7 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_datatype_t *typ) static inline jl_cgval_t mark_julia_const(jl_codectx_t &ctx, jl_value_t *jv) { jl_value_t *typ; - if (jl_is_type(jv)) { + if (jl_is_type(jv) && jv != jl_bottom_type) { typ = (jl_value_t*)jl_wrap_Type(jv); // TODO: gc-root this? } else { @@ -3619,8 +3621,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva if (arg1.constant && arg2.constant) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), jl_egal(arg1.constant, arg2.constant)); - jl_value_t *rt1 = arg1.typ; - jl_value_t *rt2 = arg2.typ; + jl_value_t *rt1 = (arg1.constant ? jl_typeof(arg1.constant) : arg1.typ); + jl_value_t *rt2 = (arg2.constant ? jl_typeof(arg2.constant) : arg2.typ); if (jl_is_concrete_type(rt1) && jl_is_concrete_type(rt2) && !jl_is_kind(rt1) && !jl_is_kind(rt2) && rt1 != rt2) { // disjoint concrete leaf types are never equal (quick test) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); @@ -9534,7 +9536,7 @@ static jl_llvm_functions_t RTindex = UndefValue::get(getInt8Ty(ctx.builder.getContext())); } else if (jl_is_concrete_type(val.typ) || val.constant) { - size_t tindex = get_box_tindex((jl_datatype_t*)val.typ, phiType); + size_t tindex = get_box_tindex((jl_datatype_t*)(val.constant ? jl_typeof(val.constant) : val.typ), phiType); if (tindex == 0) { if (VN) V = boxed(ctx, val); diff --git a/src/datatype.c b/src/datatype.c index fe457f8180dc9..1157c1d425cb2 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -357,6 +357,8 @@ int jl_struct_try_layout(jl_datatype_t *dt) int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree) { + if (jl_typeofbottom_type && ty == jl_typeofbottom_type->super) + ty = jl_typeofbottom_type; if (ty->name->mayinlinealloc && jl_struct_try_layout(ty)) { if (ty->layout->npointers > 0) { if (pointerfree) @@ -1656,6 +1658,8 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) { jl_task_t *ct = jl_current_task; if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL || jl_is_layout_opaque(type->layout)) { + if (type == jl_typeofbottom_type->super) + return jl_bottom_type; // ::Type{Union{}} is an abstract type, but is also a singleton when used as a field type jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); } if (type->instance != NULL) diff --git a/src/jltypes.c b/src/jltypes.c index 5dc50ff0ca4e6..5516b8c9c0c6e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3024,6 +3024,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_anytuple_type->layout = NULL; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); + jl_typeofbottom_type->super->layout = jl_typeofbottom_type->layout; // the only abstract type with a layout jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec, 0); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index cb983d7ab515e..a8128be270f12 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -969,3 +969,18 @@ end # Core.getptls() special handling @test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall + +# issue 55208 +@noinline function f55208(x, i) + z = (i == 0 ? x[1] : x[i]) + return z isa Core.TypeofBottom +end +@test f55208((Union{}, 5, 6, 7), 0) + +@noinline function g55208(x, i) + z = (i == 0 ? x[1] : x[i]) + typeof(z) +end +@test g55208((Union{}, true, true), 0) === typeof(Union{}) + +@test string((Core.Union{}, true, true, true)) == "(Union{}, true, true, true)" diff --git a/test/core.jl b/test/core.jl index 4cbb872ce4e50..c643c965f89cc 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8265,3 +8265,5 @@ end @test Tuple{Vararg{Int}} === Union{Tuple{Int}, Tuple{}, Tuple{Int, Int, Vararg{Int}}} @test (Tuple{Vararg{T}} where T) === (Union{Tuple{T, T, Vararg{T}}, Tuple{}, Tuple{T}} where T) @test_broken (Tuple{Vararg{T}} where T) === Union{Tuple{T, T, Vararg{T}} where T, Tuple{}, Tuple{T} where T} + +@test sizeof(Pair{Union{typeof(Union{}),Nothing}, Union{Type{Union{}},Nothing}}(Union{}, Union{})) == 2 From d4bd540d0f30605ef184af79f2aaaff9aa351a54 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 19 Aug 2024 21:57:48 -0400 Subject: [PATCH 090/548] make jl_thread_suspend_and_get_state safe (#55500) Fixes async safety, thread safety, FreeBSD safety. --- src/signals-unix.c | 109 +++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 5e79fd5a2e29e..edca523bed6d1 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -314,6 +314,8 @@ int exc_reg_is_write_fault(uintptr_t esr) { #if defined(HAVE_MACH) #include "signals-mach.c" #else +#include +#include int jl_lock_stackwalk(void) { @@ -439,17 +441,13 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) } } -#if !defined(JL_DISABLE_LIBUNWIND) -static bt_context_t *signal_context; -pthread_mutex_t in_signal_lock; -static pthread_cond_t exit_signal_cond; -static pthread_cond_t signal_caught_cond; +pthread_mutex_t in_signal_lock; // shared with jl_delete_thread +static bt_context_t *signal_context; // protected by in_signal_lock +static int exit_signal_cond = -1; +static int signal_caught_cond = -1; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout; pthread_mutex_lock(&in_signal_lock); jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; @@ -458,48 +456,51 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - jl_atomic_store_release(&ptls2->signal_request, 1); - pthread_kill(ptls2->system_id, SIGUSR2); - // wait for thread to acknowledge - int err = pthread_cond_timedwait(&signal_caught_cond, &in_signal_lock, &ts); - if (err == ETIMEDOUT) { - sig_atomic_t request = 1; + sig_atomic_t request = 0; + if (!jl_atomic_cmpswap(&ptls2->signal_request, &request, 1)) { + // something is wrong, or there is already a usr2 in flight elsewhere + pthread_mutex_unlock(&in_signal_lock); + return 0; + } + request = 1; + int err = pthread_kill(ptls2->system_id, SIGUSR2); + // wait for thread to acknowledge or timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + if (err == 0) { + do { + err = poll(&event, 1, timeout * 1000); + } while (err == -1 && errno == EINTR); + } + if ((event.revents & POLLIN) == 0) { + // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { pthread_mutex_unlock(&in_signal_lock); return 0; } - // Request is either now 0 (meaning the other thread is waiting for - // exit_signal_cond already), - // Or it is now -1 (meaning the other thread - // is waiting for in_signal_lock, and we need to release that lock - // here for a bit, until the other thread has a chance to get to the - // exit_signal_cond) - if (request == -1) { - err = pthread_cond_wait(&signal_caught_cond, &in_signal_lock); - assert(!err); - } } + eventfd_t got; + do { + err = read(signal_caught_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); (void) got; // Now the other thread is waiting on exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) - int request = jl_atomic_load_acquire(&ptls2->signal_request); + request = jl_atomic_load_acquire(&ptls2->signal_request); assert(request == 0); (void) request; - jl_atomic_store_release(&ptls2->signal_request, 1); // prepare to resume normally + jl_atomic_store_release(&ptls2->signal_request, 4); // prepare to resume normally, but later code may change this *ctx = *signal_context; return 1; } void jl_thread_resume(int tid) { - jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; - pthread_cond_broadcast(&exit_signal_cond); - pthread_cond_wait(&signal_caught_cond, &in_signal_lock); // wait for thread to acknowledge (so that signal_request doesn't get mixed up) - // The other thread is waiting to leave exit_signal_cond (verify that here by - // checking it is 0, and add an acquire barrier for good measure) - int request = jl_atomic_load_acquire(&ptls2->signal_request); - assert(request == 0); (void) request; + int err; + eventfd_t got = 1; + err = write(exit_signal_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); pthread_mutex_unlock(&in_signal_lock); } -#endif // Throw jl_interrupt_exception if the master thread is in a signal async region // or if SIGINT happens too often. @@ -508,9 +509,11 @@ static void jl_try_deliver_sigint(void) jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; jl_safepoint_enable_sigint(); jl_wake_libuv(); + pthread_mutex_lock(&in_signal_lock); jl_atomic_store_release(&ptls2->signal_request, 2); // This also makes sure `sleep` is aborted. pthread_kill(ptls2->system_id, SIGUSR2); + pthread_mutex_unlock(&in_signal_lock); } // Write only by signal handling thread, read only by main thread @@ -543,12 +546,12 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) } // request: -// -1: beginning processing [invalid outside here] // 0: nothing [not from here] -// 1: get state +// 1: get state & wait for request // 2: throw sigint if `!defer_signal && io_wait` or if force throw threshold // is reached // 3: raise `thread0_exit_signo` and try to exit +// 4: no-op void usr2_handler(int sig, siginfo_t *info, void *ctx) { jl_task_t *ct = jl_get_current_task(); @@ -559,25 +562,21 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) return; int errno_save = errno; // acknowledge that we saw the signal_request - sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, -1); -#if !defined(JL_DISABLE_LIBUNWIND) + sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); if (request == 1) { - pthread_mutex_lock(&in_signal_lock); signal_context = jl_to_bt_context(ctx); - // acknowledge that we set the signal_caught_cond broadcast + int err; + eventfd_t got = 1; + err = write(signal_caught_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); + do { + err = read(exit_signal_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); request = jl_atomic_exchange(&ptls->signal_request, 0); - assert(request == -1); (void) request; - pthread_cond_broadcast(&signal_caught_cond); - pthread_cond_wait(&exit_signal_cond, &in_signal_lock); - request = jl_atomic_exchange(&ptls->signal_request, 0); - assert(request == 1 || request == 3); - // acknowledge that we got the resume signal - pthread_cond_broadcast(&signal_caught_cond); - pthread_mutex_unlock(&in_signal_lock); + assert(request == 2 || request == 3 || request == 4); } - else -#endif - jl_atomic_exchange(&ptls->signal_request, 0); // returns -1 if (request == 2) { int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { @@ -1038,10 +1037,12 @@ void restore_signals(void) jl_sigsetset(&sset); pthread_sigmask(SIG_SETMASK, &sset, 0); -#if !defined(HAVE_MACH) && !defined(JL_DISABLE_LIBUNWIND) +#if !defined(HAVE_MACH) + exit_signal_cond = eventfd(0, EFD_CLOEXEC); + signal_caught_cond = eventfd(0, EFD_CLOEXEC); if (pthread_mutex_init(&in_signal_lock, NULL) != 0 || - pthread_cond_init(&exit_signal_cond, NULL) != 0 || - pthread_cond_init(&signal_caught_cond, NULL) != 0) { + exit_signal_cond == -1 || + signal_caught_cond == -1) { jl_error("SIGUSR pthread init failed"); } #endif From 9650510b5fa64571178cb9fe8b6799060ae0a3ac Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:58:22 +0900 Subject: [PATCH 091/548] inference: model partially initialized structs with `PartialStruct` (#55297) There is still room for improvement in the accuracy of `getfield` and `isdefined` for structs with uninitialized fields. This commit aims to enhance the accuracy of struct field defined-ness by propagating such struct as `PartialStruct` in cases where fields that might be uninitialized are confirmed to be defined. Specifically, the improvements are made in the following situations: 1. when a `:new` expression receives arguments greater than the minimum number of initialized fields. 2. when new information about the initialized fields of `x` can be obtained in the `then` branch of `if isdefined(x, :f)`. Combined with the existing optimizations, these improvements enable DCE in scenarios such as: ```julia julia> @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing); julia> @allocated broadcast_noescape1(Ref("x")) 16 # master 0 # this PR ``` One important point to note is that, as revealed in JuliaLang/julia#48999, fields and globals can revert to `undef` during precompilation. This commit does not affect globals. Furthermore, even for fields, the refinements made by 1. and 2. are propagated along with data-flow, and field defined-ness information is only used when fields are confirmed to be initialized. Therefore, the same issues as JuliaLang/julia#48999 will not occur by this commit. --- base/compiler/abstractinterpretation.jl | 86 ++++--- base/compiler/ssair/passes.jl | 7 +- base/compiler/tfuncs.jl | 42 +++- base/compiler/typelattice.jl | 87 ++++--- base/compiler/typelimits.jl | 54 +++-- .../compiler/EscapeAnalysis/EscapeAnalysis.jl | 10 +- test/compiler/inference.jl | 227 +++++++++++++++--- test/tuple.jl | 6 +- 8 files changed, 393 insertions(+), 126 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 90d395600bbde..351f241878e7d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2006,26 +2006,39 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs return Conditional(aty.slot, thentype, elsetype) end elseif f === isdefined - uty = argtypes[2] a = ssa_def_slot(fargs[2], sv) - if isa(uty, Union) && isa(a, SlotNumber) - fld = argtypes[3] - thentype = Bottom - elsetype = Bottom - for ty in uniontypes(uty) - cnd = isdefined_tfunc(𝕃ᵢ, ty, fld) - if isa(cnd, Const) - if cnd.val::Bool - thentype = thentype ⊔ ty + if isa(a, SlotNumber) + argtype2 = argtypes[2] + if isa(argtype2, Union) + fld = argtypes[3] + thentype = Bottom + elsetype = Bottom + for ty in uniontypes(argtype2) + cnd = isdefined_tfunc(𝕃ᵢ, ty, fld) + if isa(cnd, Const) + if cnd.val::Bool + thentype = thentype ⊔ ty + else + elsetype = elsetype ⊔ ty + end else + thentype = thentype ⊔ ty elsetype = elsetype ⊔ ty end - else - thentype = thentype ⊔ ty - elsetype = elsetype ⊔ ty + end + return Conditional(a, thentype, elsetype) + else + thentype = form_partially_defined_struct(argtype2, argtypes[3]) + if thentype !== nothing + elsetype = argtype2 + if rt === Const(false) + thentype = Bottom + elseif rt === Const(true) + elsetype = Bottom + end + return Conditional(a, thentype, elsetype) end end - return Conditional(a, thentype, elsetype) end end end @@ -2033,6 +2046,24 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs return rt end +function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name)) + obj isa Const && return nothing # nothing to refine + name isa Const || return nothing + objt0 = widenconst(obj) + objt = unwrap_unionall(objt0) + objt isa DataType || return nothing + isabstracttype(objt) && return nothing + fldidx = try_compute_fieldidx(objt, name.val) + fldidx === nothing && return nothing + nminfld = datatype_min_ninitialized(objt) + if ismutabletype(objt) + fldidx == nminfld+1 || return nothing + else + fldidx > nminfld || return nothing + end + return PartialStruct(objt0, Any[fieldtype(objt0, i) for i = 1:fldidx]) +end + function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta) na = length(argtypes) if isvarargtype(argtypes[end]) @@ -2573,20 +2604,18 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, vtypes::Union{V end ats[i] = at end - # For now, don't allow: - # - Const/PartialStruct of mutables (but still allow PartialStruct of mutables - # with `const` fields if anything refined) - # - partially initialized Const/PartialStruct - if fcount == nargs - if consistent === ALWAYS_TRUE && allconst - argvals = Vector{Any}(undef, nargs) - for j in 1:nargs - argvals[j] = (ats[j]::Const).val - end - rt = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs)) - elseif anyrefine - rt = PartialStruct(rt, ats) + if fcount == nargs && consistent === ALWAYS_TRUE && allconst + argvals = Vector{Any}(undef, nargs) + for j in 1:nargs + argvals[j] = (ats[j]::Const).val end + rt = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs)) + elseif anyrefine || nargs > datatype_min_ninitialized(rt) + # propagate partially initialized struct as `PartialStruct` when: + # - any refinement information is available (`anyrefine`), or when + # - `nargs` is greater than `n_initialized` derived from the struct type + # information alone + rt = PartialStruct(rt, ats) end else rt = refine_partial_type(rt) @@ -3094,7 +3123,8 @@ end @nospecializeinfer function widenreturn_partials(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo) if isa(rt, PartialStruct) fields = copy(rt.fields) - local anyrefine = false + anyrefine = !isvarargtype(rt.fields[end]) && + length(rt.fields) > datatype_min_ninitialized(unwrap_unionall(rt.typ)) 𝕃 = typeinf_lattice(info.interp) ⊏ = strictpartialorder(𝕃) for i in 1:length(fields) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 33cda9bf27d20..37d79e2bd7b0c 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1166,7 +1166,12 @@ struct IntermediaryCollector <: WalkerCallback intermediaries::SPCSet end function (walker_callback::IntermediaryCollector)(@nospecialize(def), @nospecialize(defssa::AnySSAValue)) - isa(def, Expr) || push!(walker_callback.intermediaries, defssa.id) + if !(def isa Expr) + push!(walker_callback.intermediaries, defssa.id) + if def isa PiNode + return LiftedValue(def.val) + end + end return nothing end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 9a4c761b4209b..89874b9a6df10 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -419,7 +419,7 @@ end else return Bottom end - if 1 <= idx <= datatype_min_ninitialized(a1) + if 1 ≤ idx ≤ datatype_min_ninitialized(a1) return Const(true) elseif a1.name === _NAMEDTUPLE_NAME if isconcretetype(a1) @@ -427,15 +427,21 @@ end else ns = a1.parameters[1] if isa(ns, Tuple) - return Const(1 <= idx <= length(ns)) + return Const(1 ≤ idx ≤ length(ns)) end end - elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) + elseif idx ≤ 0 || (!isvatuple(a1) && idx > fieldcount(a1)) return Const(false) elseif isa(arg1, Const) if !ismutabletype(a1) || isconst(a1, idx) return Const(isdefined(arg1.val, idx)) end + elseif isa(arg1, PartialStruct) + if !isvarargtype(arg1.fields[end]) + if 1 ≤ idx ≤ length(arg1.fields) + return Const(true) + end + end elseif !isvatuple(a1) fieldT = fieldtype(a1, idx) if isa(fieldT, DataType) && isbitstype(fieldT) @@ -989,27 +995,39 @@ end ⊑ = partialorder(𝕃) # If we have s00 being a const, we can potentially refine our type-based analysis above - if isa(s00, Const) || isconstType(s00) - if !isa(s00, Const) - sv = (s00::DataType).parameters[1] - else + if isa(s00, Const) || isconstType(s00) || isa(s00, PartialStruct) + if isa(s00, Const) sv = s00.val + sty = typeof(sv) + nflds = nfields(sv) + ismod = sv isa Module + elseif isa(s00, PartialStruct) + sty = unwrap_unionall(s00.typ) + nflds = fieldcount_noerror(sty) + ismod = false + else + sv = (s00::DataType).parameters[1] + sty = typeof(sv) + nflds = nfields(sv) + ismod = sv isa Module end if isa(name, Const) nval = name.val if !isa(nval, Symbol) - isa(sv, Module) && return false + ismod && return false isa(nval, Int) || return false end return isdefined_tfunc(𝕃, s00, name) === Const(true) end - boundscheck && return false + # If bounds checking is disabled and all fields are assigned, # we may assume that we don't throw - isa(sv, Module) && return false + @assert !boundscheck + ismod && return false name ⊑ Int || name ⊑ Symbol || return false - typeof(sv).name.n_uninitialized == 0 && return true - for i = (datatype_min_ninitialized(typeof(sv)) + 1):nfields(sv) + sty.name.n_uninitialized == 0 && return true + nflds === nothing && return false + for i = (datatype_min_ninitialized(sty)+1):nflds isdefined_tfunc(𝕃, s00, Const(i)) === Const(true) || return false end return true diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 1be76f7d8bea3..d375d61c0bdd8 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -6,17 +6,42 @@ # N.B.: Const/PartialStruct/InterConditional are defined in Core, to allow them to be used # inside the global code cache. -# -# # The type of a value might be constant -# struct Const -# val -# end -# -# struct PartialStruct -# typ -# fields::Vector{Any} # elements are other type lattice members -# end + import Core: Const, PartialStruct + +""" + struct Const + val + end + +The type representing a constant value. +""" +:(Const) + +""" + struct PartialStruct + typ + fields::Vector{Any} # elements are other type lattice members + end + +This extended lattice element is introduced when we have information about an object's +fields beyond what can be obtained from the object type. E.g. it represents a tuple where +some elements are known to be constants or a struct whose `Any`-typed field is initialized +with `Int` values. + +- `typ` indicates the type of the object +- `fields` holds the lattice elements corresponding to each field of the object + +If `typ` is a struct, `fields` represents the fields of the struct that are guaranteed to be +initialized. For instance, if the length of `fields` of `PartialStruct` representing a +struct with 4 fields is 3, the 4th field may not be initialized. If the length is 4, all +fields are guaranteed to be initialized. + +If `typ` is a tuple, the last element of `fields` may be `Vararg`. In this case, it is +guaranteed that the number of elements in the tuple is at least `length(fields)-1`, but the +exact number of elements is unknown. +""" +:(PartialStruct) function PartialStruct(@nospecialize(typ), fields::Vector{Any}) for i = 1:length(fields) assert_nested_slotwrapper(fields[i]) @@ -57,8 +82,13 @@ end Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = Conditional(slot_id(var), thentype, elsetype) +import Core: InterConditional """ - cnd::InterConditional + struct InterConditional + slot::Int + thentype + elsetype + end Similar to `Conditional`, but conveys inter-procedural constraints imposed on call arguments. This is separate from `Conditional` to catch logic errors: the lattice element name is `InterConditional` @@ -66,14 +96,6 @@ while processing a call, then `Conditional` everywhere else. Thus `InterConditio `CompilerTypes`—these type's usages are disjoint—though we define the lattice for `InterConditional`. """ :(InterConditional) -import Core: InterConditional -# struct InterConditional -# slot::Int -# thentype -# elsetype -# InterConditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) = -# new(slot, thentype, elsetype) -# end InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = InterConditional(slot_id(var), thentype, elsetype) @@ -447,8 +469,13 @@ end @nospecializeinfer function ⊑(lattice::PartialsLattice, @nospecialize(a), @nospecialize(b)) if isa(a, PartialStruct) if isa(b, PartialStruct) - if !(length(a.fields) == length(b.fields) && a.typ <: b.typ) - return false + a.typ <: b.typ || return false + if length(a.fields) ≠ length(b.fields) + if !(isvarargtype(a.fields[end]) || isvarargtype(b.fields[end])) + length(a.fields) ≥ length(b.fields) || return false + else + return false + end end for i in 1:length(b.fields) af = a.fields[i] @@ -471,19 +498,25 @@ end return isa(b, Type) && a.typ <: b elseif isa(b, PartialStruct) if isa(a, Const) - nf = nfields(a.val) - nf == length(b.fields) || return false widea = widenconst(a)::DataType wideb = widenconst(b) wideb′ = unwrap_unionall(wideb)::DataType widea.name === wideb′.name || return false - # We can skip the subtype check if b is a Tuple, since in that - # case, the ⊑ of the elements is sufficient. - if wideb′.name !== Tuple.name && !(widea <: wideb) - return false + if wideb′.name === Tuple.name + # We can skip the subtype check if b is a Tuple, since in that + # case, the ⊑ of the elements is sufficient. + # But for tuple comparisons, we need their lengths to be the same for now. + # TODO improve accuracy for cases when `b` contains vararg element + nfields(a.val) == length(b.fields) || return false + else + widea <: wideb || return false + # for structs we need to check that `a` has more information than `b` that may be partially initialized + n_initialized(a) ≥ length(b.fields) || return false end + nf = nfields(a.val) for i in 1:nf isdefined(a.val, i) || continue # since ∀ T Union{} ⊑ T + i > length(b.fields) && break # `a` has more information than `b` that is partially initialized struct bfᵢ = b.fields[i] if i == nf bfᵢ = unwrapva(bfᵢ) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 318ac0b5c27e5..91a44d3b117ab 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -321,6 +321,11 @@ end # even after complicated recursion and other operations on it elsewhere const issimpleenoughtupleelem = issimpleenoughtype +function n_initialized(t::Const) + nf = nfields(t.val) + return something(findfirst(i::Int->!isdefined(t.val,i), 1:nf), nf+1)-1 +end + # A simplified type_more_complex query over the extended lattice # (assumes typeb ⊑ typea) @nospecializeinfer function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecialize(typeb)) @@ -328,6 +333,13 @@ const issimpleenoughtupleelem = issimpleenoughtype typea === typeb && return true if typea isa PartialStruct aty = widenconst(typea) + if typeb isa Const + @assert length(typea.fields) ≤ n_initialized(typeb) "typeb ⊑ typea is assumed" + elseif typeb isa PartialStruct + @assert length(typea.fields) ≤ length(typeb.fields) "typeb ⊑ typea is assumed" + else + return false + end for i = 1:length(typea.fields) ai = unwrapva(typea.fields[i]) bi = fieldtype(aty, i) @@ -572,34 +584,38 @@ end # N.B. This can also be called with both typea::Const and typeb::Const to # to recover PartialStruct from `Const`s with overlapping fields. -@nospecializeinfer function tmerge_partial_struct(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) +@nospecializeinfer function tmerge_partial_struct(𝕃::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) aty = widenconst(typea) bty = widenconst(typeb) if aty === bty - # must have egal here, since we do not create PartialStruct for non-concrete types - typea_nfields = nfields_tfunc(lattice, typea) - typeb_nfields = nfields_tfunc(lattice, typeb) - isa(typea_nfields, Const) || return nothing - isa(typeb_nfields, Const) || return nothing - type_nfields = typea_nfields.val::Int - type_nfields === typeb_nfields.val::Int || return nothing - type_nfields == 0 && return nothing - fields = Vector{Any}(undef, type_nfields) - anyrefine = false - for i = 1:type_nfields - ai = getfield_tfunc(lattice, typea, Const(i)) - bi = getfield_tfunc(lattice, typeb, Const(i)) + if typea isa PartialStruct + if typeb isa PartialStruct + nflds = min(length(typea.fields), length(typeb.fields)) + else + nflds = min(length(typea.fields), n_initialized(typeb::Const)) + end + elseif typeb isa PartialStruct + nflds = min(n_initialized(typea::Const), length(typeb.fields)) + else + nflds = min(n_initialized(typea::Const), n_initialized(typeb::Const)) + end + nflds == 0 && return nothing + fields = Vector{Any}(undef, nflds) + anyrefine = nflds > datatype_min_ninitialized(unwrap_unionall(aty)) + for i = 1:nflds + ai = getfield_tfunc(𝕃, typea, Const(i)) + bi = getfield_tfunc(𝕃, typeb, Const(i)) # N.B.: We're assuming here that !isType(aty), because that case # only arises when typea === typeb, which should have been caught # before calling this. ft = fieldtype(aty, i) - if is_lattice_equal(lattice, ai, bi) || is_lattice_equal(lattice, ai, ft) + if is_lattice_equal(𝕃, ai, bi) || is_lattice_equal(𝕃, ai, ft) # Since ai===bi, the given type has no restrictions on complexity. # and can be used to refine ft tyi = ai - elseif is_lattice_equal(lattice, bi, ft) + elseif is_lattice_equal(𝕃, bi, ft) tyi = bi - elseif (tyi′ = tmerge_field(lattice, ai, bi); tyi′ !== nothing) + elseif (tyi′ = tmerge_field(𝕃, ai, bi); tyi′ !== nothing) # allow external lattice implementation to provide a custom field-merge strategy tyi = tyi′ else @@ -621,8 +637,8 @@ end end fields[i] = tyi if !anyrefine - anyrefine = has_nontrivial_extended_info(lattice, tyi) || # extended information - ⋤(lattice, tyi, ft) # just a type-level information, but more precise than the declared type + anyrefine = has_nontrivial_extended_info(𝕃, tyi) || # extended information + ⋤(𝕃, tyi, ft) # just a type-level information, but more precise than the declared type end end anyrefine && return PartialStruct(aty, fields) diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index d8ea8be21fe07..31c21f7228014 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -2139,21 +2139,13 @@ end # ======================== # propagate escapes imposed on call arguments -@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) -let result = code_escapes() do - broadcast_noescape1(Ref("Hi")) - end - i = only(findall(isnew, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)]) - @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # TODO `getfield(RefValue{String}, :x)` isn't safe -end @noinline broadcast_noescape2(b) = broadcast(identity, b) let result = code_escapes() do broadcast_noescape2(Ref("Hi")) end i = only(findall(isnew, result.ir.stmts.stmt)) @test_broken !has_return_escape(result.state[SSAValue(i)]) # TODO interprocedural alias analysis - @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # TODO `getfield(RefValue{String}, :x)` isn't safe + @test !has_thrown_escape(result.state[SSAValue(i)]) end @noinline allescape_argument(a) = (global GV = a) # obvious escape let result = code_escapes() do diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9ae98b884bef4..75f33a280e245 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1538,7 +1538,7 @@ let nfields_tfunc(@nospecialize xs...) = @test sizeof_nothrow(String) @test !sizeof_nothrow(Type{String}) @test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32})) - let PT = Core.Compiler.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) + let PT = Core.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) @test sizeof_tfunc(PT) === Const(16) @test nfields_tfunc(PT) === Const(2) @test sizeof_nothrow(PT) @@ -4743,32 +4743,80 @@ end # issue #43784 @testset "issue #43784" begin - init = Base.ImmutableDict{Any,Any}() - a = Const(init) - b = Core.PartialStruct(typeof(init), Any[Const(init), Any, Any]) - c = Core.Compiler.tmerge(a, b) - @test ⊑(a, c) - @test ⊑(b, c) - - init = Base.ImmutableDict{Number,Number}() - a = Const(init) - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test ⊑(a, c) && ⊑(b, c) - @test c === typeof(init) - - a = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test ⊑(a, c) && ⊑(b, c) - @test c.fields[2] === Any # or Number - @test c.fields[3] === ComplexF64 - - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) - c = Core.Compiler.tmerge(a, b) - @test ⊑(a, c) - @test ⊑(b, c) - @test c.fields[2] === Complex - @test c.fields[3] === Complex + ⊑ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) + ⊔ = Core.Compiler.join(Core.Compiler.fallback_lattice) + Const, PartialStruct = Core.Const, Core.PartialStruct + + let init = Base.ImmutableDict{Any,Any}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Any, Any]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Any,Any}(1,2) + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(getfield(init,1)), Any, Any]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c isa PartialStruct + @test length(c.fields) == 3 + end + let init = Base.ImmutableDict{Number,Number}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c isa PartialStruct + @test c.fields[2] === Number + @test c.fields[3] === ComplexF64 + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c isa PartialStruct + @test c.fields[2] === Complex + @test c.fields[3] === Complex + end + let T = Base.ImmutableDict{Number,Number} + a = PartialStruct(T, Any[T]) + b = PartialStruct(T, Any[T, Number, Number]) + @test b ⊑ a + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c isa PartialStruct + @test length(c.fields) == 1 + end + let T = Base.ImmutableDict{Number,Number} + a = PartialStruct(T, Any[T]) + b = Const(T()) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c === T + end + let T = Base.ImmutableDict{Number,Number} + a = Const(T()) + b = PartialStruct(T, Any[T]) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c === T + end + let T = Base.ImmutableDict{Number,Number} + a = Const(T()) + b = Const(T(1,2)) + c = a ⊔ b + @test a ⊑ c && b ⊑ c + @test c === T + end global const ginit43784 = Base.ImmutableDict{Any,Any}() @test Base.return_types() do @@ -4802,6 +4850,31 @@ end @test a == Tuple end +let ⊑ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) + ⊔ = Core.Compiler.join(Core.Compiler.fallback_lattice) + Const, PartialStruct = Core.Const, Core.PartialStruct + + @test (Const((1,2)) ⊑ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) + @test !(Const((1,2)) ⊑ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test !(Const((1,2,3)) ⊑ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) + @test (Const((1,2,3)) ⊑ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test (Const((1,2)) ⊑ PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}])) + @test (Const((1,2)) ⊑ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) broken=true + @test (Const((1,2,3)) ⊑ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) + @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) ⊑ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) ⊑ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) ⊑ Const((1,2,3))) + @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) ⊑ Const((1,2,3))) + @test !(PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}]) ⊑ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) ⊑ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) ⊑ Const((1,2,3))) + + t = Const((false, false)) ⊔ Const((false, true)) + @test t isa PartialStruct && length(t.fields) == 2 && t.fields[1] === Const(false) + t = t ⊔ Const((false, false, 0)) + @test t ⊑ Union{Tuple{Bool,Bool},Tuple{Bool,Bool,Int}} +end + # Test that a function-wise `@max_methods` works as expected Base.Experimental.@max_methods 1 function f_max_methods end f_max_methods(x::Int) = 1 @@ -5867,6 +5940,106 @@ bar54341(args...) = foo54341(4, args...) @test Core.Compiler.return_type(bar54341, Tuple{Vararg{Int}}) === Int +# `PartialStruct` for partially initialized structs: +struct PartiallyInitialized1 + a; b; c + PartiallyInitialized1(a) = (@nospecialize; new(a)) + PartiallyInitialized1(a, b) = (@nospecialize; new(a, b)) + PartiallyInitialized1(a, b, c) = (@nospecialize; new(a, b, c)) +end +mutable struct PartiallyInitialized2 + a; b; c + PartiallyInitialized2(a) = (@nospecialize; new(a)) + PartiallyInitialized2(a, b) = (@nospecialize; new(a, b)) + PartiallyInitialized2(a, b, c) = (@nospecialize; new(a, b, c)) +end + +# 1. isdefined modeling for partial struct +@test Base.infer_return_type((Any,Any)) do a, b + Val(isdefined(PartiallyInitialized1(a, b), :b)) +end == Val{true} +@test Base.infer_return_type((Any,Any,)) do a, b + Val(isdefined(PartiallyInitialized1(a, b), :c)) +end >: Val{false} +@test Base.infer_return_type((PartiallyInitialized1,)) do x + @assert isdefined(x, :a) + return Val(isdefined(x, :c)) +end == Val +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + Val(isdefined(PartiallyInitialized1(a, b, c), :c)) +end == Val{true} +@test Base.infer_return_type((Any,Any)) do a, b + Val(isdefined(PartiallyInitialized2(a, b), :b)) +end == Val{true} +@test Base.infer_return_type((Any,Any,)) do a, b + Val(isdefined(PartiallyInitialized2(a, b), :c)) +end >: Val{false} +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + s = PartiallyInitialized2(a, b) + s.c = c + Val(isdefined(s, :c)) +end >: Val{true} +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + Val(isdefined(PartiallyInitialized2(a, b, c), :c)) +end == Val{true} +@test Base.infer_return_type((Vector{Int},)) do xs + Val(isdefined(tuple(1, xs...), 1)) +end == Val{true} +@test Base.infer_return_type((Vector{Int},)) do xs + Val(isdefined(tuple(1, xs...), 2)) +end == Val + +# 2. getfield modeling for partial struct +@test Base.infer_effects((Any,Any); optimize=false) do a, b + getfield(PartiallyInitialized1(a, b), :b) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Symbol,); optimize=false) do a, b, f + getfield(PartiallyInitialized1(a, b), f, #=boundscheck=#false) +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any); optimize=false) do a, b, c + getfield(PartiallyInitialized1(a, b, c), :c) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any,Symbol); optimize=false) do a, b, c, f + getfield(PartiallyInitialized1(a, b, c), f, #=boundscheck=#false) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any); optimize=false) do a, b + getfield(PartiallyInitialized2(a, b), :b) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Symbol,); optimize=false) do a, b, f + getfield(PartiallyInitialized2(a, b), f, #=boundscheck=#false) +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any); optimize=false) do a, b, c + getfield(PartiallyInitialized2(a, b, c), :c) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any,Symbol); optimize=false) do a, b, c, f + getfield(PartiallyInitialized2(a, b, c), f, #=boundscheck=#false) +end |> Core.Compiler.is_nothrow + +# isdefined-Conditionals +@test Base.infer_effects((Base.RefValue{Any},)) do x + if isdefined(x, :x) + return getfield(x, :x) + end +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Base.RefValue{Any},)) do x + if isassigned(x) + return x[] + end +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any); optimize=false) do a, c + x = PartiallyInitialized2(a) + x.c = c + if isdefined(x, :c) + return x.b + end +end |> !Core.Compiler.is_nothrow + +# End to end test case for the partially initialized struct with `PartialStruct` +@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) +@test fully_eliminated() do + broadcast_noescape1(Ref("x")) +end + # InterConditional rt with Vararg argtypes fcondvarargs(a, b, c, d) = isa(d, Int64) gcondvarargs(a, x...) = return fcondvarargs(a, x...) ? isa(a, Int64) : !isa(a, Int64) diff --git a/test/tuple.jl b/test/tuple.jl index b1894bd2bb6ce..355ad965f9584 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -533,7 +533,7 @@ end @test ntuple(identity, Val(n)) == ntuple(identity, n) end - @test Core.Compiler.return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} + @test Base.infer_return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} end struct A_15703{N} @@ -835,8 +835,8 @@ end @test @inferred(Base.circshift(t3, 7)) == ('b', 'c', 'd', 'a') @test @inferred(Base.circshift(t3, -1)) == ('b', 'c', 'd', 'a') @test_throws MethodError circshift(t1, 'a') - @test Core.Compiler.return_type(circshift, Tuple{Tuple,Integer}) <: Tuple - @test Core.Compiler.return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} + @test Base.infer_return_type(circshift, Tuple{Tuple,Integer}) <: Tuple + @test Base.infer_return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} for len ∈ 0:5 v = 1:len t = Tuple(v) From 31d413eff6066c8d93e77a85338cbc0413a45174 Mon Sep 17 00:00:00 2001 From: Nathan Boyer <65452054+nathanrboyer@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:31:31 -0400 Subject: [PATCH 092/548] Explain `walkdir` docstring second example. (#55541) This is a follow-up documentation PR to #55476. I believe the second example in the `walkdir` docstring is still unintuitive since the result changes each time. I attempted a simple explanation, but I don't really know what I'm talking about. Hopefully someone else can explain what is happening better. Some additional discussion in [this Discourse post](https://discourse.julialang.org/t/find-all-files-named-findthis-csv-in-nested-subfolders-of-rootfolder/118096). --------- Co-authored-by: Lilith Orion Hafner --- base/file.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/file.jl b/base/file.jl index 7fa4a288c1d10..81bca9dd65577 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1102,6 +1102,8 @@ The directory tree can be traversed top-down or bottom-up. If `walkdir` or `stat` encounters a `IOError` it will rethrow the error by default. A custom error handling function can be provided through `onerror` keyword argument. `onerror` is called with a `IOError` as argument. +The returned iterator is stateful so when accessed repeatedly each access will +resume where the last left off, like [`Iterators.Stateful`](@ref). See also: [`readdir`](@ref). From f2f76d8833541091ef13a47c49bb77b2263d9937 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 20 Aug 2024 14:39:55 -0300 Subject: [PATCH 093/548] Mark the _state field as atomic and move to proper atomics instead of llvmcall (#55502) --- base/task.jl | 16 +++------------- src/jltypes.c | 2 ++ stdlib/Serialization/src/Serialization.jl | 6 +++--- test/channels.jl | 2 +- test/core.jl | 1 + 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/base/task.jl b/base/task.jl index 5e4af6747f128..6cb1ff785eeee 100644 --- a/base/task.jl +++ b/base/task.jl @@ -156,20 +156,10 @@ const task_state_runnable = UInt8(0) const task_state_done = UInt8(1) const task_state_failed = UInt8(2) -const _state_index = findfirst(==(:_state), fieldnames(Task)) -@eval function load_state_acquire(t) - # TODO: Replace this by proper atomic operations when available - @GC.preserve t llvmcall($(""" - %rv = load atomic i8, i8* %0 acquire, align 8 - ret i8 %rv - """), UInt8, Tuple{Ptr{UInt8}}, - Ptr{UInt8}(pointer_from_objref(t) + fieldoffset(Task, _state_index))) -end - @inline function getproperty(t::Task, field::Symbol) if field === :state # TODO: this field name should be deprecated in 2.0 - st = load_state_acquire(t) + st = @atomic :acquire t._state if st === task_state_runnable return :runnable elseif st === task_state_done @@ -223,7 +213,7 @@ julia> istaskdone(b) true ``` """ -istaskdone(t::Task) = load_state_acquire(t) !== task_state_runnable +istaskdone(t::Task) = (@atomic :acquire t._state) !== task_state_runnable """ istaskstarted(t::Task) -> Bool @@ -267,7 +257,7 @@ true !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ -istaskfailed(t::Task) = (load_state_acquire(t) === task_state_failed) +istaskfailed(t::Task) = ((@atomic :acquire t._state) === task_state_failed) Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) function Threads.threadpool(t::Task) diff --git a/src/jltypes.c b/src/jltypes.c index 5516b8c9c0c6e..a587552aaa011 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3662,6 +3662,8 @@ void jl_init_types(void) JL_GC_DISABLED XX(task); jl_value_t *listt = jl_new_struct(jl_uniontype_type, jl_task_type, jl_nothing_type); jl_svecset(jl_task_type->types, 0, listt); + const static uint32_t task_atomicfields[1] = {0x00001000}; // Set fields 13 as atomic + jl_task_type->name->atomicfields = task_atomicfields; tv = jl_svec2(tvar("A"), tvar("R")); jl_opaque_closure_type = (jl_unionall_t*)jl_new_datatype(jl_symbol("OpaqueClosure"), core, jl_function_type, tv, diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 7600457812f66..bc476181e5b0d 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1570,11 +1570,11 @@ function deserialize(s::AbstractSerializer, ::Type{Task}) t.storage = deserialize(s) state = deserialize(s) if state === :runnable - t._state = Base.task_state_runnable + @atomic :release t._state = Base.task_state_runnable elseif state === :done - t._state = Base.task_state_done + @atomic :release t._state = Base.task_state_done elseif state === :failed - t._state = Base.task_state_failed + @atomic :release t._state = Base.task_state_failed else @assert false end diff --git a/test/channels.jl b/test/channels.jl index f1642de1b7bec..d3415a96afd12 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -382,7 +382,7 @@ end """error in running finalizer: ErrorException("task switch not allowed from inside gc finalizer")""", output)) # test for invalid state in Workqueue during yield t = @async nothing - t._state = 66 + @atomic t._state = 66 newstderr = redirect_stderr() try errstream = @async read(newstderr[1], String) diff --git a/test/core.jl b/test/core.jl index c643c965f89cc..692d91b6e05b3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -42,6 +42,7 @@ for (T, c) in ( (DataType, [:types, :layout]), (Core.Memory, []), (Core.GenericMemoryRef, []), + (Task, [:_state]) ) @test Set((fieldname(T, i) for i in 1:fieldcount(T) if Base.isfieldatomic(T, i))) == Set(c) end From 7b8dd90a1342f7862e9e3c8a87b50d66dded9b40 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 20 Aug 2024 16:07:06 -0400 Subject: [PATCH 094/548] inference: represent callers_in_cycle with view slices of a stack instead of separate lists (#55364) Inspired by Tarjan's SCC, this changes the recursion representation to use a single list instead of a linked-list + merged array of cycles. --- base/compiler/abstractinterpretation.jl | 33 +++-- base/compiler/inferencestate.jl | 164 +++++++++++++++-------- base/compiler/ssair/irinterp.jl | 8 +- base/compiler/typeinfer.jl | 146 +++++++++----------- stdlib/InteractiveUtils/test/runtests.jl | 20 ++- stdlib/REPL/src/REPLCompletions.jl | 4 +- 6 files changed, 219 insertions(+), 156 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 351f241878e7d..83abfc952bf8e 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -793,10 +793,10 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState, # otherwise, we don't # check in the cycle list first - # all items in here are mutual parents of all others + # all items in here are considered mutual parents of all others if !any(p::AbsIntState->matches_sv(p, sv), callers_in_cycle(frame)) let parent = frame_parent(frame) - parent !== nothing || return false + parent === nothing && return false (is_cached(parent) || frame_parent(parent) !== nothing) || return false matches_sv(parent, sv) || return false end @@ -1298,7 +1298,7 @@ function semi_concrete_eval_call(interp::AbstractInterpreter, if code !== nothing irsv = IRInterpretationState(interp, code, mi, arginfo.argtypes, world) if irsv !== nothing - irsv.parent = sv + assign_parentchild!(irsv, sv) rt, (nothrow, noub) = ir_abstract_constant_propagation(interp, irsv) @assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from irinterp" if !(isa(rt, Type) && hasintersect(rt, Bool)) @@ -1376,11 +1376,17 @@ function const_prop_call(interp::AbstractInterpreter, add_remark!(interp, sv, "[constprop] Could not retrieve the source") return nothing # this is probably a bad generated function (unsound), but just ignore it end - frame.parent = sv + assign_parentchild!(frame, sv) if !typeinf(interp, frame) add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle") + @assert frame.frameid != 0 && frame.cycleid == frame.frameid + callstack = frame.callstack::Vector{AbsIntState} + @assert callstack[end] === frame && length(callstack) == frame.frameid + pop!(callstack) return nothing end + @assert frame.frameid != 0 && frame.cycleid == frame.frameid + @assert frame.parentid == sv.frameid @assert inf_result.result !== nothing # ConditionalSimpleArgtypes is allowed, because the only case in which it modifies # the argtypes is when one of the argtypes is a `Conditional`, which case @@ -3328,7 +3334,6 @@ end # make as much progress on `frame` as possible (without handling cycles) function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) @assert !is_inferred(frame) - frame.dont_work_on_me = true # mark that this function is currently on the stack W = frame.ip ssavaluetypes = frame.ssavaluetypes bbs = frame.cfg.blocks @@ -3549,7 +3554,6 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end end # while currbb <= nbbs - frame.dont_work_on_me = false nothing end @@ -3601,16 +3605,23 @@ end # make as much progress on `frame` as possible (by handling cycles) function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState) typeinf_local(interp, frame) + @assert isempty(frame.ip) + callstack = frame.callstack::Vector{AbsIntState} + frame.cycleid == length(callstack) && return true - # If the current frame is part of a cycle, solve the cycle before finishing no_active_ips_in_callers = false - while !no_active_ips_in_callers + while true + # If the current frame is not the top part of a cycle, continue to the top of the cycle before resuming work + frame.cycleid == frame.frameid || return false + # If done, return and finalize this cycle + no_active_ips_in_callers && return true + # Otherwise, do at least one iteration over the entire current cycle no_active_ips_in_callers = true - for caller in frame.callers_in_cycle - caller.dont_work_on_me && return false # cycle is above us on the stack + for i = reverse(frame.cycleid:length(callstack)) + caller = callstack[i]::InferenceState if !isempty(caller.ip) # Note that `typeinf_local(interp, caller)` can potentially modify the other frames - # `frame.callers_in_cycle`, which is why making incremental progress requires the + # `frame.cycleid`, which is why making incremental progress requires the # outer while loop. typeinf_local(interp, caller) no_active_ips_in_callers = false diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 87647628f772e..6953dea5b9bd7 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -209,10 +209,10 @@ to enable flow-sensitive analysis. """ const VarTable = Vector{VarState} -const CACHE_MODE_NULL = 0x00 # not cached, without optimization -const CACHE_MODE_GLOBAL = 0x01 << 0 # cached globally, optimization allowed -const CACHE_MODE_LOCAL = 0x01 << 1 # cached locally, optimization allowed -const CACHE_MODE_VOLATILE = 0x01 << 2 # not cached, optimization allowed +const CACHE_MODE_NULL = 0x00 # not cached, optimization optional +const CACHE_MODE_GLOBAL = 0x01 << 0 # cached globally, optimization required +const CACHE_MODE_LOCAL = 0x01 << 1 # cached locally, optimization required +const CACHE_MODE_VOLATILE = 0x01 << 2 # not cached, optimization required mutable struct TryCatchFrame exct @@ -254,9 +254,12 @@ mutable struct InferenceState pclimitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue limitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return cycle_backedges::Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller - callers_in_cycle::Vector{InferenceState} - dont_work_on_me::Bool - parent # ::Union{Nothing,AbsIntState} + + # IPO tracking of in-process work, shared with all frames given AbstractInterpreter + callstack #::Vector{AbsIntState} + parentid::Int # index into callstack of the parent frame that originally added this frame (call frame_parent to extract the current parent of the SCC) + frameid::Int # index into callstack at which this object is found (or zero, if this is not a cached frame and has no parent) + cycleid::Int # index into the callstack of the topmost frame in the cycle (all frames in the same cycle share the same cycleid) #= results =# result::InferenceResult # remember where to put the result @@ -324,9 +327,7 @@ mutable struct InferenceState pclimitations = IdSet{InferenceState}() limitations = IdSet{InferenceState}() cycle_backedges = Vector{Tuple{InferenceState,Int}}() - callers_in_cycle = Vector{InferenceState}() - dont_work_on_me = false - parent = nothing + callstack = AbsIntState[] valid_worlds = WorldRange(1, get_world_counter()) bestguess = Bottom @@ -347,17 +348,23 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) - # some more setups - !iszero(cache_mode & CACHE_MODE_LOCAL) && push!(get_inference_cache(interp), result) - this = new( mi, world, mod, sptypes, slottypes, src, cfg, method_info, currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, - pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, + pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, interp) + # some more setups + if !iszero(cache_mode & CACHE_MODE_LOCAL) + push!(get_inference_cache(interp), result) + end + if !iszero(cache_mode & CACHE_MODE_GLOBAL) + push!(callstack, this) + this.cycleid = this.frameid = length(callstack) + end + # Apply generated function restrictions if src.min_world != 1 || src.max_world != typemax(UInt) # From generated functions @@ -769,30 +776,6 @@ function empty_backedges!(frame::InferenceState, currpc::Int=frame.currpc) return nothing end -function print_callstack(sv::InferenceState) - print("=================== Callstack: ==================\n") - idx = 0 - while sv !== nothing - print("[") - print(idx) - if !isa(sv.interp, NativeInterpreter) - print(", ") - print(typeof(sv.interp)) - end - print("] ") - print(sv.linfo) - is_cached(sv) || print(" [uncached]") - println() - for cycle in sv.callers_in_cycle - print(' ', cycle.linfo) - println() - end - sv = sv.parent - idx += 1 - end - print("================= End callstack ==================\n") -end - function narguments(sv::InferenceState, include_va::Bool=true) nargs = Int(sv.src.nargs) if !include_va @@ -818,7 +801,9 @@ mutable struct IRInterpretationState const lazyreachability::LazyCFGReachability valid_worlds::WorldRange const edges::Vector{Any} - parent # ::Union{Nothing,AbsIntState} + callstack #::Vector{AbsIntState} + frameid::Int + parentid::Int function IRInterpretationState(interp::AbstractInterpreter, method_info::MethodInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any}, @@ -841,9 +826,9 @@ mutable struct IRInterpretationState lazyreachability = LazyCFGReachability(ir) valid_worlds = WorldRange(min_world, max_world == typemax(UInt) ? get_world_counter() : max_world) edges = Any[] - parent = nothing + callstack = AbsIntState[] return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, - ssa_refined, lazyreachability, valid_worlds, edges, parent) + ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0, 0) end end @@ -863,11 +848,34 @@ function IRInterpretationState(interp::AbstractInterpreter, codeinst.min_world, codeinst.max_world) end + # AbsIntState # =========== const AbsIntState = Union{InferenceState,IRInterpretationState} +function print_callstack(frame::AbsIntState) + print("=================== Callstack: ==================\n") + frames = frame.callstack::Vector{AbsIntState} + for idx = (frame.frameid == 0 ? 0 : 1):length(frames) + sv = (idx == 0 ? frame : frames[idx]) + idx == frame.frameid && print("*") + print("[") + print(idx) + if sv isa InferenceState && !isa(sv.interp, NativeInterpreter) + print(", ") + print(typeof(sv.interp)) + end + print("] ") + print(frame_instance(sv)) + is_cached(sv) || print(" [uncached]") + sv.parentid == idx - 1 || print(" [parent=", sv.parentid, "]") + println() + @assert sv.frameid == idx + end + print("================= End callstack ==================\n") +end + frame_instance(sv::InferenceState) = sv.linfo frame_instance(sv::IRInterpretationState) = sv.mi @@ -878,8 +886,32 @@ function frame_module(sv::AbsIntState) return def.module end -frame_parent(sv::InferenceState) = sv.parent::Union{Nothing,AbsIntState} -frame_parent(sv::IRInterpretationState) = sv.parent::Union{Nothing,AbsIntState} +function frame_parent(sv::InferenceState) + sv.parentid == 0 && return nothing + callstack = sv.callstack::Vector{AbsIntState} + sv = callstack[sv.cycleid]::InferenceState + sv.parentid == 0 && return nothing + return callstack[sv.parentid] +end +frame_parent(sv::IRInterpretationState) = sv.parentid == 0 ? nothing : (sv.callstack::Vector{AbsIntState})[sv.parentid] + +# add the orphan child to the parent and the parent to the child +function assign_parentchild!(child::InferenceState, parent::AbsIntState) + @assert child.frameid in (0, 1) + child.callstack = callstack = parent.callstack::Vector{AbsIntState} + child.parentid = parent.frameid + push!(callstack, child) + child.cycleid = child.frameid = length(callstack) + nothing +end +function assign_parentchild!(child::IRInterpretationState, parent::AbsIntState) + @assert child.frameid in (0, 1) + child.callstack = callstack = parent.callstack::Vector{AbsIntState} + child.parentid = parent.frameid + push!(callstack, child) + child.frameid = length(callstack) + nothing +end function is_constproped(sv::InferenceState) (;overridden_by_const) = sv.result @@ -899,9 +931,6 @@ method_for_inference_limit_heuristics(sv::AbsIntState) = method_info(sv).method_ frame_world(sv::InferenceState) = sv.world frame_world(sv::IRInterpretationState) = sv.world -callers_in_cycle(sv::InferenceState) = sv.callers_in_cycle -callers_in_cycle(sv::IRInterpretationState) = () - function is_effect_overridden(sv::AbsIntState, effect::Symbol) if is_effect_overridden(frame_instance(sv), effect) return true @@ -938,20 +967,39 @@ Note that cycles may be visited in any order. struct AbsIntStackUnwind sv::AbsIntState end -iterate(unw::AbsIntStackUnwind) = (unw.sv, (unw.sv, 0)) -function iterate(unw::AbsIntStackUnwind, (sv, cyclei)::Tuple{AbsIntState, Int}) - # iterate through the cycle before walking to the parent - callers = callers_in_cycle(sv) - if callers !== () && cyclei < length(callers) - cyclei += 1 - parent = callers[cyclei] - else - cyclei = 0 - parent = frame_parent(sv) +iterate(unw::AbsIntStackUnwind) = (unw.sv, length(unw.sv.callstack::Vector{AbsIntState})) +function iterate(unw::AbsIntStackUnwind, frame::Int) + frame == 0 && return nothing + return ((unw.sv.callstack::Vector{AbsIntState})[frame], frame - 1) +end + +struct AbsIntCycle + frames::Vector{AbsIntState} + cycleid::Int + cycletop::Int +end +iterate(unw::AbsIntCycle) = unw.cycleid == 0 ? nothing : (unw.frames[unw.cycletop], unw.cycletop) +function iterate(unw::AbsIntCycle, frame::Int) + frame == unw.cycleid && return nothing + return (unw.frames[frame - 1], frame - 1) +end + +""" + callers_in_cycle(sv::AbsIntState) + +Iterate through all callers of the given `AbsIntState` in the abstract +interpretation stack (including the given `AbsIntState` itself) that are part +of the same cycle, only if it is part of a cycle with multiple frames. +""" +function callers_in_cycle(sv::InferenceState) + callstack = sv.callstack::Vector{AbsIntState} + cycletop = cycleid = sv.cycleid + while cycletop < length(callstack) && (callstack[cycletop + 1]::InferenceState).cycleid == cycleid + cycletop += 1 end - parent === nothing && return nothing - return (parent, (parent, cyclei)) + return AbsIntCycle(callstack, cycletop == cycleid ? 0 : cycleid, cycletop) end +callers_in_cycle(sv::IRInterpretationState) = AbsIntCycle(sv.callstack::Vector{AbsIntState}, 0, 0) # temporarily accumulate our edges to later add as backedges in the callee function add_backedge!(caller::InferenceState, mi::MethodInstance) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 83881354e494e..3d49be33f39d5 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -24,7 +24,7 @@ function concrete_eval_invoke(interp::AbstractInterpreter, ci::CodeInstance, arg end newirsv = IRInterpretationState(interp, ci, mi, argtypes, world) if newirsv !== nothing - newirsv.parent = parent + assign_parentchild!(newirsv, parent) return ir_abstract_constant_propagation(interp, newirsv) end return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects))) @@ -440,6 +440,12 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR store_backedges(frame_instance(irsv), irsv.edges) end + if irsv.frameid != 0 + callstack = irsv.callstack::Vector{AbsIntState} + @assert callstack[end] === irsv && length(callstack) == irsv.frameid + pop!(callstack) + end + return Pair{Any,Tuple{Bool,Bool}}(maybe_singleton_const(ultimate_rt), (nothrow, noub)) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 41fb774266f25..e2f2a1f2cc975 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -261,36 +261,37 @@ end 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 - if isempty(frames) - finish_nocycle(interp, frame) - elseif length(frames) == 1 - @assert frames[1] === frame "invalid callers_in_cycle" + frames = frame.callstack::Vector{AbsIntState} + if length(frames) == frame.cycleid finish_nocycle(interp, frame) else - finish_cycle(interp, frames) + @assert frame.cycleid != 0 + finish_cycle(interp, frames, frame.cycleid) end - empty!(frames) return true end function finish_nocycle(::AbstractInterpreter, frame::InferenceState) - frame.dont_work_on_me = true finishinfer!(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 frame.cycleid != 0 + frames = frame.callstack::Vector{AbsIntState} + @assert frames[end] === frame + pop!(frames) + end return nothing end -function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) +function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int) cycle_valid_worlds = WorldRange() cycle_valid_effects = EFFECTS_TOTAL - for caller in frames - @assert !(caller.dont_work_on_me) - caller.dont_work_on_me = true + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState + @assert caller.cycleid == cycleid # 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 @@ -298,19 +299,23 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finishinfer!(caller, caller.interp) end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState opt = caller.result.src if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` optimize(caller.interp, opt, caller.result) end end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState finish!(caller.interp, caller) end + resize!(frames, cycleid - 1) return nothing end @@ -396,9 +401,9 @@ end function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) if typ isa LimitedAccuracy - if sv.parent === nothing + if sv.parentid === 0 # we might have introduced a limit marker, but we should know it must be sv and other callers_in_cycle - #@assert !isempty(sv.callers_in_cycle) + #@assert !isempty(callers_in_cycle(sv)) # FIXME: this assert fails, appearing to indicate there is a bug in filtering this list earlier. # In particular (during doctests for example), during inference of # show(Base.IOContext{Base.GenericIOBuffer{Memory{UInt8}}}, Base.Multimedia.MIME{:var"text/plain"}, LinearAlgebra.BunchKaufman{Float64, Array{Float64, 2}, Array{Int64, 1}}) @@ -407,7 +412,7 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) end causes = copy(typ.causes) delete!(causes, sv) - for caller in sv.callers_in_cycle + for caller in callers_in_cycle(sv) delete!(causes, caller) end if isempty(causes) @@ -521,6 +526,7 @@ end # update the MethodInstance function finishinfer!(me::InferenceState, interp::AbstractInterpreter) # prepare to run optimization passes on fulltree + @assert isempty(me.ip) s_edges = get_stmt_edges!(me, 1) for i = 2:length(me.stmt_edges) isassigned(me.stmt_edges, i) || continue @@ -541,7 +547,7 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) gt = me.ssavaluetypes for j = 1:length(gt) gt[j] = gtj = cycle_fix_limited(gt[j], me) - if gtj isa LimitedAccuracy && me.parent !== nothing + if gtj isa LimitedAccuracy && me.parentid != 0 limited_src = true break end @@ -573,10 +579,10 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) type_annotate!(interp, me) mayopt = may_optimize(interp) doopt = mayopt && - # disable optimization if we don't use this later - (me.cache_mode != CACHE_MODE_NULL || me.parent !== nothing) && + # disable optimization if we don't use this later (because it is not cached) + me.cache_mode != CACHE_MODE_NULL && # disable optimization if we've already obtained very accurate result - !result_is_constabi(interp, result, mayopt) + !result_is_constabi(interp, result) if doopt result.src = OptimizationState(me, interp) else @@ -746,41 +752,29 @@ function type_annotate!(interp::AbstractInterpreter, sv::InferenceState) return nothing end -# at the end, all items in b's cycle -# will now be added to a's cycle -function union_caller_cycle!(a::InferenceState, b::InferenceState) - callers_in_cycle = b.callers_in_cycle - b.parent = a.parent - b.callers_in_cycle = a.callers_in_cycle - contains_is(a.callers_in_cycle, b) || push!(a.callers_in_cycle, b) - if callers_in_cycle !== a.callers_in_cycle - for caller in callers_in_cycle - if caller !== b - caller.parent = a.parent - caller.callers_in_cycle = a.callers_in_cycle - push!(a.callers_in_cycle, caller) - end - end - end - return -end - -function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, ancestor::InferenceState, child::InferenceState) +function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, child::InferenceState) # add backedge of parent <- child # then add all backedges of parent <- parent.parent - # and merge all of the callers into ancestor.callers_in_cycle - # and ensure that walking the parent list will get the same result (DAG) from everywhere + frames = parent.callstack::Vector{AbsIntState} + @assert child.callstack === frames + ancestorid = child.cycleid while true add_cycle_backedge!(parent, child) - union_caller_cycle!(ancestor, child) + parent.cycleid === ancestorid && break child = parent - child === ancestor && break parent = frame_parent(child) while !isa(parent, InferenceState) # XXX we may miss some edges here? parent = frame_parent(parent::IRInterpretationState) end - parent = parent::InferenceState + end + # ensure that walking the callstack has the same cycleid (DAG) + for frame = reverse(ancestorid:length(frames)) + frame = frames[frame] + frame isa InferenceState || continue + frame.cycleid == ancestorid && break + @assert frame.cycleid > ancestorid + frame.cycleid = ancestorid end end @@ -796,8 +790,8 @@ end # Walk through `mi`'s upstream call chain, starting at `parent`. If a parent # frame matching `mi` is encountered, then there is a cycle in the call graph # (i.e. `mi` is a descendant callee of itself). Upon encountering this cycle, -# we "resolve" it by merging the call chain, which entails unioning each intermediary -# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, +# we "resolve" it by merging the call chain, which entails updating each intermediary +# frame's `cycleid` field and adding the appropriate backedges. Finally, # we return `mi`'s pre-existing frame. If no cycles are found, `nothing` is # returned instead. function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::AbsIntState) @@ -805,10 +799,11 @@ function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, pa # This works just because currently the `:terminate` condition guarantees that # irinterp doesn't fail into unresolved cycles, but it's not a good solution. # We should revisit this once we have a better story for handling cycles in irinterp. - isa(parent, InferenceState) || return false - frame = parent + frames = parent.callstack::Vector{AbsIntState} uncached = false - while isa(frame, InferenceState) + for frame = reverse(1:length(frames)) + frame = frames[frame] + isa(frame, InferenceState) || break uncached |= !is_cached(frame) # ensure we never add an uncached frame to a cycle if is_same_frame(interp, mi, frame) if uncached @@ -818,20 +813,9 @@ function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, pa poison_callstack!(parent, frame) return true end - merge_call_chain!(interp, parent, frame, frame) + merge_call_chain!(interp, parent, frame) return frame end - for caller in callers_in_cycle(frame) - if is_same_frame(interp, mi, caller) - if uncached - poison_callstack!(parent, frame) - return true - end - merge_call_chain!(interp, parent, frame, caller) - return caller - end - end - frame = frame_parent(frame) end return false end @@ -920,9 +904,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end return EdgeCallResult(Any, Any, nothing, Effects()) end - if is_cached(caller) || frame_parent(caller) !== nothing # don't involve uncached functions in cycle resolution - frame.parent = caller - end + assign_parentchild!(frame, caller) typeinf(interp, frame) update_valid_age!(caller, frame.valid_worlds) isinferred = is_inferred(frame) @@ -1011,9 +993,8 @@ function codeinstance_for_const_with_code(interp::AbstractInterpreter, code::Cod code.relocatability, src.debuginfo) end -result_is_constabi(interp::AbstractInterpreter, result::InferenceResult, - run_optimizer::Bool=may_optimize(interp)) = - run_optimizer && may_discard_trees(interp) && is_result_constabi_eligible(result) +result_is_constabi(interp::AbstractInterpreter, result::InferenceResult) = + may_discard_trees(interp) && is_result_constabi_eligible(result) # compute an inferred AST and return type typeinf_code(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) = @@ -1024,11 +1005,6 @@ typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), function typeinf_code(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) frame = typeinf_frame(interp, mi, run_optimizer) frame === nothing && return nothing - is_inferred(frame) || return nothing - if result_is_constabi(interp, frame.result, run_optimizer) - rt = frame.result.result::Const - return codeinfo_for_const(interp, frame.linfo, rt.val) - end return frame.src end @@ -1051,17 +1027,14 @@ typeinf_ircode(interp::AbstractInterpreter, method::Method, @nospecialize(atype) typeinf_ircode(interp, specialize_method(method, atype, sparams), optimize_until) function typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance, optimize_until::Union{Integer,AbstractString,Nothing}) - start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) frame = typeinf_frame(interp, mi, false) if frame === nothing - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return nothing, Any end (; result) = frame opt = OptimizationState(frame, interp) ir = run_passes_ipo_safe(opt.src, opt, result, optimize_until) rt = widenconst(ignorelimited(result.result)) - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return ir, rt end @@ -1072,13 +1045,22 @@ typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), run_optimizer::Bool) = typeinf_frame(interp, specialize_method(method, atype, sparams), run_optimizer) function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) - start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) result = InferenceResult(mi, typeinf_lattice(interp)) - cache_mode = run_optimizer ? :global : :no - frame = InferenceState(result, cache_mode, interp) + frame = InferenceState(result, #=cache_mode=#:no, interp) frame === nothing && return nothing typeinf(interp, frame) - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + is_inferred(frame) || return nothing + if run_optimizer + if result_is_constabi(interp, frame.result) + rt = frame.result.result::Const + opt = codeinfo_for_const(interp, frame.linfo, rt.val) + else + opt = OptimizationState(frame, interp) + optimize(interp, opt, frame.result) + opt = ir_to_codeinf!(opt) + end + result.src = frame.src = opt + end return frame end diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index b000f353443c4..424564b70384c 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -394,13 +394,21 @@ let errf = tempname(), try redirect_stderr(new_stderr) @test occursin("f_broken_code", sprint(code_native, h_broken_code, ())) + Libc.flush_cstdio() println(new_stderr, "start") flush(new_stderr) - @test_throws "could not compile the specified method" sprint(code_native, f_broken_code, ()) + @test_throws "could not compile the specified method" sprint(io -> code_native(io, f_broken_code, (), dump_module=true)) Libc.flush_cstdio() - println(new_stderr, "end") + println(new_stderr, "middle") + flush(new_stderr) + @test !isempty(sprint(io -> code_native(io, f_broken_code, (), dump_module=false))) + Libc.flush_cstdio() + println(new_stderr, "later") flush(new_stderr) @test invokelatest(g_broken_code) == 0 + Libc.flush_cstdio() + println(new_stderr, "end") + flush(new_stderr) finally Libc.flush_cstdio() redirect_stderr(old_stderr) @@ -410,6 +418,14 @@ let errf = tempname(), Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) || errstr + @test occursin("""\nmiddle + Internal error: encountered unexpected error during compilation of f_broken_code: + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") + """, errstr) || errstr + @test occursin("""\nlater + Internal error: encountered unexpected error during compilation of f_broken_code: + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") + """, errstr) || errstr @test endswith(errstr, "\nend\n") || errstr end rm(errf) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index dc21cfe529e46..77f7fdf15cc9c 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -603,7 +603,7 @@ is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && sv.cache_mode function is_call_graph_uncached(sv::CC.InferenceState) CC.is_cached(sv) && return false - parent = sv.parent + parent = CC.frame_parent(sv) parent === nothing && return true return is_call_graph_uncached(parent::CC.InferenceState) end @@ -626,7 +626,7 @@ function is_repl_frame_getproperty(sv::CC.InferenceState) def isa Method || return false def.name === :getproperty || return false CC.is_cached(sv) && return false - return is_repl_frame(sv.parent) + return is_repl_frame(CC.frame_parent(sv)) end # aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame` From a2b1b4e335a9cc97e502369840fa1a47ed44bf2a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 20 Aug 2024 16:42:30 -0400 Subject: [PATCH 095/548] Reduce size of Task object (#55515) Move the registers onto the stack, so that they only are present when the Task is actually switched out, saving memory when the Task is not running yet or already finished. It makes this mostly just a huge renaming job. On Linux x86_64 this reduces it from 376 bytes to 184 bytes. Has some additional advantages too, such as copy_stack tasks (e.g. with always_copy_stacks) can migrate to other threads before starting if they are not sticky. Also fixes a variable that got mixed up by #54639 and caused always_copy_stacks to abort, since the stack limits were wrong. Also now fixes https://github.com/JuliaLang/julia/issues/43124, though I am not quite confident enough in it to re-enable that test right now. --- src/gc-debug.c | 8 +- src/gc-stacks.c | 22 +- src/gc-stock.c | 10 +- src/init.c | 21 +- src/julia.h | 6 +- src/julia_internal.h | 3 +- src/julia_threads.h | 14 +- src/signals-unix.c | 6 +- src/stackwalk.c | 65 +++--- src/task.c | 503 +++++++++++++++++++++++-------------------- 10 files changed, 342 insertions(+), 316 deletions(-) diff --git a/src/gc-debug.c b/src/gc-debug.c index ec3c8d731edd8..19dd93af5f236 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -537,13 +537,13 @@ static void gc_scrub_task(jl_task_t *ta) char *low; char *high; - if (ta->copy_stack && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) { + if (ta->ctx.copy_stack && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) { low = (char*)ptls2->stackbase - ptls2->stacksize; high = (char*)ptls2->stackbase; } - else if (ta->stkbuf) { - low = (char*)ta->stkbuf; - high = (char*)ta->stkbuf + ta->bufsz; + else if (ta->ctx.stkbuf) { + low = (char*)ta->ctx.stkbuf; + high = (char*)ta->ctx.stkbuf + ta->ctx.bufsz; } else return; diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 5706f4ce67c1d..65173d3a8df37 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -186,14 +186,14 @@ JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz) void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task) { // avoid adding an original thread stack to the free list - if (task == ptls->root_task && !task->copy_stack) + if (task == ptls->root_task && !task->ctx.copy_stack) return; - void *stkbuf = task->stkbuf; - size_t bufsz = task->bufsz; + void *stkbuf = task->ctx.stkbuf; + size_t bufsz = task->ctx.bufsz; if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(bufsz); if (pool_sizes[pool_id] == bufsz) { - task->stkbuf = NULL; + task->ctx.stkbuf = NULL; #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); #endif @@ -296,17 +296,17 @@ void sweep_stack_pools(void) JL_NOTSAFEPOINT jl_task_t *t = (jl_task_t*)lst[n]; assert(jl_is_task(t)); if (gc_marked(jl_astaggedvalue(t)->bits.gc)) { - if (t->stkbuf == NULL) + if (t->ctx.stkbuf == NULL) ndel++; // jl_release_task_stack called else n++; } else { ndel++; - void *stkbuf = t->stkbuf; - size_t bufsz = t->bufsz; + void *stkbuf = t->ctx.stkbuf; + size_t bufsz = t->ctx.bufsz; if (stkbuf) { - t->stkbuf = NULL; + t->ctx.stkbuf = NULL; _jl_free_stack(ptls2, stkbuf, bufsz); } #ifdef _COMPILER_TSAN_ENABLED_ @@ -338,7 +338,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) continue; small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; size_t n = mtarraylist_length(live_tasks); - l += n + (ptls2->root_task->stkbuf != NULL); + l += n + (ptls2->root_task->ctx.stkbuf != NULL); } l += l / 20; // add 5% for margin of estimation error jl_array_t *a = jl_alloc_vec_any(l); // may gc, changing the number of tasks and forcing us to reload everything @@ -350,7 +350,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) if (ptls2 == NULL) continue; jl_task_t *t = ptls2->root_task; - if (t->stkbuf != NULL) { + if (t->ctx.stkbuf != NULL) { if (j == l) goto restart; jl_array_data(a,void*)[j++] = t; @@ -359,7 +359,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) size_t n = mtarraylist_length(live_tasks); for (size_t i = 0; i < n; i++) { jl_task_t *t = (jl_task_t*)mtarraylist_get(live_tasks, i); - if (t->stkbuf != NULL) { + if (t->ctx.stkbuf != NULL) { if (j == l) goto restart; jl_array_data(a,void*)[j++] = t; diff --git a/src/gc-stock.c b/src/gc-stock.c index 1fc2087da6503..3ae14f378a2e7 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2144,9 +2144,9 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ (ta, tid != -1 && ta == gc_all_tls_states[tid]->root_task)); } #ifdef COPY_STACKS - void *stkbuf = ta->stkbuf; - if (stkbuf && ta->copy_stack) { - gc_setmark_buf_(ptls, stkbuf, bits, ta->bufsz); + void *stkbuf = ta->ctx.stkbuf; + if (stkbuf && ta->ctx.copy_stack) { + gc_setmark_buf_(ptls, stkbuf, bits, ta->ctx.bufsz); // For gc_heap_snapshot_record: // TODO: attribute size of stack // TODO: edge to stack data @@ -2159,12 +2159,12 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ uintptr_t lb = 0; uintptr_t ub = (uintptr_t)-1; #ifdef COPY_STACKS - if (stkbuf && ta->copy_stack && !ta->ptls) { + if (stkbuf && ta->ctx.copy_stack && !ta->ptls) { int16_t tid = jl_atomic_load_relaxed(&ta->tid); assert(tid >= 0); jl_ptls_t ptls2 = gc_all_tls_states[tid]; ub = (uintptr_t)ptls2->stackbase; - lb = ub - ta->copy_stack; + lb = ub - ta->ctx.copy_stack; offset = (uintptr_t)stkbuf - lb; } #endif diff --git a/src/init.c b/src/init.c index eff786b564e54..9e6a695c71eb0 100644 --- a/src/init.c +++ b/src/init.c @@ -64,15 +64,19 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi) // threads since it seems to return bogus values for master thread on Linux // and possibly OSX. if (!ismaster) { -# if defined(_OS_LINUX_) +# if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) pthread_attr_t attr; +#if defined(_OS_FREEBSD_) + pthread_attr_get_np(pthread_self(), &attr); +#else pthread_getattr_np(pthread_self(), &attr); +#endif void *stackaddr; size_t stacksize; pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_destroy(&attr); - *stack_hi = stackaddr; - *stack_lo = (char*)stackaddr - stacksize; + *stack_lo = stackaddr; + *stack_hi = (char*)stackaddr + stacksize; return; # elif defined(_OS_DARWIN_) extern void *pthread_get_stackaddr_np(pthread_t thread); @@ -80,19 +84,8 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi) pthread_t thread = pthread_self(); void *stackaddr = pthread_get_stackaddr_np(thread); size_t stacksize = pthread_get_stacksize_np(thread); - *stack_hi = stackaddr; *stack_lo = (char*)stackaddr - stacksize; - return; -# elif defined(_OS_FREEBSD_) - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_get_np(pthread_self(), &attr); - void *stackaddr; - size_t stacksize; - pthread_attr_getstack(&attr, &stackaddr, &stacksize); - pthread_attr_destroy(&attr); *stack_hi = stackaddr; - *stack_lo = (char*)stackaddr - stacksize; return; # else # warning "Getting precise stack size for thread is not supported." diff --git a/src/julia.h b/src/julia.h index e211f31c6512c..074c50fd0aa21 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2231,11 +2231,7 @@ typedef struct _jl_task_t { // current exception handler jl_handler_t *eh; // saved thread state - jl_ucontext_t ctx; - void *stkbuf; // malloc'd memory (either copybuf or stack) - size_t bufsz; // actual sizeof stkbuf - unsigned int copy_stack:31; // sizeof stack for copybuf - unsigned int started:1; + jl_ucontext_t ctx; // pointer into stkbuf, if suspended } jl_task_t; #define JL_TASK_STATE_RUNNABLE 0 diff --git a/src/julia_internal.h b/src/julia_internal.h index d4d1a3239785c..23a9c90edf8aa 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -65,7 +65,8 @@ static inline void asan_unpoison_task_stack(jl_task_t *ct, jl_jmp_buf *buf) that we're resetting to. The idea is to remove the poison from the frames that we're skipping over, since they won't be unwound. */ uintptr_t top = jmpbuf_sp(buf); - uintptr_t bottom = (uintptr_t)ct->stkbuf; + uintptr_t bottom = (uintptr_t)(ct->ctx.copy_stack ? (char*)ct->ptls->stackbase - ct->ptls->stacksize : (char*)ct->ctx.stkbuf); + //uintptr_t bottom = (uintptr_t)⊤ __asan_unpoison_stack_memory(bottom, top - bottom); } static inline void asan_unpoison_stack_memory(uintptr_t addr, size_t size) { diff --git a/src/julia_threads.h b/src/julia_threads.h index 9a2a8cec375f5..e56ff5edd6176 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -86,9 +86,13 @@ typedef ucontext_t _jl_ucontext_t; typedef struct { union { - _jl_ucontext_t ctx; - jl_stack_context_t copy_ctx; + _jl_ucontext_t *ctx; + jl_stack_context_t *copy_ctx; }; + void *stkbuf; // malloc'd memory (either copybuf or stack) + size_t bufsz; // actual sizeof stkbuf + unsigned int copy_stack:31; // sizeof stack for copybuf + unsigned int started:1; #if defined(_COMPILER_TSAN_ENABLED_) void *tsan_state; #endif @@ -155,13 +159,9 @@ typedef struct _jl_tls_states_t { struct _jl_task_t *previous_task; struct _jl_task_t *root_task; struct _jl_timing_block_t *timing_stack; + // This is the location of our copy_stack void *stackbase; size_t stacksize; - union { - _jl_ucontext_t base_ctx; // base context of stack - // This hack is needed to support always_copy_stacks: - jl_stack_context_t copy_stack_ctx; - }; // Temp storage for exception thrown in signal handler. Not rooted. struct _jl_value_t *sig_exception; // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. diff --git a/src/signals-unix.c b/src/signals-unix.c index edca523bed6d1..005422bea03d3 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -230,13 +230,13 @@ static pthread_t signals_thread; static int is_addr_on_stack(jl_task_t *ct, void *addr) { - if (ct->copy_stack) { + if (ct->ctx.copy_stack) { jl_ptls_t ptls = ct->ptls; return ((char*)addr > (char*)ptls->stackbase - ptls->stacksize && (char*)addr < (char*)ptls->stackbase); } - return ((char*)addr > (char*)ct->stkbuf && - (char*)addr < (char*)ct->stkbuf + ct->bufsz); + return ((char*)addr > (char*)ct->ctx.stkbuf && + (char*)addr < (char*)ct->ctx.stkbuf + ct->ctx.bufsz); } static void sigdie_handler(int sig, siginfo_t *info, void *context) diff --git a/src/stackwalk.c b/src/stackwalk.c index a63694e7c3b0c..15a9fddeac9a4 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -83,7 +83,7 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b skip--; } #endif -#if !defined(_OS_WINDOWS_) +#if !defined(_OS_WINDOWS_) // no point on windows, since RtlVirtualUnwind won't give us a second chance if the segfault happens in ntdll jl_jmp_buf *old_buf = jl_get_safe_restore(); jl_jmp_buf buf; jl_set_safe_restore(&buf); @@ -921,14 +921,12 @@ _os_ptr_munge(uintptr_t ptr) JL_NOTSAFEPOINT extern bt_context_t *jl_to_bt_context(void *sigctx); -static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT +JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, size_t max_bt_size) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; - ptls->bt_size = 0; if (t == ct) { - ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, 0); - return; + return rec_backtrace(bt_data, max_bt_size, 0); } bt_context_t *context = NULL; bt_context_t c; @@ -936,9 +934,11 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT while (!jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid) { int lockret = jl_lock_stackwalk(); // if this task is already running somewhere, we need to stop the thread it is running on and query its state - if (!jl_thread_suspend_and_get_state(old, 0, &c)) { + if (!jl_thread_suspend_and_get_state(old, 1, &c)) { jl_unlock_stackwalk(lockret); - return; + if (jl_atomic_load_relaxed(&t->tid) != old) + continue; + return 0; } jl_unlock_stackwalk(lockret); if (jl_atomic_load_relaxed(&t->tid) == old) { @@ -953,11 +953,11 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT // got the wrong thread stopped, try again jl_thread_resume(old); } - if (context == NULL && (!t->copy_stack && t->started && t->stkbuf != NULL)) { + if (context == NULL && (!t->ctx.copy_stack && t->ctx.started && t->ctx.ctx != NULL)) { // need to read the context from the task stored state #if defined(_OS_WINDOWS_) memset(&c, 0, sizeof(c)); - _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx.uc_mcontext; + _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx->uc_mcontext; #if defined(_CPU_X86_64_) c.Rbx = mctx->Rbx; c.Rsp = mctx->Rsp; @@ -979,13 +979,13 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT #endif context = &c; #elif defined(JL_HAVE_UNW_CONTEXT) - context = &t->ctx.ctx; + context = t->ctx.ctx; #elif defined(JL_HAVE_UCONTEXT) - context = jl_to_bt_context(&t->ctx.ctx); + context = jl_to_bt_context(t->ctx.ctx); #elif defined(JL_HAVE_ASM) memset(&c, 0, sizeof(c)); #if defined(_OS_LINUX_) && defined(__GLIBC__) - __jmp_buf *mctx = &t->ctx.ctx.uc_mcontext->__jmpbuf; + __jmp_buf *mctx = &t->ctx.ctx->uc_mcontext->__jmpbuf; mcontext_t *mc = &c.uc_mcontext; #if defined(_CPU_X86_) // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S @@ -1071,13 +1071,13 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT mc->pc = mc->regs[30]; context = &c; #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown linux") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown linux") (void)mc; (void)c; (void)mctx; #endif #elif defined(_OS_DARWIN_) - sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; + sigjmp_buf *mctx = &t->ctx.ctx->uc_mcontext; #if defined(_CPU_X86_64_) // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/x86_64/_setjmp.s x86_thread_state64_t *mc = (x86_thread_state64_t*)&c; @@ -1133,12 +1133,12 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT mc->__pad = 0; // aka __ra_sign_state = not signed context = &c; #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown darwin") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown darwin") (void)mctx; (void)c; #endif #elif defined(_OS_FREEBSD_) - sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; + sigjmp_buf *mctx = &t->ctx.ctx->uc_mcontext; mcontext_t *mc = &c.uc_mcontext; #if defined(_CPU_X86_64_) // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S @@ -1175,24 +1175,26 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT mc->mc_fpregs.fp_q[14] = ((long*)mctx)[20]; context = &c; #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown freebsd") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown freebsd") (void)mctx; (void)c; #endif #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown system") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown system") (void)c; #endif #else - #pragma message("jl_rec_backtrace not defined for unknown task system") + #pragma message("jl_record_backtrace not defined for unknown task system") #endif } + size_t bt_size = 0; if (context) - ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, context, t->gcstack); + bt_size = rec_backtrace_ctx(bt_data, max_bt_size, context, t->gcstack); if (old == -1) jl_atomic_store_relaxed(&t->tid, old); else if (old != ptls->tid) jl_thread_resume(old); + return bt_size; } //-------------------------------------------------- @@ -1224,12 +1226,15 @@ JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; - jl_rec_backtrace(t); - size_t i, bt_size = ptls->bt_size; + ptls->bt_size = 0; jl_bt_element_t *bt_data = ptls->bt_data; + size_t bt_size = jl_record_backtrace(t, bt_data, JL_MAX_BT_SIZE); + size_t i; for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { jl_print_bt_entry_codeloc(bt_data + i); } + if (bt_size == 0) + jl_safe_printf(" no backtrace recorded\n"); } JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT @@ -1269,14 +1274,9 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task); if (t != NULL) { jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - t->sticky, t->started, t_state, + t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - if (t->stkbuf != NULL) { - jlbacktracet(t); - } - else { - jl_safe_printf(" no stack\n"); - } + jlbacktracet(t); } jl_safe_printf(" ---- End root task\n"); } @@ -1291,12 +1291,9 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT jl_safe_printf(" ---- Task %zu (%p)\n", j + 1, t); // n.b. this information might not be consistent with the stack printing after it, since it could start running or change tid, etc. jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - t->sticky, t->started, t_state, + t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - if (t->stkbuf != NULL) - jlbacktracet(t); - else - jl_safe_printf(" no stack\n"); + jlbacktracet(t); jl_safe_printf(" ---- End task %zu\n", j + 1); } jl_safe_printf("==== End thread %d\n", ptls2->tid + 1); diff --git a/src/task.c b/src/task.c index 1f41ebd8cd2f8..a88f3b55fc419 100644 --- a/src/task.c +++ b/src/task.c @@ -49,27 +49,27 @@ extern "C" { // c.f. interceptor in jl_dlopen as well void (*real_siglongjmp)(jmp_buf _Buf, int _Value) = NULL; #endif -static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_task_t *from, jl_task_t *to) { +static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_ucontext_t *from, jl_ucontext_t *to) { if (to->copy_stack) - __sanitizer_start_switch_fiber(&from->ctx.asan_fake_stack, (char*)ptls->stackbase-ptls->stacksize, ptls->stacksize); + __sanitizer_start_switch_fiber(&from->asan_fake_stack, (char*)ptls->stackbase - ptls->stacksize, ptls->stacksize); else - __sanitizer_start_switch_fiber(&from->ctx.asan_fake_stack, to->stkbuf, to->bufsz); + __sanitizer_start_switch_fiber(&from->asan_fake_stack, to->stkbuf, to->bufsz); } -static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_task_t *to) { +static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_ucontext_t *to) { if (to->copy_stack) - __sanitizer_start_switch_fiber(NULL, (char*)ptls->stackbase-ptls->stacksize, ptls->stacksize); + __sanitizer_start_switch_fiber(NULL, (char*)ptls->stackbase - ptls->stacksize, ptls->stacksize); else __sanitizer_start_switch_fiber(NULL, to->stkbuf, to->bufsz); } -static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *current) { - __sanitizer_finish_switch_fiber(current->ctx.asan_fake_stack, NULL, NULL); +static inline void sanitizer_finish_switch_fiber(jl_ucontext_t *last, jl_ucontext_t *current) { + __sanitizer_finish_switch_fiber(current->asan_fake_stack, NULL, NULL); //(const void**)&last->stkbuf, //&last->bufsz); } #else -static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT {} -static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_task_t *to) JL_NOTSAFEPOINT {} -static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *current) JL_NOTSAFEPOINT {} +static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_ucontext_t *from, jl_ucontext_t *to) JL_NOTSAFEPOINT {} +static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_ucontext_t *to) JL_NOTSAFEPOINT {} +static inline void sanitizer_finish_switch_fiber(jl_ucontext_t *last, jl_ucontext_t *current) JL_NOTSAFEPOINT {} #endif #if defined(_COMPILER_TSAN_ENABLED_) @@ -85,19 +85,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \ } while (0) -#ifdef COPY_STACKS -#define tsan_destroy_copyctx(_ptls, _ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \ - __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \ - } \ - _tsan_macro_ctx->tsan_state = NULL; \ - } while (0) -#define tsan_switch_to_copyctx(_ctx) do { \ - struct jl_stack_context_t *_tsan_macro_ctx = (_ctx); \ - __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \ - } while (0) -#endif #else // just do minimal type-checking on the arguments #define tsan_destroy_ctx(_ptls, _ctx) do { \ @@ -108,16 +95,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ (void)_tsan_macro_ctx; \ } while (0) -#ifdef COPY_STACKS -#define tsan_destroy_copyctx(_ptls, _ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - (void)_tsan_macro_ctx; \ - } while (0) -#define tsan_switch_to_copyctx(_ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - (void)_tsan_macro_ctx; \ - } while (0) -#endif #endif // empirically, jl_finish_task needs about 64k stack space to infer/run @@ -134,7 +111,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur #define ROOT_TASK_STACK_ADJUSTMENT 3000000 #endif -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT; static void jl_set_fiber(jl_ucontext_t *t); static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t); static void jl_start_fiber_swap(jl_ucontext_t *savet, jl_ucontext_t *t); @@ -214,17 +190,17 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt assert(stackbase > frame_addr); size_t nb = stackbase - frame_addr; void *buf; - if (lastt->bufsz < nb) { - asan_free_copy_stack(lastt->stkbuf, lastt->bufsz); + if (lastt->ctx.bufsz < nb) { + asan_free_copy_stack(lastt->ctx.stkbuf, lastt->ctx.bufsz); buf = (void*)jl_gc_alloc_buf(ptls, nb); - lastt->stkbuf = buf; - lastt->bufsz = nb; + lastt->ctx.stkbuf = buf; + lastt->ctx.bufsz = nb; } else { - buf = lastt->stkbuf; + buf = lastt->ctx.stkbuf; } *pt = NULL; // clear the gc-root for the target task before copying the stack for saving - lastt->copy_stack = nb; + lastt->ctx.copy_stack = nb; lastt->sticky = 1; memcpy_stack_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb); // this task's stack could have been modified after @@ -233,58 +209,97 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt jl_gc_wb_back(lastt); } -JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_task_t *t, jl_ptls_t ptls, char *p) +JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_ptls_t ptls, char *p) { size_t nb = t->copy_stack; char *_x = (char*)ptls->stackbase - nb; if (!p) { // switch to a stackframe that's beyond the bounds of the last switch - p = _x; - if ((char*)&_x > _x) { - p = (char*)alloca((char*)&_x - _x); + p = _x - 4096; + if ((char*)&_x > p) { + p = (char*)alloca((char*)&_x - p); } restore_stack(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca } void *_y = t->stkbuf; assert(_x != NULL && _y != NULL); +#if defined(_CPU_X86_) || defined(_CPU_X86_64_) + void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; + assert(*return_address == __builtin_return_address(0)); + *return_address = NULL; +#else +#pragma message("warning: CFI_NORETURN not implemented for this platform, so profiling of copy_stacks may segfault in this build") +#endif +CFI_NORETURN memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe #if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); + jl_setcontext(t->copy_ctx); #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); + jl_longjmp(t->copy_ctx->uc_mcontext, 1); #endif abort(); // unreachable } -JL_NO_ASAN static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt) +JL_NO_ASAN static void restore_stack2(jl_ucontext_t *t, jl_ptls_t ptls, jl_ucontext_t *lastt) { assert(t->copy_stack && !lastt->copy_stack); size_t nb = t->copy_stack; - char *_x = (char*)ptls->stackbase - nb; - void *_y = t->stkbuf; - assert(_x != NULL && _y != NULL); - memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe + if (nb > 1) { + char *_x = (char*)ptls->stackbase - nb; + void *_y = t->stkbuf; + assert(_x != NULL && _y != NULL); + memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); + } +#if defined(_OS_WINDOWS_) + // jl_swapcontext and setjmp are the same on Windows, so we can just use swapcontext directly + tsan_switch_to_ctx(t); + jl_swapcontext(lastt->ctx, t->copy_ctx); +#else #if defined(JL_HAVE_UNW_CONTEXT) volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx.ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); -#elif defined(JL_HAVE_ASM) || defined(_OS_WINDOWS_) - if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) +#elif defined(JL_HAVE_ASM) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; #else #error COPY_STACKS is incompatible with this platform #endif - tsan_switch_to_copyctx(&t->ctx); -#if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); + tsan_switch_to_ctx(t); + jl_longjmp(t->copy_ctx->uc_mcontext, 1); +#endif +} + +JL_NO_ASAN static void NOINLINE restore_stack3(jl_ucontext_t *t, jl_ptls_t ptls, char *p) +{ +#if !defined(JL_HAVE_ASM) + char *_x = (char*)ptls->stackbase; + if (!p) { + // switch to a stackframe that's well beyond the bounds of the next switch + p = _x - 4096; + if ((char*)&_x > p) { + p = (char*)alloca((char*)&_x - p); + } + restore_stack3(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca + } +#endif +#if defined(_CPU_X86_) || defined(_CPU_X86_64_) + void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; + assert(*return_address == __builtin_return_address(0)); + *return_address = NULL; #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); +#pragma message("warning: CFI_NORETURN not implemented for this platform, so profiling of copy_stacks may segfault in this build") #endif +CFI_NORETURN + tsan_switch_to_ctx(t); + jl_start_fiber_set(t); // (doesn't return) + abort(); } + #endif /* Rooted by the base module */ @@ -298,9 +313,9 @@ void JL_NORETURN jl_finish_task(jl_task_t *ct) jl_atomic_store_release(&ct->_state, JL_TASK_STATE_FAILED); else jl_atomic_store_release(&ct->_state, JL_TASK_STATE_DONE); - if (ct->copy_stack) { // early free of stkbuf - asan_free_copy_stack(ct->stkbuf, ct->bufsz); - ct->stkbuf = NULL; + if (ct->ctx.copy_stack) { // early free of stkbuf + asan_free_copy_stack(ct->ctx.stkbuf, ct->ctx.bufsz); + ct->ctx.stkbuf = NULL; } // ensure that state is cleared ct->ptls->in_finalizer = 0; @@ -344,33 +359,33 @@ JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid if (ptls2) { *ptid = jl_atomic_load_relaxed(&task->tid); #ifdef COPY_STACKS - if (task->copy_stack) { + if (task->ctx.copy_stack) { *size = ptls2->stacksize; return (char *)ptls2->stackbase - *size; } #endif } - *size = task->bufsz - off; - return (void *)((char *)task->stkbuf + off); + *size = task->ctx.bufsz - off; + return (void *)((char *)task->ctx.stkbuf + off); } JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, char **active_start, char **active_end, char **total_start, char **total_end) { - if (!task->started) { + if (!task->ctx.started) { *total_start = *active_start = 0; *total_end = *active_end = 0; return; } jl_ptls_t ptls2 = task->ptls; - if (task->copy_stack && ptls2) { + if (task->ctx.copy_stack && ptls2) { *total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize; *total_end = *active_end = (char*)ptls2->stackbase; } - else if (task->stkbuf) { - *total_start = *active_start = (char*)task->stkbuf; + else if (task->ctx.stkbuf) { + *total_start = *active_start = (char*)task->ctx.stkbuf; #ifndef _OS_WINDOWS_ jl_ptls_t ptls0 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; if (ptls0->root_task == task) { @@ -383,12 +398,12 @@ JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, } #endif - *total_end = *active_end = (char*)task->stkbuf + task->bufsz; + *total_end = *active_end = (char*)task->ctx.stkbuf + task->ctx.bufsz; #ifdef COPY_STACKS // save_stack stores the stack of an inactive task in stkbuf, and the // actual number of used bytes in copy_stack. - if (task->copy_stack > 1) - *active_end = (char*)task->stkbuf + task->copy_stack; + if (task->ctx.copy_stack > 1) + *active_end = (char*)task->ctx.stkbuf + task->ctx.copy_stack; #endif } else { @@ -449,20 +464,16 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) #endif int killed = jl_atomic_load_relaxed(&lastt->_state) != JL_TASK_STATE_RUNNABLE; - if (!t->started && !t->copy_stack) { + if (!t->ctx.started && !t->ctx.copy_stack) { // may need to allocate the stack - if (t->stkbuf == NULL) { - t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t); - if (t->stkbuf == NULL) { + if (t->ctx.stkbuf == NULL) { + t->ctx.stkbuf = jl_malloc_stack(&t->ctx.bufsz, t); + if (t->ctx.stkbuf == NULL) { #ifdef COPY_STACKS // fall back to stack copying if mmap fails - t->copy_stack = 1; + t->ctx.copy_stack = 1; + t->ctx.bufsz = 0; t->sticky = 1; - t->bufsz = 0; - if (always_copy_stacks) - memcpy(&t->ctx.copy_ctx, &ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx)); - else - memcpy(&t->ctx.ctx, &ptls->base_ctx, sizeof(t->ctx.ctx)); #else jl_throw(jl_memory_exception); #endif @@ -470,28 +481,45 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } + union { + _jl_ucontext_t ctx; + jl_stack_context_t copy_ctx; + } lasttstate; + if (killed) { *pt = NULL; // can't fail after here: clear the gc-root for the target task now lastt->gcstack = NULL; lastt->eh = NULL; - if (!lastt->copy_stack && lastt->stkbuf) { + if (!lastt->ctx.copy_stack && lastt->ctx.stkbuf) { // early free of stkbuf back to the pool jl_release_task_stack(ptls, lastt); } } else { + if (lastt->ctx.copy_stack) { // save the old copy-stack +#ifdef _OS_WINDOWS_ + lasttstate.copy_ctx.uc_stack.ss_sp = (char*)ptls->stackbase - ptls->stacksize; + lasttstate.copy_ctx.uc_stack.ss_size = ptls->stacksize; +#endif #ifdef COPY_STACKS - if (lastt->copy_stack) { // save the old copy-stack - save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail) - if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) { - sanitizer_finish_switch_fiber(ptls->previous_task, jl_atomic_load_relaxed(&ptls->current_task)); - // TODO: mutex unlock the thread we just switched from + if (jl_setjmp(lasttstate.copy_ctx.uc_mcontext, 0)) { +#ifdef MIGRATE_TASKS + ptls = lastt->ptls; +#endif + lastt->ctx.copy_ctx = NULL; + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &lastt->ctx); return; } - } - else + save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail) + lastt->ctx.copy_ctx = &lasttstate.copy_ctx; +#else + abort(); #endif - *pt = NULL; // can't fail after here: clear the gc-root for the target task now + } + else { + *pt = NULL; // can't fail after here: clear the gc-root for the target task now + lastt->ctx.ctx = &lasttstate.ctx; + } } // set up global state for new task and clear global state for old task @@ -506,41 +534,44 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) ptls->previous_task = lastt; #endif - if (t->started) { + if (t->ctx.started) { + if (t->ctx.copy_stack) { #ifdef COPY_STACKS - if (t->copy_stack) { - if (lastt->copy_stack) { + if (lastt->ctx.copy_stack) { // Switching from copystack to copystack. Clear any shadow stack // memory above the saved shadow stack. - uintptr_t stacktop = (uintptr_t)ptls->stackbase - t->copy_stack; + uintptr_t stacktop = (uintptr_t)ptls->stackbase - t->ctx.copy_stack; uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); if (stackbottom < stacktop) - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } - if (!killed && !lastt->copy_stack) { - sanitizer_start_switch_fiber(ptls, lastt, t); - restore_stack2(t, ptls, lastt); - } else { - tsan_switch_to_copyctx(&t->ctx); + if (!killed && !lastt->ctx.copy_stack) { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + restore_stack2(&t->ctx, ptls, &lastt->ctx); // half jl_swap_fiber and half restore_stack + } + else { + tsan_switch_to_ctx(&t->ctx); if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); - tsan_destroy_copyctx(ptls, &lastt->ctx); - } else { - sanitizer_start_switch_fiber(ptls, lastt, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); + tsan_destroy_ctx(ptls, &lastt->ctx); + } + else { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); } - if (lastt->copy_stack) { - restore_stack(t, ptls, NULL); // (doesn't return) + if (lastt->ctx.copy_stack) { + restore_stack(&t->ctx, ptls, NULL); // (doesn't return) + abort(); } else { - restore_stack(t, ptls, (char*)1); // (doesn't return) + restore_stack(&t->ctx, ptls, (char*)1); // (doesn't return) + abort(); } } - } - else #endif - { - if (lastt->copy_stack) { + } + else { + if (lastt->ctx.copy_stack) { // Switching away from a copystack to a non-copystack. Clear // the whole shadow stack now, because otherwise we won't know // how much stack memory to clear the next time we switch to @@ -549,22 +580,23 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); // We're not restoring the stack, but we still need to unpoison the // stack, so it starts with a pristine stack. - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_switch_to_ctx(&t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); jl_set_fiber(&t->ctx); // (doesn't return) abort(); // unreachable } else { - sanitizer_start_switch_fiber(ptls, lastt, t); - if (lastt->copy_stack) { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { // Resume at the jl_setjmp earlier in this function, // don't do a full task swap tsan_switch_to_ctx(&t->ctx); jl_set_fiber(&t->ctx); // (doesn't return) + abort(); } else { jl_swap_fiber(&lastt->ctx, &t->ctx); @@ -573,41 +605,58 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } else { - if (lastt->copy_stack) { +#ifdef _COMPILER_TSAN_ENABLED_ + t->ctx.tsan_state = __tsan_create_fiber(0); +#endif + if (lastt->ctx.copy_stack) { uintptr_t stacktop = (uintptr_t)ptls->stackbase; uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); // We're not restoring the stack, but we still need to unpoison the // stack, so it starts with a pristine stack. - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } - if (t->copy_stack && always_copy_stacks) { + if (t->ctx.copy_stack) { +#ifdef COPY_STACKS tsan_switch_to_ctx(&t->ctx); + // create a temporary non-copy_stack context for starting this fiber + jl_ucontext_t ctx = t->ctx; + ctx.ctx = NULL; + ctx.stkbuf = (char*)ptls->stackbase - ptls->stacksize; + ctx.bufsz = ptls->stacksize; + ctx.copy_stack = 0; + ctx.started = 0; if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); - } else { - sanitizer_start_switch_fiber(ptls, lastt, t); + if (lastt->ctx.copy_stack) + restore_stack3(&ctx, ptls, NULL); // (doesn't return) + else + jl_start_fiber_set(&ctx); + abort(); + } + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { + restore_stack3(&ctx, ptls, NULL); // (doesn't return) + abort(); + } + else { + jl_start_fiber_swap(&lastt->ctx, &ctx); } -#ifdef COPY_STACKS -#if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); + abort(); #endif -#endif - abort(); // unreachable } else { if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_switch_to_ctx(&t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); jl_start_fiber_set(&t->ctx); // (doesn't return) abort(); } - sanitizer_start_switch_fiber(ptls, lastt, t); - if (lastt->copy_stack) { - // Resume at the jl_setjmp earlier in this function + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { + // copy_stack resumes at the jl_setjmp earlier in this function, so don't swap here tsan_switch_to_ctx(&t->ctx); jl_start_fiber_set(&t->ctx); // (doesn't return) abort(); @@ -617,7 +666,14 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } } - sanitizer_finish_switch_fiber(ptls->previous_task, jl_atomic_load_relaxed(&ptls->current_task)); + +#ifdef MIGRATE_TASKS + ptls = lastt->ptls; +#endif + assert(ptls); + assert(lastt == jl_atomic_load_relaxed(&ptls->current_task)); + lastt->ctx.ctx = NULL; + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &lastt->ctx); } JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER @@ -629,7 +685,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER return; } int8_t gc_state = jl_gc_unsafe_enter(ptls); - if (t->started && t->stkbuf == NULL) + if (t->ctx.started && t->ctx.stkbuf == NULL) jl_error("attempt to switch to exited task"); if (ptls->in_finalizer) jl_error("task switch not allowed from inside gc finalizer"); @@ -654,7 +710,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER ptls->previous_task = NULL; assert(t != ct); assert(jl_atomic_load_relaxed(&t->tid) == ptls->tid); - if (!t->sticky && !t->copy_stack) + if (!t->sticky && !t->ctx.copy_stack) jl_atomic_store_release(&t->tid, -1); #else assert(ptls == ct->ptls); @@ -1071,26 +1127,28 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type); jl_set_typetagof(t, jl_task_tag, 0); JL_PROBE_RT_NEW_TASK(ct, t); - t->copy_stack = 0; + t->ctx.copy_stack = 0; if (ssize == 0) { // stack size unspecified; use default if (always_copy_stacks) { - t->copy_stack = 1; - t->bufsz = 0; + t->ctx.copy_stack = 1; + t->ctx.bufsz = 0; } else { - t->bufsz = JL_STACK_SIZE; + t->ctx.bufsz = JL_STACK_SIZE; } - t->stkbuf = NULL; + t->ctx.stkbuf = NULL; } else { // user requested dedicated stack of a certain size if (ssize < MINSTKSZ) ssize = MINSTKSZ; - t->bufsz = ssize; - t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t); - if (t->stkbuf == NULL) + t->ctx.bufsz = ssize; + t->ctx.stkbuf = jl_malloc_stack(&t->ctx.bufsz, t); + if (t->ctx.stkbuf == NULL) { + t->ctx.bufsz = 0; jl_throw(jl_memory_exception); + } } t->next = jl_nothing; t->queue = jl_nothing; @@ -1109,30 +1167,21 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->sticky = 1; t->gcstack = NULL; t->excstack = NULL; - t->started = 0; + t->ctx.started = 0; t->priority = 0; - jl_atomic_store_relaxed(&t->tid, t->copy_stack ? jl_atomic_load_relaxed(&ct->tid) : -1); // copy_stacks are always pinned since they can't be moved + jl_atomic_store_relaxed(&t->tid, -1); t->threadpoolid = ct->threadpoolid; t->ptls = NULL; t->world_age = ct->world_age; t->reentrant_timing = 0; jl_timing_task_init(t); -#ifdef COPY_STACKS - if (!t->copy_stack) { -#if defined(JL_DEBUG_BUILD) - memset(&t->ctx, 0, sizeof(t->ctx)); -#endif - } - else { - if (always_copy_stacks) - memcpy(&t->ctx.copy_ctx, &ct->ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx)); - else - memcpy(&t->ctx.ctx, &ct->ptls->base_ctx, sizeof(t->ctx.ctx)); - } -#endif + if (t->ctx.copy_stack) + t->ctx.copy_ctx = NULL; + else + t->ctx.ctx = NULL; #ifdef _COMPILER_TSAN_ENABLED_ - t->ctx.tsan_state = __tsan_create_fiber(0); + t->ctx.tsan_state = NULL; #endif #ifdef _COMPILER_ASAN_ENABLED_ t->ctx.asan_fake_stack = NULL; @@ -1196,7 +1245,7 @@ CFI_NORETURN jl_task_t *ct = jl_current_task; #endif jl_ptls_t ptls = ct->ptls; - sanitizer_finish_switch_fiber(ptls->previous_task, ct); + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &ct->ctx); _start_task(); } @@ -1210,6 +1259,7 @@ CFI_NORETURN #else jl_task_t *ct = jl_current_task; #endif + ct->ctx.ctx = NULL; jl_ptls_t ptls = ct->ptls; jl_value_t *res; assert(ptls->finalizers_inhibited == 0); @@ -1217,11 +1267,11 @@ CFI_NORETURN #ifdef MIGRATE_TASKS jl_task_t *pt = ptls->previous_task; ptls->previous_task = NULL; - if (!pt->sticky && !pt->copy_stack) + if (!pt->sticky && !pt->ctx.copy_stack) jl_atomic_store_release(&pt->tid, -1); #endif - ct->started = 1; + ct->ctx.started = 1; JL_PROBE_RT_START_TASK(ct); jl_timing_block_task_enter(ct, ptls, NULL); if (jl_atomic_load_relaxed(&ct->_isexception)) { @@ -1258,64 +1308,52 @@ skip_pop_exception:; #ifdef _OS_WINDOWS_ #define setcontext jl_setcontext #define swapcontext jl_swapcontext -#define makecontext jl_makecontext #endif -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT +static int make_fiber(jl_ucontext_t *t, _jl_ucontext_t *ctx) { #ifndef _OS_WINDOWS_ - int r = getcontext(t); - if (r != 0) - jl_error("getcontext failed"); + int r = getcontext(ctx); + if (r != 0) abort(); #endif - void *stk = jl_malloc_stack(ssize, owner); - if (stk == NULL) - return NULL; - t->uc_stack.ss_sp = stk; - t->uc_stack.ss_size = *ssize; + ctx->uc_stack.ss_sp = (char*)t->stkbuf; + ctx->uc_stack.ss_size = t->bufsz; #ifdef _OS_WINDOWS_ - makecontext(t, &start_task); + jl_makecontext(ctx, &start_task); #else - t->uc_link = NULL; - makecontext(t, &start_task, 0); + ctx->uc_link = NULL; + makecontext(ctx, &start_task, 0); #endif - return (char*)stk; + return 1; } static void jl_start_fiber_set(jl_ucontext_t *t) { - setcontext(&t->ctx); + _jl_ucontext_t ctx; + make_fiber(t, &ctx); + setcontext(&ctx); } static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { + _jl_ucontext_t ctx; + make_fiber(t, &ctx); assert(lastt); tsan_switch_to_ctx(t); - swapcontext(&lastt->ctx, &t->ctx); + swapcontext(lastt->ctx, &ctx); } static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { tsan_switch_to_ctx(t); - swapcontext(&lastt->ctx, &t->ctx); + swapcontext(lastt->ctx, t->ctx); } static void jl_set_fiber(jl_ucontext_t *t) { - setcontext(&t->ctx); -} -#endif - -#if defined(JL_HAVE_UNW_CONTEXT) || defined(JL_HAVE_ASM) -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) -{ - char *stkbuf = (char*)jl_malloc_stack(ssize, owner); - if (stkbuf == NULL) - return NULL; -#ifndef __clang_gcanalyzer__ - ((char**)t)[0] = stkbuf; // stash the stack pointer somewhere for start_fiber - ((size_t*)t)[1] = *ssize; // stash the stack size somewhere for start_fiber -#endif - return stkbuf; + setcontext(t->ctx); } #endif #if defined(JL_HAVE_UNW_CONTEXT) +#ifdef _OS_WINDOWS_ +#error unw_context_t not defined in Windows +#endif static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c) { volatile int returns = 0; @@ -1329,15 +1367,15 @@ static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c) static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { unw_cursor_t c; - int r = unw_init_local(&c, &t->ctx); + int r = unw_init_local(&c, t->ctx); if (r < 0) abort(); - jl_unw_swapcontext(&lastt->ctx, &c); + jl_unw_swapcontext(lastt->ctx, &c); } static void jl_set_fiber(jl_ucontext_t *t) { unw_cursor_t c; - int r = unw_init_local(&c, &t->ctx); + int r = unw_init_local(&c, t->ctx); if (r < 0) abort(); unw_resume(&c); @@ -1345,14 +1383,14 @@ static void jl_set_fiber(jl_ucontext_t *t) #elif defined(JL_HAVE_ASM) static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { - if (jl_setjmp(lastt->ctx.uc_mcontext, 0)) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; tsan_switch_to_ctx(t); jl_set_fiber(t); // doesn't return } static void jl_set_fiber(jl_ucontext_t *t) { - jl_longjmp(t->ctx.uc_mcontext, 1); + jl_longjmp(t->ctx->uc_mcontext, 1); } #endif @@ -1373,14 +1411,14 @@ static void jl_set_fiber(jl_ucontext_t *t) static void jl_start_fiber_set(jl_ucontext_t *t) { unw_cursor_t c; - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; - int r = unw_getcontext(&t->ctx); + int r = unw_getcontext(t->ctx); if (r) abort(); - if (unw_init_local(&c, &t->ctx)) + if (unw_init_local(&c, t->ctx)) abort(); PUSH_RET(&c, stk); #if defined __linux__ @@ -1396,43 +1434,46 @@ static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { assert(lastt); unw_cursor_t c; - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); - r = unw_getcontext(&t->ctx); + r = unw_getcontext(t->ctx); if (r != 0) abort(); - if (unw_init_local(&c, &t->ctx)) + if (unw_init_local(&c, t->ctx)) abort(); PUSH_RET(&c, stk); if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk)) abort(); if (unw_set_reg(&c, UNW_REG_IP, fn)) abort(); - jl_unw_swapcontext(&lastt->ctx, &c); + jl_unw_swapcontext(lastt->ctx, &c); } #endif #if defined(JL_HAVE_ASM) +#ifdef _OS_WINDOWS_ +#error JL_HAVE_ASM not defined in Windows +#endif JL_NO_ASAN static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { assert(lastt); #ifdef JL_HAVE_UNW_CONTEXT volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); #else - if (jl_setjmp(lastt->ctx.uc_mcontext, 0)) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; #endif tsan_switch_to_ctx(t); @@ -1440,8 +1481,9 @@ JL_NO_ASAN static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t * } JL_NO_ASAN static void jl_start_fiber_set(jl_ucontext_t *t) { - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; +CFI_NORETURN + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; #ifdef _CPU_X86_64_ @@ -1539,14 +1581,14 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) } #endif if (always_copy_stacks) { - ct->copy_stack = 1; - ct->stkbuf = NULL; - ct->bufsz = 0; + ct->ctx.copy_stack = 1; + ct->ctx.stkbuf = NULL; + ct->ctx.bufsz = 0; } else { - ct->copy_stack = 0; - ct->stkbuf = stack; - ct->bufsz = ssize; + ct->ctx.copy_stack = 0; + ct->ctx.stkbuf = stack; + ct->ctx.bufsz = ssize; } #ifdef USE_TRACY @@ -1554,7 +1596,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) strcpy(unique_string, "Root"); ct->name = unique_string; #endif - ct->started = 1; + ct->ctx.started = 1; ct->next = jl_nothing; ct->queue = jl_nothing; ct->tls = jl_nothing; @@ -1594,21 +1636,18 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) if (always_copy_stacks) { // when this is set, we will attempt to corrupt the process stack to switch tasks, // although this is unreliable, and thus not recommended - ptls->stackbase = stack_hi; - ptls->stacksize = ssize; -#ifdef _OS_WINDOWS_ - ptls->copy_stack_ctx.uc_stack.ss_sp = stack_hi; - ptls->copy_stack_ctx.uc_stack.ss_size = ssize; -#endif - if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0)) - start_task(); // sanitizer_finish_switch_fiber is part of start_task + ptls->stackbase = jl_get_frame_addr(); + ptls->stacksize = (char*)ptls->stackbase - (char*)stack_lo; } else { - ssize = JL_STACK_SIZE; - char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL); + size_t bufsz = JL_STACK_SIZE; + void *stkbuf = jl_malloc_stack(&bufsz, NULL); if (stkbuf != NULL) { - ptls->stackbase = stkbuf + ssize; - ptls->stacksize = ssize; + ptls->stackbase = (char*)stkbuf + bufsz; + ptls->stacksize = bufsz; + } + else { + ptls->stacksize = 0; } } #endif @@ -1621,7 +1660,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT { - return t->started; + return t->ctx.started; } JL_DLLEXPORT int16_t jl_get_task_tid(jl_task_t *t) JL_NOTSAFEPOINT From 7d341eac799ffaa7dfbb62eaf670ab24de0e1a81 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Tue, 20 Aug 2024 17:20:16 -0500 Subject: [PATCH 096/548] Update display style of `n` in `invmod(n)` dosctring. (#55539) From https://github.com/JuliaLang/julia/pull/55531#discussion_r1722585738 --- base/intfuncs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index e44ea8eed2463..8d46fcffa3ad5 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -271,7 +271,7 @@ n * invmod(n) == 1 (n % T) * invmod(n, T) == 1 ``` Note that `*` here is modular multiplication in the integer ring, `T`. This will -throw an error if ``n`` is even, because then it is not relatively prime with `2^N` +throw an error if `n` is even, because then it is not relatively prime with `2^N` and thus has no such inverse. Specifying the modulus implied by an integer type as an explicit value is often From 86cba99f6f79bfc6b67e52f0575de211109b638c Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 21 Aug 2024 07:16:39 +0530 Subject: [PATCH 097/548] Remove specialized vector-matrix multiplication methods (#55538) The specialized methods for `TransposeAbsMat` and `AdjointAbsMat` seem unnecessary, as these are also `AbstractMatrix`es, and are treated identically. I've also added a `require_one_based_indexing` check on the vector to avoid accepting `OffsetArray`s. --- stdlib/LinearAlgebra/src/matmul.jl | 7 ++++--- stdlib/LinearAlgebra/test/matmul.jl | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 412d375d3e842..9a232d3ad1e51 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -61,9 +61,10 @@ function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S} end # these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case): -(*)(a::AbstractVector, tB::TransposeAbsMat) = reshape(a, length(a), 1) * tB -(*)(a::AbstractVector, adjB::AdjointAbsMat) = reshape(a, length(a), 1) * adjB -(*)(a::AbstractVector, B::AbstractMatrix) = reshape(a, length(a), 1) * B +function (*)(a::AbstractVector, B::AbstractMatrix) + require_one_based_indexing(a) + reshape(a, length(a), 1) * B +end # Add a level of indirection and specialize _mul! to avoid ambiguities in mul! @inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 56834a39a3ceb..4c79451ebfc8b 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1120,4 +1120,14 @@ end end end +@testset "vector-matrix multiplication" begin + a = [1,2] + A = reshape([1,2], 2, 1) + B = [1 2] + @test a * B ≈ A * B + B = reshape([1,2], 2, 1) + @test a * B' ≈ A * B' + @test a * transpose(B) ≈ A * transpose(B) +end + end # module TestMatmul From 54142b7a8255d2231af8182eae52417eb6c72327 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:13:39 +0900 Subject: [PATCH 098/548] effects: minor fixes for the effects system correctness (#55536) This commit implements several fixes related to the correctness of the effect system. The most significant change addresses an issue where post-opt analysis was not correctly propagating the taints of `:noub` and `:nortcall` of `:foreigncall` expressions, which could lead to incorrect effect bits. Additionally, adjustments have been made to the values of effects used in various worst-case scenarios. --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/effects.jl | 10 +++++----- base/compiler/optimize.jl | 10 ++++++++-- base/compiler/ssair/ir.jl | 3 ++- base/compiler/ssair/irinterp.jl | 3 ++- base/compiler/tfuncs.jl | 7 ++++++- base/compiler/validation.jl | 4 +++- base/strings/string.jl | 6 ++++-- test/compiler/effects.jl | 5 +++++ test/strings/basic.jl | 2 ++ 10 files changed, 38 insertions(+), 14 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 83abfc952bf8e..c947832e97d16 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2829,7 +2829,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif ehead === :globaldecl return RTEffects(Nothing, Any, EFFECTS_UNKNOWN) elseif ehead === :thunk - return RTEffects(Any, Any, EFFECTS_UNKNOWN) + return RTEffects(Any, Any, Effects()) end # N.B.: abstract_eval_value_expr can modify the global effects, but # we move out any arguments with effects during SSA construction later diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 166df78f3130c..7778c96e019e5 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -169,12 +169,12 @@ const NOUB_IF_NOINBOUNDS = 0x01 << 1 # :nonoverlayed bits const CONSISTENT_OVERLAY = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # 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, false) # unknown really +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -function Effects(effects::Effects = _EFFECTS_UNKNOWN; +function Effects(effects::Effects=Effects( + ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false); consistent::UInt8 = effects.consistent, effect_free::UInt8 = effects.effect_free, nothrow::Bool = effects.nothrow, diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 936b604d373a0..fb712b1c71b12 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -59,6 +59,12 @@ const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM has_flag(curr::UInt32, flag::UInt32) = (curr & flag) == flag +function iscallstmt(@nospecialize stmt) + stmt isa Expr || return false + head = stmt.head + return head === :call || head === :invoke || head === :foreigncall +end + function flags_for_effects(effects::Effects) flags = zero(UInt32) if is_consistent(effects) @@ -380,7 +386,7 @@ function recompute_effects_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), elseif nothrow flag |= IR_FLAG_NOTHROW end - if !(isexpr(stmt, :call) || isexpr(stmt, :invoke)) + if !iscallstmt(stmt) # There is a bit of a subtle point here, which is that some non-call # statements (e.g. PiNode) can be UB:, however, we consider it # illegal to introduce such statements that actually cause UB (for any @@ -784,7 +790,7 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) if !has_flag(flag, IR_FLAG_NORTCALL) # if a function call that might invoke `Core.Compiler.return_type` has been deleted, # there's no need to taint with `:nortcall`, allowing concrete evaluation - if isexpr(stmt, :call) || isexpr(stmt, :invoke) + if iscallstmt(stmt) sv.nortcall = false end end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index c665c5bef299e..960da88ddffc8 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -2,7 +2,8 @@ Core.PhiNode() = Core.PhiNode(Int32[], Any[]) -isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave) +isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || + isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave) struct CFG blocks::Vector{BasicBlock} diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 3d49be33f39d5..1aeb87accbcd7 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -141,7 +141,8 @@ function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction, rt = nothing if isa(stmt, Expr) head = stmt.head - if head === :call || head === :foreigncall || head === :new || head === :splatnew || head === :static_parameter || head === :isdefined || head === :boundscheck + if (head === :call || head === :foreigncall || head === :new || head === :splatnew || + head === :static_parameter || head === :isdefined || head === :boundscheck) (; rt, effects) = abstract_eval_statement_expr(interp, stmt, nothing, irsv) add_flag!(inst, flags_for_effects(effects)) elseif head === :invoke diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 89874b9a6df10..0c57c04a6ddea 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2966,7 +2966,7 @@ end function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState, max_methods::Int) length(argtypes) < 2 && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) - isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_UNKNOWN, NoCallInfo()) + isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo()) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) matches = find_method_matches(interp, argtypes, atype; max_methods) @@ -3191,6 +3191,11 @@ function foreigncall_effects(@specialize(abstract_eval), e::Expr) elseif name === :jl_genericmemory_copy_slice return Effects(EFFECTS_TOTAL; consistent=CONSISTENT_IF_NOTRETURNED, nothrow=false) end + # `:foreigncall` can potentially perform all sorts of operations, including calling + # overlay methods, but the `:foreigncall` itself is not dispatched, and there is no + # concern that the method calls that potentially occur within the `:foreigncall` will + # be executed using the wrong method table due to concrete evaluation, so using + # `EFFECTS_UNKNOWN` here and not tainting with `:nonoverlayed` is fine return EFFECTS_UNKNOWN end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index a9f2f1eebe1b5..78db5ef5e4ed8 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -257,7 +257,9 @@ end function is_valid_rvalue(@nospecialize(x)) is_valid_argument(x) && return true - if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, :invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast, :new_opaque_closure) + if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, + :invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast, + :new_opaque_closure) return true end return false diff --git a/base/strings/string.jl b/base/strings/string.jl index 89e2ff288c3d7..f5abbead34bd1 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -102,9 +102,11 @@ function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}) ccall(:jl_cstr_to_string, Ref{String}, (Ptr{UInt8},), p) end -# This is @assume_effects :effect_free :nothrow :terminates_globally @ccall jl_alloc_string(n::Csize_t)::Ref{String}, +# This is `@assume_effects :total !:consistent @ccall jl_alloc_string(n::Csize_t)::Ref{String}`, # but the macro is not available at this time in bootstrap, so we write it manually. -@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall,0x000e)), :(convert(Csize_t, n)))) +const _string_n_override = 0x04ee +@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, + :(Core.svec(Csize_t)), 1, QuoteNode((:ccall, _string_n_override)), :(convert(Csize_t, n)))) """ String(s::AbstractString) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index a27d52d68b9a9..11c30aad0b9a4 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1361,3 +1361,8 @@ end |> Core.Compiler.is_nothrow @test Base.infer_effects((Vector{Any},)) do xs Core.svec(xs...) end |> Core.Compiler.is_nothrow + +# effects for unknown `:foreigncall`s +@test Base.infer_effects() do + @ccall unsafecall()::Cvoid +end == Core.Compiler.EFFECTS_UNKNOWN diff --git a/test/strings/basic.jl b/test/strings/basic.jl index d8ca4d204b6f4..511b498e5cd89 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1235,6 +1235,8 @@ end @test !Core.Compiler.is_removable_if_unused(e) || (f, Ts) end @test_throws ArgumentError Symbol("a\0a") + + @test Base._string_n_override == Core.Compiler.encode_effects_override(Base.compute_assumed_settings((:total, :(!:consistent)))) end @testset "Ensure UTF-8 DFA can never leave invalid state" begin From 58c7186d1983de304a47fdefb9a9a16f8b4901e7 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:53:59 +0900 Subject: [PATCH 099/548] inference: propagate partially initialized mutable structs more (#55533) --- base/compiler/abstractinterpretation.jl | 16 +++++++++++++--- base/compiler/typeutils.jl | 2 +- test/compiler/inference.jl | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index c947832e97d16..26bba7b51a2dd 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1027,7 +1027,7 @@ collect_const_args(arginfo::ArgInfo, start::Int) = collect_const_args(arginfo.ar function collect_const_args(argtypes::Vector{Any}, start::Int) return Any[ let a = widenslotwrapper(argtypes[i]) isa(a, Const) ? a.val : - isconstType(a) ? (a::DataType).parameters[1] : + isconstType(a) ? a.parameters[1] : (a::DataType).instance end for i = start:length(argtypes) ] end @@ -2063,11 +2063,21 @@ function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name)) fldidx === nothing && return nothing nminfld = datatype_min_ninitialized(objt) if ismutabletype(objt) - fldidx == nminfld+1 || return nothing + # A mutable struct can have non-contiguous undefined fields, but `PartialStruct` cannot + # model such a state. So here `PartialStruct` can be used to represent only the + # objects where the field following the minimum initialized fields is also defined. + if fldidx ≠ nminfld+1 + # if it is already represented as a `PartialStruct`, we can add one more + # `isdefined`-field information on top of those implied by its `fields` + if !(obj isa PartialStruct && fldidx == length(obj.fields)+1) + return nothing + end + end else fldidx > nminfld || return nothing end - return PartialStruct(objt0, Any[fieldtype(objt0, i) for i = 1:fldidx]) + return PartialStruct(objt0, Any[obj isa PartialStruct && i≤length(obj.fields) ? + obj.fields[i] : fieldtype(objt0,i) for i = 1:fldidx]) end function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta) diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index a4499e003cf2c..577452a873b5e 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -18,7 +18,7 @@ function hasuniquerep(@nospecialize t) iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system isconcretetype(t) && return true # these are also interned and pointer comparable if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes - return _all(hasuniquerep, t.parameters) + return all(hasuniquerep, t.parameters) end return false end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 75f33a280e245..1595594fb2068 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6033,6 +6033,22 @@ end |> Core.Compiler.is_nothrow return x.b end end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((PartiallyInitialized2,); optimize=false) do x + if isdefined(x, :b) + if isdefined(x, :c) + return x.c + end + return x.b + end + return nothing +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Bool,Int,); optimize=false) do c, b + x = c ? PartiallyInitialized1(true) : PartiallyInitialized1(true, b) + if isdefined(x, :b) + return Val(x.a), x.b + end + return nothing +end |> Core.Compiler.is_nothrow # End to end test case for the partially initialized struct with `PartialStruct` @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) From 6d7f4a209c0dc933618e4f948e7c7f8b3f036c83 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 22 Aug 2024 23:08:37 +0530 Subject: [PATCH 100/548] Use `===` to compare with `nothing` in tests (#55563) This follows the generally recommended style, and updates instances of `a == nothing` to `a === nothing` in tests, and similarly for the `!=` comparison. --- stdlib/OpenBLAS_jll/test/runtests.jl | 2 +- test/arrayops.jl | 18 +-- test/bitarray.jl | 34 ++--- test/channels.jl | 2 +- test/char.jl | 2 +- test/compiler/codegen.jl | 2 +- test/compiler/inference.jl | 4 +- test/compiler/ssair.jl | 2 +- test/copy.jl | 2 +- test/core.jl | 2 +- test/corelogging.jl | 6 +- test/dict.jl | 6 +- test/generic_map_tests.jl | 2 +- test/intrinsics.jl | 4 +- test/iobuffer.jl | 2 +- test/iterators.jl | 4 +- test/loading.jl | 16 +-- test/ranges.jl | 14 +- test/regex.jl | 2 +- test/some.jl | 4 +- test/spawn.jl | 2 +- test/strings/search.jl | 202 +++++++++++++-------------- test/strings/types.jl | 4 +- test/testdefs.jl | 2 +- 24 files changed, 170 insertions(+), 170 deletions(-) diff --git a/stdlib/OpenBLAS_jll/test/runtests.jl b/stdlib/OpenBLAS_jll/test/runtests.jl index 1d944bab8cd67..76242b2e4080e 100644 --- a/stdlib/OpenBLAS_jll/test/runtests.jl +++ b/stdlib/OpenBLAS_jll/test/runtests.jl @@ -13,5 +13,5 @@ else end @testset "OpenBLAS_jll" begin - @test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) != nothing + @test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) !== nothing end diff --git a/test/arrayops.jl b/test/arrayops.jl index f58fdb36942a2..333b68e287c4c 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -591,32 +591,32 @@ end @test findall(!, m) == [k for (k,v) in pairs(m) if !v] @test findfirst(!iszero, a) == 2 @test findfirst(a.==0) == 1 - @test findfirst(a.==5) == nothing + @test findfirst(a.==5) === nothing @test findfirst(Dict(1=>false, 2=>true)) == 2 - @test findfirst(Dict(1=>false)) == nothing + @test findfirst(Dict(1=>false)) === nothing @test findfirst(isequal(3), [1,2,4,1,2,3,4]) == 6 @test findfirst(!isequal(1), [1,2,4,1,2,3,4]) == 2 @test findfirst(isodd, [2,4,6,3,9,2,0]) == 4 - @test findfirst(isodd, [2,4,6,2,0]) == nothing + @test findfirst(isodd, [2,4,6,2,0]) === nothing @test findnext(!iszero,a,4) == 4 @test findnext(!iszero,a,5) == 6 @test findnext(!iszero,a,1) == 2 @test findnext(isequal(1),a,4) == 6 - @test findnext(isequal(5),a,4) == nothing + @test findnext(isequal(5),a,4) === nothing @test findlast(!iszero, a) == 8 @test findlast(a.==0) == 5 - @test findlast(a.==5) == nothing - @test findlast(false) == nothing # test non-AbstractArray findlast + @test findlast(a.==5) === nothing + @test findlast(false) === nothing # test non-AbstractArray findlast @test findlast(isequal(3), [1,2,4,1,2,3,4]) == 6 @test findlast(isodd, [2,4,6,3,9,2,0]) == 5 - @test findlast(isodd, [2,4,6,2,0]) == nothing + @test findlast(isodd, [2,4,6,2,0]) === nothing @test findprev(!iszero,a,4) == 4 @test findprev(!iszero,a,5) == 4 - @test findprev(!iszero,a,1) == nothing + @test findprev(!iszero,a,1) === nothing @test findprev(isequal(1),a,4) == 2 @test findprev(isequal(1),a,8) == 6 @test findprev(isodd, [2,4,5,3,9,2,0], 7) == 5 - @test findprev(isodd, [2,4,5,3,9,2,0], 2) == nothing + @test findprev(isodd, [2,4,5,3,9,2,0], 2) === nothing @test findfirst(isequal(0x00), [0x01, 0x00]) == 2 @test findlast(isequal(0x00), [0x01, 0x00]) == 2 @test findnext(isequal(0x00), [0x00, 0x01, 0x00], 2) == 3 diff --git a/test/bitarray.jl b/test/bitarray.jl index 2cf285370441e..67d8fae0eda6d 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1357,11 +1357,11 @@ timesofar("find") @test findprev(b1, 777) == findprevnot(b2, 777) == findprev(!, b2, 777) == 777 @test findprev(b1, 776) == findprevnot(b2, 776) == findprev(!, b2, 776) == 77 @test findprev(b1, 77) == findprevnot(b2, 77) == findprev(!, b2, 77) == 77 - @test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) == nothing - @test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) == nothing - @test findprev(identity, b1, -1) == nothing - @test findprev(Returns(false), b1, -1) == nothing - @test findprev(Returns(true), b1, -1) == nothing + @test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) === nothing + @test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) === nothing + @test findprev(identity, b1, -1) === nothing + @test findprev(Returns(false), b1, -1) === nothing + @test findprev(Returns(true), b1, -1) === nothing @test_throws BoundsError findnext(b1, -1) @test_throws BoundsError findnextnot(b2, -1) @test_throws BoundsError findnext(!, b2, -1) @@ -1372,28 +1372,28 @@ timesofar("find") @test findnext(b1, 77) == findnextnot(b2, 77) == findnext(!, b2, 77) == 77 @test findnext(b1, 78) == findnextnot(b2, 78) == findnext(!, b2, 78) == 777 @test findnext(b1, 777) == findnextnot(b2, 777) == findnext(!, b2, 777) == 777 - @test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) == nothing - @test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) == nothing - @test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) == nothing + @test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) === nothing + @test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) === nothing + @test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) === nothing @test findlast(b1) == Base.findlastnot(b2) == 777 @test findfirst(b1) == Base.findfirstnot(b2) == 77 b0 = BitVector() - @test findprev(Returns(true), b0, -1) == nothing + @test findprev(Returns(true), b0, -1) === nothing @test_throws BoundsError findprev(Returns(true), b0, 1) @test_throws BoundsError findnext(Returns(true), b0, -1) - @test findnext(Returns(true), b0, 1) == nothing + @test findnext(Returns(true), b0, 1) === nothing b1 = falses(10) @test findprev(Returns(true), b1, 5) == 5 @test findnext(Returns(true), b1, 5) == 5 - @test findprev(Returns(true), b1, -1) == nothing - @test findnext(Returns(true), b1, 11) == nothing - @test findprev(Returns(false), b1, 5) == nothing - @test findnext(Returns(false), b1, 5) == nothing - @test findprev(Returns(false), b1, -1) == nothing - @test findnext(Returns(false), b1, 11) == nothing + @test findprev(Returns(true), b1, -1) === nothing + @test findnext(Returns(true), b1, 11) === nothing + @test findprev(Returns(false), b1, 5) === nothing + @test findnext(Returns(false), b1, 5) === nothing + @test findprev(Returns(false), b1, -1) === nothing + @test findnext(Returns(false), b1, 11) === nothing @test_throws BoundsError findprev(Returns(true), b1, 11) @test_throws BoundsError findnext(Returns(true), b1, -1) @@ -1415,7 +1415,7 @@ timesofar("find") for l = [1, 63, 64, 65, 127, 128, 129] f = falses(l) t = trues(l) - @test findprev(f, l) == findprevnot(t, l) == nothing + @test findprev(f, l) == findprevnot(t, l) === nothing @test findprev(t, l) == findprevnot(f, l) == l b1 = falses(l) b1[end] = true diff --git a/test/channels.jl b/test/channels.jl index d3415a96afd12..d62c0b581775c 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -500,7 +500,7 @@ end c = Channel(1) close(c) @test !isopen(c) - c.excp == nothing # to trigger the branch + c.excp === nothing # to trigger the branch @test_throws InvalidStateException Base.check_channel_state(c) end diff --git a/test/char.jl b/test/char.jl index 5da92121b1630..3100add0e81c5 100644 --- a/test/char.jl +++ b/test/char.jl @@ -121,7 +121,7 @@ end #iterate(c::Char) for x in testarrays @test iterate(x)[1] == x - @test iterate(x, iterate(x)[2]) == nothing + @test iterate(x, iterate(x)[2]) === nothing end #isless(x::Char, y::Integer) = isless(UInt32(x), y) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index a8128be270f12..76e5bd9e8780f 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -440,7 +440,7 @@ function f1_30093(r) end end -@test f1_30093(Ref(0)) == nothing +@test f1_30093(Ref(0)) === nothing # issue 33590 function f33590(b, x) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 1595594fb2068..6e2fa77eb15c8 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1065,7 +1065,7 @@ gl_17003 = [1, 2, 3] f2_17003(item::AVector_17003) = nothing f2_17003(::Any) = f2_17003(NArray_17003(gl_17003)) -@test f2_17003(1) == nothing +@test f2_17003(1) === nothing # issue #20847 function segfaultfunction_20847(A::Vector{NTuple{N, T}}) where {N, T} @@ -1076,7 +1076,7 @@ end tuplevec_20847 = Tuple{Float64, Float64}[(0.0,0.0), (1.0,0.0)] for A in (1,) - @test segfaultfunction_20847(tuplevec_20847) == nothing + @test segfaultfunction_20847(tuplevec_20847) === nothing end # Issue #20902, check that this doesn't error. diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 06258f52cb69c..b7d75d0be5567 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -219,7 +219,7 @@ let code = Any[ ] ir = make_ircode(code; verify=false) ir = Core.Compiler.compact!(ir, true) - @test Core.Compiler.verify_ir(ir) == nothing + @test Core.Compiler.verify_ir(ir) === nothing end # issue #37919 diff --git a/test/copy.jl b/test/copy.jl index b6ad53600027a..d2f555604c4d8 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -198,7 +198,7 @@ end bar = Bar19921(foo, Dict(foo => 3)) bar2 = deepcopy(bar) @test bar2.foo ∈ keys(bar2.fooDict) - @test bar2.fooDict[bar2.foo] != nothing + @test bar2.fooDict[bar2.foo] !== nothing end let d = IdDict(rand(2) => rand(2) for i = 1:100) diff --git a/test/core.jl b/test/core.jl index 692d91b6e05b3..648dd68602fa5 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7031,7 +7031,7 @@ translate27368(::Type{Val{name}}) where {name} = # issue #27456 @inline foo27456() = try baz_nonexistent27456(); catch; nothing; end bar27456() = foo27456() -@test bar27456() == nothing +@test bar27456() === nothing # issue #27365 mutable struct foo27365 diff --git a/test/corelogging.jl b/test/corelogging.jl index 778e70aecd406..b8cd3716cad2e 100644 --- a/test/corelogging.jl +++ b/test/corelogging.jl @@ -140,9 +140,9 @@ end end @test length(logger.logs) == 1 record = logger.logs[1] - @test record._module == nothing - @test record.file == nothing - @test record.line == nothing + @test record._module === nothing + @test record.file === nothing + @test record.line === nothing end # PR #28209 diff --git a/test/dict.jl b/test/dict.jl index e327c86521c88..13c60d5a6a053 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -8,7 +8,7 @@ using Random @test isequal(p,10=>20) @test iterate(p)[1] == 10 @test iterate(p, iterate(p)[2])[1] == 20 - @test iterate(p, iterate(p, iterate(p)[2])[2]) == nothing + @test iterate(p, iterate(p, iterate(p)[2])[2]) === nothing @test firstindex(p) == 1 @test lastindex(p) == length(p) == 2 @test Base.indexed_iterate(p, 1, nothing) == (10,2) @@ -683,9 +683,9 @@ end @inferred setindex!(d, -1, 10) @test d[10] == -1 @test 1 == @inferred d[1] - @test get(d, -111, nothing) == nothing + @test get(d, -111, nothing) === nothing @test 1 == @inferred get(d, 1, 1) - @test pop!(d, -111, nothing) == nothing + @test pop!(d, -111, nothing) === nothing @test 1 == @inferred pop!(d, 1) # get! and delete! diff --git a/test/generic_map_tests.jl b/test/generic_map_tests.jl index b155370dd6465..7f19d60fe31fb 100644 --- a/test/generic_map_tests.jl +++ b/test/generic_map_tests.jl @@ -43,7 +43,7 @@ function generic_map_tests(mapf, inplace_mapf=nothing) @test mapf(f, Int[], Int[], Complex{Int}[]) == Union{}[] # In-place map - if inplace_mapf != nothing + if inplace_mapf !== nothing A = Float64[1:10...] inplace_mapf(x -> x*x, A, A) @test A == map(x -> x*x, Float64[1:10...]) diff --git a/test/intrinsics.jl b/test/intrinsics.jl index e61354fe4f7f3..7a63cd1c0a62e 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -197,8 +197,8 @@ for order in (:not_atomic, :monotonic, :acquire, :release, :acquire_release, :se @test (order -> Core.Intrinsics.atomic_fence(order))(order) === nothing @test Base.invokelatest(@eval () -> Core.Intrinsics.atomic_fence($(QuoteNode(order)))) === nothing end -@test Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent) == nothing -@test (@force_compile; Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent)) == nothing +@test Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent) === nothing +@test (@force_compile; Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent)) === nothing primitive type Int256 <: Signed 256 end Int256(i::Int) = Core.Intrinsics.sext_int(Int256, i) diff --git a/test/iobuffer.jl b/test/iobuffer.jl index 0e74595d29d20..b5b34a2dbed8c 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -351,7 +351,7 @@ end a = Base.GenericIOBuffer(UInt8[], true, true, false, true, typemax(Int)) mark(a) # mark at position 0 write(a, "Hello!") - @test Base.compact(a) == nothing # because pointer > mark + @test Base.compact(a) === nothing # because pointer > mark close(a) b = Base.GenericIOBuffer(UInt8[], true, true, false, true, typemax(Int)) write(b, "Hello!") diff --git a/test/iterators.jl b/test/iterators.jl index 95275195cd7c0..0df4d9afd371a 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -499,7 +499,7 @@ end @test Base.IteratorSize(product(1:2, countfrom(1))) == Base.IsInfinite() @test Base.iterate(product()) == ((), true) -@test Base.iterate(product(), 1) == nothing +@test Base.iterate(product(), 1) === nothing # intersection @test intersect(product(1:3, 4:6), product(2:4, 3:5)) == Iterators.ProductIterator((2:3, 4:5)) @@ -993,7 +993,7 @@ end end @testset "Iterators.peel" begin - @test Iterators.peel([]) == nothing + @test Iterators.peel([]) === nothing @test Iterators.peel(1:10)[1] == 1 @test Iterators.peel(1:10)[2] |> collect == 2:10 @test Iterators.peel(x^2 for x in 2:4)[1] == 4 diff --git a/test/loading.jl b/test/loading.jl index 8310cb03c410b..51e0c45d2faf1 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -167,7 +167,7 @@ end @test root.uuid == root_uuid @test this.uuid == this_uuid - @test that == nothing + @test that === nothing write(project_file, """ name = "Root" @@ -180,8 +180,8 @@ end that = Base.identify_package("That") @test root.uuid == proj_uuid - @test this == nothing - @test that == nothing + @test this === nothing + @test that === nothing finally copy!(LOAD_PATH, old_load_path) end @@ -213,8 +213,8 @@ end that = Base.identify_package("That") @test root.uuid == root_uuid - @test this == nothing - @test that == nothing + @test this === nothing + @test that === nothing @test Base.get_uuid_name(project_file, this_uuid) == "This" finally @@ -273,8 +273,8 @@ end @test joinpath(@__DIR__, normpath(path)) == locate_package(pkg) @test Base.compilecache_path(pkg, UInt64(0)) == Base.compilecache_path(pkg, UInt64(0)) end - @test identify_package("Baz") == nothing - @test identify_package("Qux") == nothing + @test identify_package("Baz") === nothing + @test identify_package("Qux") === nothing @testset "equivalent package names" begin classes = [ ["Foo"], @@ -848,7 +848,7 @@ end proj = joinpath(tmp, "Project.toml") touch(proj) touch(joinpath(tmp, "Manifest-v1.5.toml")) - @test Base.project_file_manifest_path(proj) == nothing + @test Base.project_file_manifest_path(proj) === nothing touch(joinpath(tmp, "Manifest.toml")) man = basename(Base.project_file_manifest_path(proj)) @test man == "Manifest.toml" diff --git a/test/ranges.jl b/test/ranges.jl index d789871c6d049..16b2c6bf7b77b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -437,17 +437,17 @@ end @testset "findfirst" begin @test findfirst(==(1), Base.IdentityUnitRange(-1:1)) == 1 @test findfirst(isequal(3), Base.OneTo(10)) == 3 - @test findfirst(==(0), Base.OneTo(10)) == nothing - @test findfirst(==(11), Base.OneTo(10)) == nothing + @test findfirst(==(0), Base.OneTo(10)) === nothing + @test findfirst(==(11), Base.OneTo(10)) === nothing @test findfirst(==(4), Int16(3):Int16(7)) === Int(2) - @test findfirst(==(2), Int16(3):Int16(7)) == nothing - @test findfirst(isequal(8), 3:7) == nothing + @test findfirst(==(2), Int16(3):Int16(7)) === nothing + @test findfirst(isequal(8), 3:7) === nothing @test findfirst(isequal(7), 1:2:10) == 4 @test findfirst(==(7), 1:2:10) == 4 - @test findfirst(==(10), 1:2:10) == nothing - @test findfirst(==(11), 1:2:10) == nothing + @test findfirst(==(10), 1:2:10) === nothing + @test findfirst(==(11), 1:2:10) === nothing @test findfirst(==(-7), 1:-1:-10) == 9 - @test findfirst(==(2),1:-1:2) == nothing + @test findfirst(==(2),1:-1:2) === nothing end @testset "reverse" begin @test reverse(reverse(1:10)) == 1:10 diff --git a/test/regex.jl b/test/regex.jl index a1d0b1b0ed69a..51802125a3467 100644 --- a/test/regex.jl +++ b/test/regex.jl @@ -213,7 +213,7 @@ r = r"" * raw"a\Eb|c" @test match(r, raw"a\Eb|c").match == raw"a\Eb|c" - @test match(r, raw"c") == nothing + @test match(r, raw"c") === nothing # error for really incompatible options @test_throws ArgumentError r"a" * Regex("b", Base.DEFAULT_COMPILER_OPTS & ~Base.PCRE.UCP, Base.DEFAULT_MATCH_OPTS) diff --git a/test/some.jl b/test/some.jl index 59ccd05be96bf..89f699d8306c3 100644 --- a/test/some.jl +++ b/test/some.jl @@ -44,8 +44,8 @@ ## == and isequal nothing -@test Some(1) != nothing -@test Some(nothing) != nothing +@test Some(1) !== nothing +@test Some(nothing) !== nothing @test !isequal(Some(1), nothing) @test !isequal(Some(nothing), nothing) diff --git a/test/spawn.jl b/test/spawn.jl index 831eac493d4aa..c1802ba1f74da 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -573,7 +573,7 @@ end @test Cmd(`foo`, env=["A=true"]).env == ["A=true"] @test Cmd(`foo`, env=("A"=>true,)).env == ["A=true"] @test Cmd(`foo`, env=["A"=>true]).env == ["A=true"] -@test Cmd(`foo`, env=nothing).env == nothing +@test Cmd(`foo`, env=nothing).env === nothing # test for interpolation of Cmd let c = setenv(`x`, "A"=>true) diff --git a/test/strings/search.jl b/test/strings/search.jl index e737096b3371d..692286359868d 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -26,19 +26,19 @@ end for str in [astr, GenericString(astr)] @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('∀'), str, 0) - @test findfirst(isequal('x'), str) == nothing - @test findfirst(isequal('\0'), str) == nothing - @test findfirst(isequal('\u80'), str) == nothing - @test findfirst(isequal('∀'), str) == nothing + @test findfirst(isequal('x'), str) === nothing + @test findfirst(isequal('\0'), str) === nothing + @test findfirst(isequal('\u80'), str) === nothing + @test findfirst(isequal('∀'), str) === nothing @test findfirst(isequal('H'), str) == 1 @test findfirst(isequal('l'), str) == 3 @test findnext(isequal('l'), str, 4) == 4 @test findnext(isequal('l'), str, 5) == 11 - @test findnext(isequal('l'), str, 12) == nothing + @test findnext(isequal('l'), str, 12) === nothing @test findfirst(isequal(','), str) == 6 - @test findnext(isequal(','), str, 7) == nothing + @test findnext(isequal(','), str, 7) === nothing @test findfirst(isequal('\n'), str) == 14 - @test findnext(isequal('\n'), str, 15) == nothing + @test findnext(isequal('\n'), str, 15) === nothing @test_throws BoundsError findnext(isequal('ε'), str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end @@ -46,59 +46,59 @@ end for str in [astr, GenericString(astr)] @test_throws BoundsError findnext('z', str, 0) @test_throws BoundsError findnext('∀', str, 0) - @test findfirst('x', str) == nothing - @test findfirst('\0', str) == nothing - @test findfirst('\u80', str) == nothing - @test findfirst('∀', str) == nothing + @test findfirst('x', str) === nothing + @test findfirst('\0', str) === nothing + @test findfirst('\u80', str) === nothing + @test findfirst('∀', str) === nothing @test findfirst('H', str) == 1 @test findfirst('l', str) == 3 @test findfirst('e', str) == 2 - @test findfirst('u', str) == nothing + @test findfirst('u', str) === nothing @test findnext('l', str, 4) == 4 @test findnext('l', str, 5) == 11 - @test findnext('l', str, 12) == nothing + @test findnext('l', str, 12) === nothing @test findfirst(',', str) == 6 - @test findnext(',', str, 7) == nothing + @test findnext(',', str, 7) === nothing @test findfirst('\n', str) == 14 - @test findnext('\n', str, 15) == nothing + @test findnext('\n', str, 15) === nothing @test_throws BoundsError findnext('ε', str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext('a', str, nextind(str,lastindex(str))+1) end # ascii backward search for str in [astr] - @test findlast(isequal('x'), str) == nothing - @test findlast(isequal('\0'), str) == nothing - @test findlast(isequal('\u80'), str) == nothing - @test findlast(isequal('∀'), str) == nothing + @test findlast(isequal('x'), str) === nothing + @test findlast(isequal('\0'), str) === nothing + @test findlast(isequal('\u80'), str) === nothing + @test findlast(isequal('∀'), str) === nothing @test findlast(isequal('H'), str) == 1 - @test findprev(isequal('H'), str, 0) == nothing + @test findprev(isequal('H'), str, 0) === nothing @test findlast(isequal('l'), str) == 11 @test findprev(isequal('l'), str, 5) == 4 @test findprev(isequal('l'), str, 4) == 4 @test findprev(isequal('l'), str, 3) == 3 - @test findprev(isequal('l'), str, 2) == nothing + @test findprev(isequal('l'), str, 2) === nothing @test findlast(isequal(','), str) == 6 - @test findprev(isequal(','), str, 5) == nothing + @test findprev(isequal(','), str, 5) === nothing @test findlast(isequal('\n'), str) == 14 end for str in [astr] - @test findlast('x', str) == nothing - @test findlast('\0', str) == nothing - @test findlast('\u80', str) == nothing - @test findlast('∀', str) == nothing + @test findlast('x', str) === nothing + @test findlast('\0', str) === nothing + @test findlast('\u80', str) === nothing + @test findlast('∀', str) === nothing @test findlast('H', str) == 1 - @test findprev('H', str, 0) == nothing + @test findprev('H', str, 0) === nothing @test findlast('l', str) == 11 @test findprev('l', str, 5) == 4 @test findprev('l', str, 4) == 4 @test findprev('l', str, 3) == 3 - @test findprev('l', str, 2) == nothing + @test findprev('l', str, 2) === nothing @test findlast(',', str) == 6 - @test findprev(',', str, 5) == nothing - @test findlast(str, "") == nothing - @test findlast(str^2, str) == nothing + @test findprev(',', str, 5) === nothing + @test findlast(str, "") === nothing + @test findlast(str^2, str) === nothing @test findlast('\n', str) == 14 end @@ -106,133 +106,133 @@ end for str in (u8str, GenericString(u8str)) @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('∀'), str, 0) - @test findfirst(isequal('z'), str) == nothing - @test findfirst(isequal('\0'), str) == nothing - @test findfirst(isequal('\u80'), str) == nothing - @test findfirst(isequal('∄'), str) == nothing + @test findfirst(isequal('z'), str) === nothing + @test findfirst(isequal('\0'), str) === nothing + @test findfirst(isequal('\u80'), str) === nothing + @test findfirst(isequal('∄'), str) === nothing @test findfirst(isequal('∀'), str) == 1 @test_throws StringIndexError findnext(isequal('∀'), str, 2) - @test findnext(isequal('∀'), str, 4) == nothing + @test findnext(isequal('∀'), str, 4) === nothing @test findfirst(isequal('∃'), str) == 13 @test_throws StringIndexError findnext(isequal('∃'), str, 15) - @test findnext(isequal('∃'), str, 16) == nothing + @test findnext(isequal('∃'), str, 16) === nothing @test findfirst(isequal('x'), str) == 26 @test findnext(isequal('x'), str, 27) == 43 - @test findnext(isequal('x'), str, 44) == nothing + @test findnext(isequal('x'), str, 44) === nothing @test findfirst(isequal('δ'), str) == 17 @test_throws StringIndexError findnext(isequal('δ'), str, 18) @test findnext(isequal('δ'), str, nextind(str,17)) == 33 - @test findnext(isequal('δ'), str, nextind(str,33)) == nothing + @test findnext(isequal('δ'), str, nextind(str,33)) === nothing @test findfirst(isequal('ε'), str) == 5 @test findnext(isequal('ε'), str, nextind(str,5)) == 54 - @test findnext(isequal('ε'), str, nextind(str,54)) == nothing - @test findnext(isequal('ε'), str, nextind(str,lastindex(str))) == nothing - @test findnext(isequal('a'), str, nextind(str,lastindex(str))) == nothing + @test findnext(isequal('ε'), str, nextind(str,54)) === nothing + @test findnext(isequal('ε'), str, nextind(str,lastindex(str))) === nothing + @test findnext(isequal('a'), str, nextind(str,lastindex(str))) === nothing @test_throws BoundsError findnext(isequal('ε'), str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end # utf-8 backward search for str in [u8str] - @test findlast(isequal('z'), str) == nothing - @test findlast(isequal('\0'), str) == nothing - @test findlast(isequal('\u80'), str) == nothing - @test findlast(isequal('∄'), str) == nothing + @test findlast(isequal('z'), str) === nothing + @test findlast(isequal('\0'), str) === nothing + @test findlast(isequal('\u80'), str) === nothing + @test findlast(isequal('∄'), str) === nothing @test findlast(isequal('∀'), str) == 1 - @test findprev(isequal('∀'), str, 0) == nothing + @test findprev(isequal('∀'), str, 0) === nothing @test findlast(isequal('∃'), str) == 13 @test findprev(isequal('∃'), str, 14) == 13 @test findprev(isequal('∃'), str, 13) == 13 - @test findprev(isequal('∃'), str, 12) == nothing + @test findprev(isequal('∃'), str, 12) === nothing @test findlast(isequal('x'), str) == 43 @test findprev(isequal('x'), str, 42) == 26 - @test findprev(isequal('x'), str, 25) == nothing + @test findprev(isequal('x'), str, 25) === nothing @test findlast(isequal('δ'), str) == 33 @test findprev(isequal('δ'), str, 32) == 17 - @test findprev(isequal('δ'), str, 16) == nothing + @test findprev(isequal('δ'), str, 16) === nothing @test findlast(isequal('ε'), str) == 54 @test findprev(isequal('ε'), str, 53) == 5 - @test findprev(isequal('ε'), str, 4) == nothing + @test findprev(isequal('ε'), str, 4) === nothing end # string forward search with a single-char string -@test findfirst("x", astr) == nothing +@test findfirst("x", astr) === nothing @test findfirst("H", astr) == 1:1 -@test findnext("H", astr, 2) == nothing +@test findnext("H", astr, 2) === nothing @test findfirst("l", astr) == 3:3 @test findnext("l", astr, 4) == 4:4 @test findnext("l", astr, 5) == 11:11 -@test findnext("l", astr, 12) == nothing +@test findnext("l", astr, 12) === nothing @test findfirst("\n", astr) == 14:14 -@test findnext("\n", astr, 15) == nothing +@test findnext("\n", astr, 15) === nothing -@test findfirst("z", u8str) == nothing -@test findfirst("∄", u8str) == nothing +@test findfirst("z", u8str) === nothing +@test findfirst("∄", u8str) === nothing @test findfirst("∀", u8str) == 1:1 -@test findnext("∀", u8str, 4) == nothing +@test findnext("∀", u8str, 4) === nothing @test findfirst("∃", u8str) == 13:13 -@test findnext("∃", u8str, 16) == nothing +@test findnext("∃", u8str, 16) === nothing @test findfirst("x", u8str) == 26:26 @test findnext("x", u8str, 27) == 43:43 -@test findnext("x", u8str, 44) == nothing +@test findnext("x", u8str, 44) === nothing @test findfirst("ε", u8str) == 5:5 @test findnext("ε", u8str, 7) == 54:54 -@test findnext("ε", u8str, 56) == nothing +@test findnext("ε", u8str, 56) === nothing # strifindprev backward search with a single-char string -@test findlast("x", astr) == nothing +@test findlast("x", astr) === nothing @test findlast("H", astr) == 1:1 @test findprev("H", astr, 2) == 1:1 -@test findprev("H", astr, 0) == nothing +@test findprev("H", astr, 0) === nothing @test findlast("l", astr) == 11:11 @test findprev("l", astr, 10) == 4:4 @test findprev("l", astr, 4) == 4:4 @test findprev("l", astr, 3) == 3:3 -@test findprev("l", astr, 2) == nothing +@test findprev("l", astr, 2) === nothing @test findlast("\n", astr) == 14:14 -@test findprev("\n", astr, 13) == nothing +@test findprev("\n", astr, 13) === nothing -@test findlast("z", u8str) == nothing -@test findlast("∄", u8str) == nothing +@test findlast("z", u8str) === nothing +@test findlast("∄", u8str) === nothing @test findlast("∀", u8str) == 1:1 -@test findprev("∀", u8str, 0) == nothing +@test findprev("∀", u8str, 0) === nothing #TODO: setting the limit in the middle of a wide char # makes findnext fail but findprev succeed. # Should findprev fail as well? -#@test findprev("∀", u8str, 2) == nothing # gives 1:3 +#@test findprev("∀", u8str, 2) === nothing # gives 1:3 @test findlast("∃", u8str) == 13:13 -@test findprev("∃", u8str, 12) == nothing +@test findprev("∃", u8str, 12) === nothing @test findlast("x", u8str) == 43:43 @test findprev("x", u8str, 42) == 26:26 -@test findprev("x", u8str, 25) == nothing +@test findprev("x", u8str, 25) === nothing @test findlast("ε", u8str) == 54:54 @test findprev("ε", u8str, 53) == 5:5 -@test findprev("ε", u8str, 4) == nothing +@test findprev("ε", u8str, 4) === nothing # string forward search with a single-char regex -@test findfirst(r"x", astr) == nothing +@test findfirst(r"x", astr) === nothing @test findfirst(r"H", astr) == 1:1 -@test findnext(r"H", astr, 2) == nothing +@test findnext(r"H", astr, 2) === nothing @test findfirst(r"l", astr) == 3:3 @test findnext(r"l", astr, 4) == 4:4 @test findnext(r"l", astr, 5) == 11:11 -@test findnext(r"l", astr, 12) == nothing +@test findnext(r"l", astr, 12) === nothing @test findfirst(r"\n", astr) == 14:14 -@test findnext(r"\n", astr, 15) == nothing -@test findfirst(r"z", u8str) == nothing -@test findfirst(r"∄", u8str) == nothing +@test findnext(r"\n", astr, 15) === nothing +@test findfirst(r"z", u8str) === nothing +@test findfirst(r"∄", u8str) === nothing @test findfirst(r"∀", u8str) == 1:1 -@test findnext(r"∀", u8str, 4) == nothing +@test findnext(r"∀", u8str, 4) === nothing @test findfirst(r"∀", u8str) == findfirst(r"\u2200", u8str) @test findnext(r"∀", u8str, 4) == findnext(r"\u2200", u8str, 4) @test findfirst(r"∃", u8str) == 13:13 -@test findnext(r"∃", u8str, 16) == nothing +@test findnext(r"∃", u8str, 16) === nothing @test findfirst(r"x", u8str) == 26:26 @test findnext(r"x", u8str, 27) == 43:43 -@test findnext(r"x", u8str, 44) == nothing +@test findnext(r"x", u8str, 44) === nothing @test findfirst(r"ε", u8str) == 5:5 @test findnext(r"ε", u8str, 7) == 54:54 -@test findnext(r"ε", u8str, 56) == nothing +@test findnext(r"ε", u8str, 56) === nothing for i = 1:lastindex(astr) @test findnext(r"."s, astr, i) == i:i end @@ -272,18 +272,18 @@ for i = 1:lastindex(u8str) end # string forward search with a two-char string literal -@test findfirst("xx", "foo,bar,baz") == nothing +@test findfirst("xx", "foo,bar,baz") === nothing @test findfirst("fo", "foo,bar,baz") == 1:2 -@test findnext("fo", "foo,bar,baz", 3) == nothing +@test findnext("fo", "foo,bar,baz", 3) === nothing @test findfirst("oo", "foo,bar,baz") == 2:3 -@test findnext("oo", "foo,bar,baz", 4) == nothing +@test findnext("oo", "foo,bar,baz", 4) === nothing @test findfirst("o,", "foo,bar,baz") == 3:4 -@test findnext("o,", "foo,bar,baz", 5) == nothing +@test findnext("o,", "foo,bar,baz", 5) === nothing @test findfirst(",b", "foo,bar,baz") == 4:5 @test findnext(",b", "foo,bar,baz", 6) == 8:9 -@test findnext(",b", "foo,bar,baz", 10) == nothing +@test findnext(",b", "foo,bar,baz", 10) === nothing @test findfirst("az", "foo,bar,baz") == 10:11 -@test findnext("az", "foo,bar,baz", 12) == nothing +@test findnext("az", "foo,bar,baz", 12) === nothing # issue #9365 # string forward search with a two-char UTF-8 (2 byte) string literal @@ -327,32 +327,32 @@ end @test findprev("\U1f596\U1f596", "\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 1:5 # string backward search with a two-char string literal -@test findlast("xx", "foo,bar,baz") == nothing +@test findlast("xx", "foo,bar,baz") === nothing @test findlast("fo", "foo,bar,baz") == 1:2 -@test findprev("fo", "foo,bar,baz", 1) == nothing +@test findprev("fo", "foo,bar,baz", 1) === nothing @test findlast("oo", "foo,bar,baz") == 2:3 -@test findprev("oo", "foo,bar,baz", 2) == nothing +@test findprev("oo", "foo,bar,baz", 2) === nothing @test findlast("o,", "foo,bar,baz") == 3:4 -@test findprev("o,", "foo,bar,baz", 1) == nothing +@test findprev("o,", "foo,bar,baz", 1) === nothing @test findlast(",b", "foo,bar,baz") == 8:9 @test findprev(",b", "foo,bar,baz", 6) == 4:5 -@test findprev(",b", "foo,bar,baz", 3) == nothing +@test findprev(",b", "foo,bar,baz", 3) === nothing @test findlast("az", "foo,bar,baz") == 10:11 -@test findprev("az", "foo,bar,baz", 10) == nothing +@test findprev("az", "foo,bar,baz", 10) === nothing # string search with a two-char regex -@test findfirst(r"xx", "foo,bar,baz") == nothing +@test findfirst(r"xx", "foo,bar,baz") === nothing @test findfirst(r"fo", "foo,bar,baz") == 1:2 -@test findnext(r"fo", "foo,bar,baz", 3) == nothing +@test findnext(r"fo", "foo,bar,baz", 3) === nothing @test findfirst(r"oo", "foo,bar,baz") == 2:3 -@test findnext(r"oo", "foo,bar,baz", 4) == nothing +@test findnext(r"oo", "foo,bar,baz", 4) === nothing @test findfirst(r"o,", "foo,bar,baz") == 3:4 -@test findnext(r"o,", "foo,bar,baz", 5) == nothing +@test findnext(r"o,", "foo,bar,baz", 5) === nothing @test findfirst(r",b", "foo,bar,baz") == 4:5 @test findnext(r",b", "foo,bar,baz", 6) == 8:9 -@test findnext(r",b", "foo,bar,baz", 10) == nothing +@test findnext(r",b", "foo,bar,baz", 10) === nothing @test findfirst(r"az", "foo,bar,baz") == 10:11 -@test findnext(r"az", "foo,bar,baz", 12) == nothing +@test findnext(r"az", "foo,bar,baz", 12) === nothing # occursin with a String and Char needle @test occursin("o", "foo") @@ -417,7 +417,7 @@ end A = T[0x40, 0x52, 0x00, 0x52, 0x00] for A in (A, @view(A[1:end]), codeunits(String(copyto!(Vector{UInt8}(undef,5), A)))) - @test findfirst(VT[0x30], A) === findfirst(==(VT(0x30)), A) == nothing + @test findfirst(VT[0x30], A) === findfirst(==(VT(0x30)), A) === nothing @test findfirst(VT[0x52], A) === 2:2 @test findfirst(==(VT(0x52)), A) === 2 @test findlast(VT[0x30], A) === findlast(==(VT(0x30)), A) === nothing diff --git a/test/strings/types.jl b/test/strings/types.jl index 771be253b1ec9..dbcf65b1d843b 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -118,8 +118,8 @@ end # search and SubString (issue #5679) let str = "Hello, world!" u = SubString(str, 1, 5) - @test findlast("World", u) == nothing - @test findlast(isequal('z'), u) == nothing + @test findlast("World", u) === nothing + @test findlast(isequal('z'), u) === nothing @test findlast("ll", u) == 3:4 end diff --git a/test/testdefs.jl b/test/testdefs.jl index b96c95045f2bd..eb0bf570b11fd 100644 --- a/test/testdefs.jl +++ b/test/testdefs.jl @@ -23,7 +23,7 @@ function runtests(name, path, isolate=true; seed=nothing) end res_and_time_data = @timed @testset "$name" begin # Random.seed!(nothing) will fail - seed != nothing && Random.seed!(seed) + seed !== nothing && Random.seed!(seed) original_depot_path = copy(Base.DEPOT_PATH) original_load_path = copy(Base.LOAD_PATH) From 6866b118fd6401cb51495100f2b05761c7ee7318 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 23 Aug 2024 03:21:44 +0530 Subject: [PATCH 101/548] LinearAlgebra: return destination in `setindex!` (#55544) Currently, in LinearAlgebra, `setindex!` occasionally returns the value, and at other times the destination array (or its parent). This PR consistently returns the destination in `setindex!`, which matches the behavior for `Array`s. Note that this does not change the behavior of `A[i,j] = v`, which still returns `v`. This only changes `setindex!`. --- stdlib/LinearAlgebra/src/bidiag.jl | 2 +- stdlib/LinearAlgebra/src/diagonal.jl | 2 +- stdlib/LinearAlgebra/src/symmetric.jl | 2 ++ stdlib/LinearAlgebra/src/tridiag.jl | 4 ++-- stdlib/LinearAlgebra/test/bidiag.jl | 3 +++ stdlib/LinearAlgebra/test/diagonal.jl | 2 ++ stdlib/LinearAlgebra/test/symmetric.jl | 8 ++++++++ stdlib/LinearAlgebra/test/tridiag.jl | 2 ++ 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 5aa4314c9ae51..2b99cb1800409 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -186,7 +186,7 @@ end throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off the ", A.uplo == 'U' ? "upper" : "lower", " bidiagonal band to a nonzero value ", x))) end - return x + return A end Base._reverse(A::Bidiagonal, dims) = reverse!(Matrix(A); dims) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 77459f7cca520..92f399bb774ff 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -205,7 +205,7 @@ function setindex!(D::Diagonal, v, i::Int, j::Int) elseif !iszero(v) throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)")) end - return v + return D end diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index c336785792588..ab7b5ee031260 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -261,6 +261,7 @@ Base._reverse(A::Symmetric, ::Colon) = Symmetric(reverse(A.data), A.uplo == 'U' @propagate_inbounds function setindex!(A::Symmetric, v, i::Integer, j::Integer) i == j || throw(ArgumentError("Cannot set a non-diagonal index in a symmetric matrix")) setindex!(A.data, v, i, j) + return A end Base._reverse(A::Hermitian, dims) = reverse!(Matrix(A); dims) @@ -274,6 +275,7 @@ Base._reverse(A::Hermitian, ::Colon) = Hermitian(reverse(A.data), A.uplo == 'U' else setindex!(A.data, v, i, j) end + return A end Base.dataids(A::HermOrSym) = Base.dataids(parent(A)) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 0ba03634d82ad..c2806c21c00ff 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -476,7 +476,7 @@ Base._reverse!(A::SymTridiagonal, dims::Colon) = (reverse!(A.dv); reverse!(A.ev) else throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j)")) end - return x + return A end ## Tridiagonal matrices ## @@ -731,7 +731,7 @@ end throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ", lazy"the tridiagonal band to a nonzero value ($x)"))) end - return x + return A end ## structured matrix methods ## diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index e19d890237a26..d0153896106bf 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -124,6 +124,9 @@ Random.seed!(1) Bl = Bidiagonal(rand(elty, 10), zeros(elty, 9), 'L') @test_throws ArgumentError Bu[5, 4] = 1 @test_throws ArgumentError Bl[4, 5] = 1 + + # setindex should return the destination + @test setindex!(ubd, 1, 1, 1) === ubd end @testset "isstored" begin diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 29f3a38473d4a..afb49b696d968 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -617,6 +617,8 @@ end @test_throws ArgumentError D[i, j] = 1 end end + # setindex should return the destination + @test setindex!(D, 1, 1, 1) === D end @testset "Test reverse" begin diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 5f1293ab2cdd7..939e677039dc7 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1127,4 +1127,12 @@ end end end +@testset "setindex! returns the destination" begin + M = rand(2,2) + for T in (Symmetric, Hermitian) + S = T(M) + @test setindex!(S, 0, 2, 2) === S + end +end + end # module TestSymmetric diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index e0a8e32d77852..1aae03c49e686 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -262,6 +262,8 @@ end @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal end + # setindex! should return the destination + @test setindex!(A, A[2,2], 2, 2) === A end @testset "diag" begin @test (@inferred diag(A))::typeof(d) == d From 0c642832dc420c4bb239c846ca496f9d4e3d7927 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:31:54 +0900 Subject: [PATCH 102/548] better implementations for `unionlen`/`uniontypes` (#55561) - unify the dispatch targets - removed unnecessary `_uniontypes(::MustAlias)` method --- base/compiler/typelattice.jl | 2 -- base/reflection.jl | 14 ++++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index d375d61c0bdd8..14477c5dc2725 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -142,8 +142,6 @@ end MustAlias(var::SlotNumber, @nospecialize(vartyp), fldidx::Int, @nospecialize(fldtyp)) = MustAlias(slot_id(var), vartyp, fldidx, fldtyp) -_uniontypes(x::MustAlias, ts) = _uniontypes(widenconst(x), ts) - """ alias::InterMustAlias diff --git a/base/reflection.jl b/base/reflection.jl index 6dfaf34bc0047..4b491ca9f6bd4 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1199,11 +1199,17 @@ hasgenerator(m::Core.MethodInstance) = hasgenerator(m.def::Method) # low-level method lookup functions used by the compiler -unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) -unionlen(@nospecialize(x)) = 1 +unionlen(@nospecialize(x)) = x isa Union ? unionlen(x.a) + unionlen(x.b) : 1 -_uniontypes(x::Union, ts) = (_uniontypes(x.a,ts); _uniontypes(x.b,ts); ts) -_uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) +function _uniontypes(@nospecialize(x), ts::Array{Any,1}) + if x isa Union + _uniontypes(x.a, ts) + _uniontypes(x.b, ts) + else + push!(ts, x) + end + return ts +end uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) From 3d20a9210a59097b46ed2cbdcd1e87435873bcfa Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 23 Aug 2024 12:55:46 +0530 Subject: [PATCH 103/548] Fix indexing in _mapreducedim for OffsetArrays (#55506) The destination array was being indexed incorrectly if it had offset indices. This led to the following on nightly: ```julia julia> using OffsetArrays julia> r = 5:100; julia> a = OffsetVector(r, 2); julia> sum(a, dims=1) 1-element OffsetArray(::Vector{Int64}, 3:3) with eltype Int64 with indices 3:3: 0 julia> sum(a) 5040 ``` The indexing was marked `@inbounds`, so this was not throwing an error. This PR also follows #55329 and only marks the indexing operations as `@inbounds`, omitting the function calls. --------- Co-authored-by: Matt Bauman --- base/reducedim.jl | 5 +++-- test/offsetarray.jl | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/base/reducedim.jl b/base/reducedim.jl index e74fe2b765277..0478afe1a46b6 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -258,8 +258,9 @@ function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArrayOrBroadcasted) # use mapreduce_impl, which is probably better tuned to achieve higher performance nslices = div(length(A), lsiz) ibase = first(LinearIndices(A))-1 - for i = 1:nslices - @inbounds R[i] = op(R[i], mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) + for i in eachindex(R) + r = op(@inbounds(R[i]), mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) + @inbounds R[i] = r ibase += lsiz end return R diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 5ee918e85faf7..fb5855dfbaa0d 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -907,3 +907,10 @@ end v = view([1,2,3,4], :) @test v[Base.IdentityUnitRange(2:3)] == OffsetArray(2:3, 2:3) end + +@testset "mapreduce with OffsetRanges" begin + r = 5:100 + a = OffsetArray(r, 2) + b = sum(a, dims=1) + @test b[begin] == sum(r) +end From 5c1cfb3cf32d80235b2262ab73eb3cf5b66c8c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 23 Aug 2024 22:25:07 +0200 Subject: [PATCH 104/548] [Profile] Replace `SIGTERM` with `SIGQUIT` (#55566) The `SIGQUIT` signal is handled specially in CI and automatically uploads failure information artifacts, which may be helpful to debug failures. --------- Co-authored-by: Jameson Nash --- stdlib/Profile/test/runtests.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index cbfdde61d7054..2b1ab546161c6 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -172,8 +172,10 @@ let cmd = Base.julia_cmd() t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING debuginfo registration test BY PROFILE TEST WATCHDOG\n") - kill(p, Base.SIGTERM) - sleep(10) + kill(p, Base.SIGQUIT) + sleep(30) + kill(p, Base.SIGQUIT) + sleep(30) kill(p, Base.SIGKILL) end s = read(p, String) @@ -202,8 +204,10 @@ if Sys.isbsd() || Sys.islinux() t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING siginfo/sigusr1 test BY PROFILE TEST WATCHDOG\n") - kill(p, Base.SIGTERM) - sleep(10) + kill(p, Base.SIGQUIT) + sleep(30) + kill(p, Base.SIGQUIT) + sleep(30) kill(p, Base.SIGKILL) close(notify_exit) end From 94c9d0ada7ce76b8b1c794e77fbc7d2b10ebc2b6 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 24 Aug 2024 04:57:44 +0200 Subject: [PATCH 105/548] document `fourthroot` (#55560) Updates #19529 --- doc/src/base/math.md | 1 + doc/src/manual/mathematical-operations.md | 31 ++++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/doc/src/base/math.md b/doc/src/base/math.md index 7091aa6f1aa87..4f816ce2a6c1d 100644 --- a/doc/src/base/math.md +++ b/doc/src/base/math.md @@ -166,6 +166,7 @@ Base.flipsign Base.sqrt(::Number) Base.isqrt Base.Math.cbrt(::AbstractFloat) +Base.fourthroot(::Number) Base.real Base.imag Base.reim diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 1d613931669fc..d2cef68bd6fff 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -551,21 +551,22 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define ### Powers, logs and roots -| Function | Description | -|:------------------------ |:-------------------------------------------------------------------------- | -| [`sqrt(x)`](@ref), `√x` | square root of `x` | -| [`cbrt(x)`](@ref), `∛x` | cube root of `x` | -| [`hypot(x, y)`](@ref) | hypotenuse of right-angled triangle with other sides of length `x` and `y` | -| [`exp(x)`](@ref) | natural exponential function at `x` | -| [`expm1(x)`](@ref) | accurate `exp(x) - 1` for `x` near zero | -| [`ldexp(x, n)`](@ref) | `x * 2^n` computed efficiently for integer values of `n` | -| [`log(x)`](@ref) | natural logarithm of `x` | -| [`log(b, x)`](@ref) | base `b` logarithm of `x` | -| [`log2(x)`](@ref) | base 2 logarithm of `x` | -| [`log10(x)`](@ref) | base 10 logarithm of `x` | -| [`log1p(x)`](@ref) | accurate `log(1 + x)` for `x` near zero | -| [`exponent(x)`](@ref) | binary exponent of `x` | -| [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | +| Function | Description | +|:----------------------------- |:-------------------------------------------------------------------------- | +| [`sqrt(x)`](@ref), `√x` | square root of `x` | +| [`cbrt(x)`](@ref), `∛x` | cube root of `x` | +| [`fourthroot(x)`](@ref), `∜x` | fourth root of `x` | +| [`hypot(x, y)`](@ref) | hypotenuse of right-angled triangle with other sides of length `x` and `y` | +| [`exp(x)`](@ref) | natural exponential function at `x` | +| [`expm1(x)`](@ref) | accurate `exp(x) - 1` for `x` near zero | +| [`ldexp(x, n)`](@ref) | `x * 2^n` computed efficiently for integer values of `n` | +| [`log(x)`](@ref) | natural logarithm of `x` | +| [`log(b, x)`](@ref) | base `b` logarithm of `x` | +| [`log2(x)`](@ref) | base 2 logarithm of `x` | +| [`log10(x)`](@ref) | base 10 logarithm of `x` | +| [`log1p(x)`](@ref) | accurate `log(1 + x)` for `x` near zero | +| [`exponent(x)`](@ref) | binary exponent of `x` | +| [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | For an overview of why functions like [`hypot`](@ref), [`expm1`](@ref), and [`log1p`](@ref) are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: [expm1, log1p, erfc](https://www.johndcook.com/blog/2010/06/07/math-library-functions-that-seem-unnecessary/), From eaa2edd6ac7b8012af0f67e3993873a0e8945882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 24 Aug 2024 08:47:20 +0200 Subject: [PATCH 106/548] Add tests for alignment of `Int128`/`UInt128` (#55565) --- test/compiler/codegen.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 76e5bd9e8780f..0260113044a3b 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -984,3 +984,18 @@ end @test g55208((Union{}, true, true), 0) === typeof(Union{}) @test string((Core.Union{}, true, true, true)) == "(Union{}, true, true, true)" + +# Issue #55558 +for (T, StructName) in ((Int128, :Issue55558), (UInt128, :UIssue55558)) + @eval begin + struct $(StructName) + a::$(T) + b::Int64 + c::$(T) + end + local broken_i128 = Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "powerpc64le" + @test fieldoffset($(StructName), 2) == 16 + @test fieldoffset($(StructName), 3) == 32 broken=broken_i128 + @test sizeof($(StructName)) == 48 broken=broken_i128 + end +end From 083bd8f687bb2a0608a1b0b4c99f811eecb56b3e Mon Sep 17 00:00:00 2001 From: Devansh Date: Sat, 24 Aug 2024 04:13:40 -0400 Subject: [PATCH 107/548] added cholesky of cholesky (#55559) Currently, if you have a cholesky matrix, you cannot call `cholesky` on it again: ``` using LinearAlgebra N = 10 A = randn(N, N) P = Symmetric(A * A' + I) C = cholesky(P) CC = cholesky(C) # this line throws an error ``` This small PR provides the fix. --------- Co-authored-by: Jeff Bezanson --- stdlib/LinearAlgebra/src/cholesky.jl | 3 +++ stdlib/LinearAlgebra/test/cholesky.jl | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index cb7c6b94d4ca6..545d92ec1704d 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -551,6 +551,9 @@ end # allow packages like SparseArrays.jl to hook into here and redirect to out-of-place `cholesky` _cholesky(A::AbstractMatrix, args...; kwargs...) = cholesky!(A, args...; kwargs...) +# allow cholesky of cholesky +cholesky(A::Cholesky) = A + ## With pivoting """ cholesky(A, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index 2bcc6208c12df..00bfc18a21638 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -630,4 +630,14 @@ end end end +@testset "cholesky_of_cholesky" begin + for T in (Float64, ComplexF64), uplo in (:U, :L) + A = randn(T, 100, 100) + P = Hermitian(A' * A, uplo) + C = cholesky(P) + CC = cholesky(C) + @test C == CC + end +end + end # module TestCholesky From eb5587dac02d1f6edf486a71b95149139cc5d9f7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 25 Aug 2024 09:05:57 +0530 Subject: [PATCH 108/548] Fix self-recursion in generic triangular (l/r)mul! (#55547) This fixes a stack-overflow in the following: ```julia julia> using LinearAlgebra julia> struct MyTriangularWithoutLRMul{T, A<:LinearAlgebra.AbstractTriangular{T}} <: LinearAlgebra.AbstractTriangular{T} data :: A end julia> Base.size(A::MyTriangularWithoutLRMul) = size(A.data) julia> Base.getindex(A::MyTriangularWithoutLRMul, i::Int, j::Int) = A.data[i,j] julia> M = MyTriangularWithoutLRMul(UpperTriangular(rand(4,4))); julia> A = rand(4,4); julia> lmul!(M, A) Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable. ERROR: StackOverflowError: Stacktrace: [1] unsafe_copyto! @ ./genericmemory.jl:122 [inlined] [2] _copyto_impl! @ ./array.jl:308 [inlined] [3] copyto! @ ./array.jl:299 [inlined] [4] copyto! @ ./array.jl:322 [inlined] [5] _trimul!(C::Matrix{Float64}, A::MyTriangularWithoutLRMul{Float64, UpperTriangular{Float64, Matrix{Float64}}}, B::Matrix{Float64}) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:961 [6] lmul!(A::MyTriangularWithoutLRMul{Float64, UpperTriangular{Float64, Matrix{Float64}}}, B::Matrix{Float64}) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:982--- the above 2 lines are repeated 39990 more times --- ``` This is done by rerouting the generic `lmul!`/`rmul!` methods to those for `UpperTriangular` or `LowerTriangular`, depending on which triangular half is populated. A similar issue with `ldiv!`/`rdiv!` is also resolved. --- stdlib/LinearAlgebra/src/triangular.jl | 33 ++++++++++++++++++++---- stdlib/LinearAlgebra/test/triangular.jl | 34 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 365ce8ee4bae2..923e13e488c85 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -979,9 +979,20 @@ _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) = _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B) -rmul!(A::AbstractMatrix, B::AbstractTriangular) = @inline _trimul!(A, A, B) - +function lmul!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _trimul!(B, UpperTriangular(A), B) + else + _trimul!(B, LowerTriangular(A), B) + end +end +function rmul!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _trimul!(A, A, UpperTriangular(B)) + else + _trimul!(A, A, LowerTriangular(B)) + end +end for TC in (:AbstractVector, :AbstractMatrix) @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) @@ -1017,8 +1028,20 @@ _ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) = _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B) -rdiv!(A::AbstractMatrix, B::AbstractTriangular) = @inline _rdiv!(A, A, B) +function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _ldiv!(B, UpperTriangular(A), B) + else + _ldiv!(B, LowerTriangular(A), B) + end +end +function rdiv!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _rdiv!(A, A, UpperTriangular(B)) + else + _rdiv!(A, A, LowerTriangular(B)) + end +end # preserve triangular structure in in-place multiplication/division for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 3f7cea91ec6d4..a9aeab5650f44 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -25,6 +25,12 @@ debug && println("Test basic type functionality") @test_throws DimensionMismatch LowerTriangular(randn(5, 4)) @test LowerTriangular(randn(3, 3)) |> t -> [size(t, i) for i = 1:3] == [size(Matrix(t), i) for i = 1:3] +struct MyTriangular{T, A<:LinearAlgebra.AbstractTriangular{T}} <: LinearAlgebra.AbstractTriangular{T} + data :: A +end +Base.size(A::MyTriangular) = size(A.data) +Base.getindex(A::MyTriangular, i::Int, j::Int) = A.data[i,j] + # The following test block tries to call all methods in base/linalg/triangular.jl in order for a combination of input element types. Keep the ordering when adding code. @testset for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) # Begin loop for first Triangular matrix @@ -1194,4 +1200,32 @@ end end end +@testset "(l/r)mul! and (l/r)div! for generic triangular" begin + @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) + M = MyTriangular(T(rand(4,4))) + A = rand(4,4) + Ac = similar(A) + @testset "lmul!" begin + Ac .= A + lmul!(M, Ac) + @test Ac ≈ M * A + end + @testset "rmul!" begin + Ac .= A + rmul!(Ac, M) + @test Ac ≈ A * M + end + @testset "ldiv!" begin + Ac .= A + ldiv!(M, Ac) + @test Ac ≈ M \ A + end + @testset "rdiv!" begin + Ac .= A + rdiv!(Ac, M) + @test Ac ≈ A / M + end + end +end + end # module TestTriangular From 638a049a6c6f8f0044876942a7fad2e92020695a Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 25 Aug 2024 14:34:36 +0530 Subject: [PATCH 109/548] Quick return for empty arrays in bidiagonal matrix multiplications (#55414) --- stdlib/LinearAlgebra/src/bidiag.jl | 28 +++++++++++++++++----------- stdlib/LinearAlgebra/test/bidiag.jl | 22 ++++++++++++++++++++++ stdlib/LinearAlgebra/test/tridiag.jl | 22 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 2b99cb1800409..a997347eabd58 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -535,12 +535,18 @@ function rmul!(B::Bidiagonal, D::Diagonal) end @noinline function check_A_mul_B!_sizes((mC, nC)::NTuple{2,Integer}, (mA, nA)::NTuple{2,Integer}, (mB, nB)::NTuple{2,Integer}) + # check for matching sizes in one column of B and C + check_A_mul_B!_sizes((mC,), (mA, nA), (mB,)) + # ensure that the number of columns in B and C match + if nB != nC + throw(DimensionMismatch(lazy"second dimension of output C, $nC, and second dimension of B, $nB, must match")) + end +end +@noinline function check_A_mul_B!_sizes((mC,)::Tuple{Integer}, (mA, nA)::NTuple{2,Integer}, (mB,)::Tuple{Integer}) if mA != mC throw(DimensionMismatch(lazy"first dimension of A, $mA, and first dimension of output C, $mC, must match")) elseif nA != mB throw(DimensionMismatch(lazy"second dimension of A, $nA, and first dimension of B, $mB, must match")) - elseif nB != nC - throw(DimensionMismatch(lazy"second dimension of output C, $nC, and second dimension of B, $nB, must match")) end end @@ -563,8 +569,10 @@ _mul!(C::AbstractMatrix, A::BiTriSym, B::TriSym, _add::MulAddMul) = _mul!(C::AbstractMatrix, A::BiTriSym, B::Bidiagonal, _add::MulAddMul) = _bibimul!(C, A, B, _add) function _bibimul!(C, A, B, _add) + require_one_based_indexing(C) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) + iszero(n) && return C n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the @@ -727,15 +735,10 @@ end function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulAddMul) require_one_based_indexing(C, B) + check_A_mul_B!_sizes(size(C), size(A), size(B)) nA = size(A,1) nB = size(B,2) - if !(size(C,1) == size(B,1) == nA) - throw(DimensionMismatch(lazy"A has first dimension $nA, B has $(size(B,1)), C has $(size(C,1)) but all must match")) - end - if size(C,2) != nB - throw(DimensionMismatch(lazy"A has second dimension $nA, B has $(size(B,2)), C has $(size(C,2)) but all must match")) - end - iszero(nA) && return C + (iszero(nA) || iszero(nB)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) l = _diag(A, -1) @@ -758,9 +761,10 @@ end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) require_one_based_indexing(C, A) check_A_mul_B!_sizes(size(C), size(A), size(B)) - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) n = size(A,1) m = size(B,2) + (iszero(m) || iszero(n)) && return C + iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) if n <= 3 || m <= 1 return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end @@ -793,11 +797,12 @@ end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAddMul) require_one_based_indexing(C, A) check_A_mul_B!_sizes(size(C), size(A), size(B)) + m, n = size(A) + (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) if size(A, 1) <= 3 || size(B, 2) <= 1 return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end - m, n = size(A) @inbounds if B.uplo == 'U' for i in 1:m for j in n:-1:2 @@ -824,6 +829,7 @@ function _dibimul!(C, A, B, _add) require_one_based_indexing(C) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) + iszero(n) && return C n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index d0153896106bf..387657ba12d04 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -1023,4 +1023,26 @@ end @test_throws "cannot set entry" B[1,2] = 4 end +@testset "mul with empty arrays" begin + A = zeros(5,0) + B = Bidiagonal(zeros(0), zeros(0), :U) + BL = Bidiagonal(zeros(5), zeros(4), :U) + @test size(A * B) == size(A) + @test size(BL * A) == size(A) + @test size(B * B) == size(B) + C = similar(A) + @test mul!(C, A, B) == A * B + @test mul!(C, BL, A) == BL * A + @test mul!(similar(B), B, B) == B * B + @test mul!(similar(B, size(B)), B, B) == B * B + + v = zeros(size(B,2)) + @test size(B * v) == size(v) + @test mul!(similar(v), B, v) == B * v + + D = Diagonal(zeros(size(B,2))) + @test size(B * D) == size(D * B) == size(D) + @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 1aae03c49e686..759d692f8bc68 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -919,6 +919,28 @@ end end end +@testset "mul with empty arrays" begin + A = zeros(5,0) + T = Tridiagonal(zeros(0), zeros(0), zeros(0)) + TL = Tridiagonal(zeros(4), zeros(5), zeros(4)) + @test size(A * T) == size(A) + @test size(TL * A) == size(A) + @test size(T * T) == size(T) + C = similar(A) + @test mul!(C, A, T) == A * T + @test mul!(C, TL, A) == TL * A + @test mul!(similar(T), T, T) == T * T + @test mul!(similar(T, size(T)), T, T) == T * T + + v = zeros(size(T,2)) + @test size(T * v) == size(v) + @test mul!(similar(v), T, v) == T * v + + D = Diagonal(zeros(size(T,2))) + @test size(T * D) == size(D * T) == size(D) + @test mul!(similar(D), T, D) == mul!(similar(D), D, T) == T * D +end + @testset "show" begin T = Tridiagonal(1:3, 1:4, 1:3) @test sprint(show, T) == "Tridiagonal(1:3, 1:4, 1:3)" From adb323f1b51b65dbd17ffe1d2325a3e7d928b2ff Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sun, 25 Aug 2024 12:41:21 +0200 Subject: [PATCH 110/548] include the `[]` doc string into the docs (#55400) --- doc/src/base/base.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 1a8cd29f91066..b5d50a846ce89 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -106,6 +106,7 @@ where . -> :: +[] ``` ## Standard Modules From 0c8641aacb53e6f4c7f21dd08e35873a2801a8f8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sun, 25 Aug 2024 10:21:58 -0300 Subject: [PATCH 111/548] Empty out loaded_precompiles dict instead of asserting it's empty. (#55564) This dict will contain things if we load a package image during precompilation --- base/Base.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index 081426fa94d67..10a8dd1532f92 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -647,7 +647,7 @@ function __init__() init_active_project() append!(empty!(_sysimage_modules), keys(loaded_modules)) empty!(explicit_loaded_modules) - @assert isempty(loaded_precompiles) + empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty for (mod, key) in module_keys loaded_precompiles[key => module_build_id(mod)] = mod end From 383c8efcc70c44b6e3bf07199e3f7ad5a14cde42 Mon Sep 17 00:00:00 2001 From: Kiran Pamnany Date: Sun, 25 Aug 2024 12:02:34 -0400 Subject: [PATCH 112/548] Redact object data in heap snapshots, with option to opt-out (#55326) The contents of strings can contain user data which may be proprietary and emitting them in the heap snapshot makes the heap snapshot a potential vulnerability rather than a useful debugging artifact. There are likely other tweaks necessary to make heap snapshots "safe", but this is one less. --------- Co-authored-by: Nathan Daly Co-authored-by: Ian Butterworth --- NEWS.md | 4 ++++ src/gc-heap-snapshot.cpp | 7 +++++-- src/gc-heap-snapshot.h | 2 +- stdlib/Profile/src/Profile.jl | 28 ++++++++++++++++------------ stdlib/Profile/test/runtests.jl | 21 ++++++++++++++++++--- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4bbe7645165dd..b5caaf5376fb5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -135,6 +135,10 @@ Standard library changes #### Profile +* `Profile.take_heap_snapshot` takes a new keyword argument, `redact_data::Bool`, + that is `true` by default. When set, the contents of Julia objects are not emitted + in the heap snapshot. This currently only applies to strings. ([#55326]) + #### Random #### REPL diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index b84d1f96f273c..4fcc66495fc45 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -182,6 +182,7 @@ struct HeapSnapshot { // global heap snapshot, mutated by garbage collector // when snapshotting is on. int gc_heap_snapshot_enabled = 0; +int gc_heap_snapshot_redact_data = 0; HeapSnapshot *g_snapshot = nullptr; // mutex for gc-heap-snapshot. jl_mutex_t heapsnapshot_lock; @@ -195,7 +196,7 @@ void _add_synthetic_root_entries(HeapSnapshot *snapshot) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, - ios_t *strings, ios_t *json, char all_one) + ios_t *strings, ios_t *json, char all_one, char redact_data) { HeapSnapshot snapshot; snapshot.nodes = nodes; @@ -207,6 +208,7 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, // Enable snapshotting g_snapshot = &snapshot; + gc_heap_snapshot_redact_data = redact_data; gc_heap_snapshot_enabled = true; _add_synthetic_root_entries(&snapshot); @@ -216,6 +218,7 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, // Disable snapshotting gc_heap_snapshot_enabled = false; + gc_heap_snapshot_redact_data = 0; g_snapshot = nullptr; jl_mutex_unlock(&heapsnapshot_lock); @@ -328,7 +331,7 @@ size_t record_node_to_gc_snapshot(jl_value_t *a) JL_NOTSAFEPOINT if (jl_is_string(a)) { node_type = "String"; - name = jl_string_data(a); + name = gc_heap_snapshot_redact_data ? "" : jl_string_data(a); self_size = jl_string_len(a); } else if (jl_is_symbol(a)) { diff --git a/src/gc-heap-snapshot.h b/src/gc-heap-snapshot.h index a58f58aba5458..e7fbb36249ec1 100644 --- a/src/gc-heap-snapshot.h +++ b/src/gc-heap-snapshot.h @@ -122,7 +122,7 @@ static inline void gc_heap_snapshot_record_finlist(jl_value_t *finlist, size_t i // Functions to call from Julia to take heap snapshot // --------------------------------------------------------------------- JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, - ios_t *strings, ios_t *json, char all_one); + ios_t *strings, ios_t *json, char all_one, char redact_data); #ifdef __cplusplus diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 55352cc4b3a9c..799f23034b9ac 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -1250,8 +1250,10 @@ end """ - Profile.take_heap_snapshot(filepath::String, all_one::Bool=false, streaming=false) - Profile.take_heap_snapshot(all_one::Bool=false; dir::String, streaming=false) + Profile.take_heap_snapshot(filepath::String, all_one::Bool=false; + redact_data::Bool=true, streaming::Bool=false) + Profile.take_heap_snapshot(all_one::Bool=false; redact_data:Bool=true, + dir::String=nothing, streaming::Bool=false) Write a snapshot of the heap, in the JSON format expected by the Chrome Devtools Heap Snapshot viewer (.heapsnapshot extension) to a file @@ -1262,6 +1264,8 @@ full file path, or IO stream. If `all_one` is true, then report the size of every object as one so they can be easily counted. Otherwise, report the actual size. +If `redact_data` is true (default), then do not emit the contents of any object. + If `streaming` is true, we will stream the snapshot data out into four files, using filepath as the prefix, to avoid having to hold the entire snapshot in memory. This option should be used for any setting where your memory is constrained. These files can then be reassembled @@ -1277,28 +1281,28 @@ backwards-compatibility) and your process is killed, note that this will always parts in the same directory as your provided filepath, so you can still reconstruct the snapshot after the fact, via `assemble_snapshot()`. """ -function take_heap_snapshot(filepath::AbstractString, all_one::Bool=false; streaming::Bool=false) +function take_heap_snapshot(filepath::AbstractString, all_one::Bool=false; redact_data::Bool=true, streaming::Bool=false) if streaming - _stream_heap_snapshot(filepath, all_one) + _stream_heap_snapshot(filepath, all_one, redact_data) else # Support the legacy, non-streaming mode, by first streaming the parts, then # reassembling it after we're done. prefix = filepath - _stream_heap_snapshot(prefix, all_one) + _stream_heap_snapshot(prefix, all_one, redact_data) Profile.HeapSnapshot.assemble_snapshot(prefix, filepath) Profile.HeapSnapshot.cleanup_streamed_files(prefix) end return filepath end -function take_heap_snapshot(io::IO, all_one::Bool=false) +function take_heap_snapshot(io::IO, all_one::Bool=false; redact_data::Bool=true) # Support the legacy, non-streaming mode, by first streaming the parts to a tempdir, # then reassembling it after we're done. dir = tempdir() prefix = joinpath(dir, "snapshot") - _stream_heap_snapshot(prefix, all_one) + _stream_heap_snapshot(prefix, all_one, redact_data) Profile.HeapSnapshot.assemble_snapshot(prefix, io) end -function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) +function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool, redact_data::Bool) # Nodes and edges are binary files open("$prefix.nodes", "w") do nodes open("$prefix.edges", "w") do edges @@ -1311,9 +1315,9 @@ function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) Base.@_lock_ios(json, ccall(:jl_gc_take_heap_snapshot, Cvoid, - (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}, Cchar), + (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}, Cchar, Cchar), nodes.handle, edges.handle, strings.handle, json.handle, - Cchar(all_one)) + Cchar(all_one), Cchar(redact_data)) ) ) ) @@ -1323,7 +1327,7 @@ function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) end end end -function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing) where {S <: AbstractString} +function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing, kwargs...) where {S <: AbstractString} fname = "$(getpid())_$(time_ns()).heapsnapshot" if isnothing(dir) wd = pwd() @@ -1338,7 +1342,7 @@ function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing) else fpath = joinpath(expanduser(dir), fname) end - return take_heap_snapshot(fpath, all_one) + return take_heap_snapshot(fpath, all_one; kwargs...) end """ diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 2b1ab546161c6..32d628130c4ac 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -279,16 +279,31 @@ end @testset "HeapSnapshot" begin tmpdir = mktempdir() + + # ensure that we can prevent redacting data fname = cd(tmpdir) do - read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; print(Profile.take_heap_snapshot())"`, String) + read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; const x = \"redact_this\"; print(Profile.take_heap_snapshot(; redact_data=false))"`, String) end @test isfile(fname) - open(fname) do fs - @test readline(fs) != "" + sshot = read(fname, String) + @test sshot != "" + @test contains(sshot, "redact_this") + + rm(fname) + + # ensure that string data is redacted by default + fname = cd(tmpdir) do + read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; const x = \"redact_this\"; print(Profile.take_heap_snapshot())"`, String) end + @test isfile(fname) + + sshot = read(fname, String) + @test sshot != "" + @test !contains(sshot, "redact_this") + rm(fname) rm(tmpdir, force = true, recursive = true) end From 03451ff827aec1bf85113bd63b9fe9d36a55589d Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 25 Aug 2024 18:06:09 -0400 Subject: [PATCH 113/548] Small missing AnnotatedString/Char tests (#55582) Just a few things that coverage shows aren't hit yet --- test/strings/annotated.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index c8fa0680113a7..90aaadd6ede24 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -5,6 +5,7 @@ @test str == Base.AnnotatedString(str.string, Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]) @test length(str) == 11 @test ncodeunits(str) == 11 + @test convert(Base.AnnotatedString, str) === str @test eltype(str) == Base.AnnotatedChar{eltype(str.string)} @test first(str) == Base.AnnotatedChar(first(str.string), Pair{Symbol, Any}[]) @test str[1:4] isa SubString{typeof(str)} @@ -63,6 +64,8 @@ end @testset "AnnotatedChar" begin chr = Base.AnnotatedChar('c') + @test Base.AnnotatedChar(UInt32('c')) == chr + @test convert(Base.AnnotatedChar, chr) === chr @test chr == Base.AnnotatedChar(chr.char, Pair{Symbol, Any}[]) @test uppercase(chr) == Base.AnnotatedChar('C') @test titlecase(chr) == Base.AnnotatedChar('C') From 647753071a1e2ddbddf7ab07f55d7146238b6b72 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 25 Aug 2024 19:39:29 -0400 Subject: [PATCH 114/548] prevent stackoverflow of stat/lstat (#55554) Gives a better error message if joinpath does not change types (which will cause stat/lstat to resolve to the same method and crash). Fixes #50890 --- base/stat.jl | 4 ++++ test/file.jl | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/base/stat.jl b/base/stat.jl index 3330ff0c35bc8..506b5644dccbc 100644 --- a/base/stat.jl +++ b/base/stat.jl @@ -198,6 +198,7 @@ end """ stat(file) + stat(joinpath...) Return a structure whose fields contain information about the file. The fields of the structure are: @@ -218,16 +219,19 @@ The fields of the structure are: | mtime | `Float64` | Unix timestamp of when the file was last modified | | ctime | `Float64` | Unix timestamp of when the file's metadata was changed | """ +stat(path) = (path2 = joinpath(path); path2 isa typeof(path) ? error("stat not implemented for $(typeof(path))") : stat(path2)) stat(path...) = stat(joinpath(path...)) """ lstat(file) + lstat(joinpath...) Like [`stat`](@ref), but for symbolic links gets the info for the link itself rather than the file it refers to. This function must be called on a file path rather than a file object or a file descriptor. """ +lstat(path) = (path2 = joinpath(path); path2 isa typeof(path) ? error("lstat not implemented for $(typeof(path))") : lstat(path2)) lstat(path...) = lstat(joinpath(path...)) # some convenience functions diff --git a/test/file.jl b/test/file.jl index 005c765e08b90..4531cd8e66998 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1753,8 +1753,18 @@ end @test s.blocks isa Int64 @test s.mtime isa Float64 @test s.ctime isa Float64 + + @test s === stat((f,)) + @test s === lstat((f,)) + @test s === stat(".", f) + @test s === lstat(".", f) end +mutable struct URI50890; f::String; end +Base.joinpath(x::URI50890) = URI50890(x.f) +@test_throws "stat not implemented" stat(URI50890(".")) +@test_throws "lstat not implemented" lstat(URI50890(".")) + @testset "StatStruct show's extended details" begin f, io = mktemp() s = stat(f) From 733b3f55a1daa7afd2a6804e06dba46fc2f2cfa0 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 26 Aug 2024 19:27:17 -0300 Subject: [PATCH 115/548] Fix cong implementation to be properly random and not just cycling. (#55509) This was found by @IanButterworth. It unfortunately has a small performance regression due to actually using all the rng bits --- src/gc-stock.h | 2 +- src/julia_internal.h | 34 +++++++++++++++++++++++++--------- src/scheduler.c | 2 +- src/signal-handling.c | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/gc-stock.h b/src/gc-stock.h index 7176ad8b504f4..45c93bf4289ae 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -446,7 +446,7 @@ STATIC_INLINE int gc_is_concurrent_collector_thread(int tid) JL_NOTSAFEPOINT STATIC_INLINE int gc_random_parallel_collector_thread_id(jl_ptls_t ptls) JL_NOTSAFEPOINT { assert(jl_n_markthreads > 0); - int v = gc_first_tid + (int)cong(jl_n_markthreads - 1, &ptls->rngseed); + int v = gc_first_tid + (int)cong(jl_n_markthreads, &ptls->rngseed); // cong is [0, n) assert(v >= gc_first_tid && v <= gc_last_parallel_collector_thread_id()); return v; } diff --git a/src/julia_internal.h b/src/julia_internal.h index 23a9c90edf8aa..dad28791f8c35 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1307,20 +1307,36 @@ JL_DLLEXPORT size_t jl_maxrss(void); // congruential random number generator // for a small amount of thread-local randomness -STATIC_INLINE uint64_t cong(uint64_t max, uint64_t *seed) JL_NOTSAFEPOINT +//TODO: utilize https://github.com/openssl/openssl/blob/master/crypto/rand/rand_uniform.c#L13-L99 +// for better performance, it does however require making users expect a 32bit random number. + +STATIC_INLINE uint64_t cong(uint64_t max, uint64_t *seed) JL_NOTSAFEPOINT // Open interval [0, max) { - if (max == 0) + if (max < 2) return 0; uint64_t mask = ~(uint64_t)0; - --max; - mask >>= __builtin_clzll(max|1); - uint64_t x; + int zeros = __builtin_clzll(max); + int bits = CHAR_BIT * sizeof(uint64_t) - zeros; + mask = mask >> zeros; do { - *seed = 69069 * (*seed) + 362437; - x = *seed & mask; - } while (x > max); - return x; + uint64_t value = 69069 * (*seed) + 362437; + *seed = value; + uint64_t x = value & mask; + if (x < max) { + return x; + } + int bits_left = zeros; + while (bits_left >= bits) { + value >>= bits; + x = value & mask; + if (x < max) { + return x; + } + bits_left -= bits; + } + } while (1); } + JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_srand(uint64_t) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_init_rand(void); diff --git a/src/scheduler.c b/src/scheduler.c index 3cf97ba108873..71943a25f3233 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -87,7 +87,7 @@ extern int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, // parallel task runtime // --- -JL_DLLEXPORT uint32_t jl_rand_ptls(uint32_t max) +JL_DLLEXPORT uint32_t jl_rand_ptls(uint32_t max) // [0, n) { jl_ptls_t ptls = jl_current_task->ptls; return cong(max, &ptls->rngseed); diff --git a/src/signal-handling.c b/src/signal-handling.c index 6835f5fa364c5..d7f4697a3c4f0 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -155,7 +155,7 @@ static void jl_shuffle_int_array_inplace(int *carray, int size, uint64_t *seed) // The "modern Fisher–Yates shuffle" - O(n) algorithm // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm for (int i = size; i-- > 1; ) { - size_t j = cong(i, seed); + size_t j = cong(i + 1, seed); // cong is an open interval so we add 1 uint64_t tmp = carray[j]; carray[j] = carray[i]; carray[i] = tmp; From 78b0b74048dd64e742ca28e52994f449ba67c259 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 27 Aug 2024 21:29:03 +0900 Subject: [PATCH 116/548] inference: refine slot undef info within `then` branch of `@isdefined` (#55545) By adding some information to `Conditional`, it is possible to improve the `undef` information of `slot` within the `then` branch of `@isdefined slot`. As a result, it's now possible to prove the `:nothrow`-ness in cases like: ```julia @test Base.infer_effects((Bool,Int)) do c, x local val if c val = x end if @isdefined val return val end return zero(Int) end |> Core.Compiler.is_nothrow ``` --- base/compiler/abstractinterpretation.jl | 16 ++++++++------ base/compiler/typelattice.jl | 28 ++++++++++++++++++------- test/compiler/inference.jl | 12 +++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 26bba7b51a2dd..f3fc4e0423173 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2733,6 +2733,8 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::U rt = Const(false) # never assigned previously elseif !vtyp.undef rt = Const(true) # definitely assigned previously + else # form `Conditional` to refine `vtyp.undef` in the then branch + rt = Conditional(sym, vtyp.typ, vtyp.typ; isdefined=true) end elseif isa(sym, GlobalRef) if InferenceParams(interp).assume_bindings_static @@ -3205,7 +3207,7 @@ end @inline function abstract_eval_basic_statement(interp::AbstractInterpreter, @nospecialize(stmt), pc_vartable::VarTable, frame::InferenceState) if isa(stmt, NewvarNode) - changes = StateUpdate(stmt.slot, VarState(Bottom, true), false) + changes = StateUpdate(stmt.slot, VarState(Bottom, true)) return BasicStmtChange(changes, nothing, Union{}) elseif !isa(stmt, Expr) (; rt, exct) = abstract_eval_statement(interp, stmt, pc_vartable, frame) @@ -3220,7 +3222,7 @@ end end lhs = stmt.args[1] if isa(lhs, SlotNumber) - changes = StateUpdate(lhs, VarState(rt, false), false) + changes = StateUpdate(lhs, VarState(rt, false)) elseif isa(lhs, GlobalRef) handle_global_assignment!(interp, frame, lhs, rt) elseif !isa(lhs, SSAValue) @@ -3230,7 +3232,7 @@ end elseif hd === :method fname = stmt.args[1] if isa(fname, SlotNumber) - changes = StateUpdate(fname, VarState(Any, false), false) + changes = StateUpdate(fname, VarState(Any, false)) end return BasicStmtChange(changes, nothing, Union{}) elseif (hd === :code_coverage_effect || ( @@ -3576,7 +3578,7 @@ function apply_refinement!(𝕃ᵢ::AbstractLattice, slot::SlotNumber, @nospecia oldtyp = vtype.typ ⊏ = strictpartialorder(𝕃ᵢ) if newtyp ⊏ oldtyp - stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef), false) + stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef)) stoverwrite1!(currstate, stmtupdate) end end @@ -3600,7 +3602,9 @@ function conditional_change(𝕃ᵢ::AbstractLattice, currstate::VarTable, condt # "causes" since we ignored those in the comparison newtyp = tmerge(𝕃ᵢ, newtyp, LimitedAccuracy(Bottom, oldtyp.causes)) end - return StateUpdate(SlotNumber(condt.slot), VarState(newtyp, vtype.undef), true) + # if this `Conditional` is from from `@isdefined condt.slot`, refine its `undef` information + newundef = condt.isdefined ? !then_or_else : vtype.undef + return StateUpdate(SlotNumber(condt.slot), VarState(newtyp, newundef), #=conditional=#true) end function condition_object_change(currstate::VarTable, condt::Conditional, @@ -3609,7 +3613,7 @@ function condition_object_change(currstate::VarTable, condt::Conditional, newcondt = Conditional(condt.slot, then_or_else ? condt.thentype : Union{}, then_or_else ? Union{} : condt.elsetype) - return StateUpdate(condslot, VarState(newcondt, vtype.undef), false) + return StateUpdate(condslot, VarState(newcondt, vtype.undef)) end # make as much progress on `frame` as possible (by handling cycles) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 14477c5dc2725..86fa8af21615f 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -73,14 +73,19 @@ struct Conditional slot::Int thentype elsetype - function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) + # `isdefined` indicates this `Conditional` is from `@isdefined slot`, implying that + # the `undef` information of `slot` can be improved in the then branch. + # Since this is only beneficial for local inference, it is not translated into `InterConditional`. + isdefined::Bool + function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype); + isdefined::Bool=false) assert_nested_slotwrapper(thentype) assert_nested_slotwrapper(elsetype) - return new(slot, thentype, elsetype) + return new(slot, thentype, elsetype, isdefined) end end -Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = - Conditional(slot_id(var), thentype, elsetype) +Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype); isdefined::Bool=false) = + Conditional(slot_id(var), thentype, elsetype; isdefined) import Core: InterConditional """ @@ -180,6 +185,7 @@ struct StateUpdate var::SlotNumber vtype::VarState conditional::Bool + StateUpdate(var::SlotNumber, vtype::VarState, conditional::Bool=false) = new(var, vtype, conditional) end """ @@ -305,11 +311,17 @@ end # `Conditional` and `InterConditional` are valid in opposite contexts # (i.e. local inference and inter-procedural call), as such they will never be compared -@nospecializeinfer function issubconditional(lattice::AbstractLattice, a::C, b::C) where {C<:AnyConditional} +@nospecializeinfer issubconditional(𝕃::AbstractLattice, a::Conditional, b::Conditional) = + _issubconditional(𝕃, a, b, #=check_isdefined=#true) +@nospecializeinfer issubconditional(𝕃::AbstractLattice, a::InterConditional, b::InterConditional) = + _issubconditional(𝕃, a, b, #=check_isdefined=#false) +@nospecializeinfer function _issubconditional(𝕃::AbstractLattice, a::C, b::C, check_isdefined::Bool) where C<:AnyConditional if is_same_conditionals(a, b) - if ⊑(lattice, a.thentype, b.thentype) - if ⊑(lattice, a.elsetype, b.elsetype) - return true + if ⊑(𝕃, a.thentype, b.thentype) + if ⊑(𝕃, a.elsetype, b.elsetype) + if !check_isdefined || a.isdefined ≥ b.isdefined + return true + end end end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 6e2fa77eb15c8..7cb97db7b9cf4 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6050,6 +6050,18 @@ end |> Core.Compiler.is_nothrow return nothing end |> Core.Compiler.is_nothrow +# refine `undef` information from `@isdefined` check +@test Base.infer_effects((Bool,Int)) do c, x + local val + if c + val = x + end + if @isdefined val + return val + end + return zero(Int) +end |> Core.Compiler.is_nothrow + # End to end test case for the partially initialized struct with `PartialStruct` @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) @test fully_eliminated() do From f457a7561526aa28e20c4b2a8ff1c650f0c0f910 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 27 Aug 2024 12:25:52 -0400 Subject: [PATCH 117/548] Try to remove likely-unused codepath in codegen for :method (#55532) This codepath is odd. It derives a method name from the slotname metadata and then does an implicit assignment to that slot. Worse, as the comment indicates, the codepath is missing from the interpreter, which will crash if it were to ever encounter such a piece of code. I suspect this pattern is unused - I accidentally broke it badly in (the as of yet unmerged PR) #54788 and neither tests nor pkgeval noticed. So let's try removing it on master. If it turns out something does depend on it, we can go the opposite way and implement it properly in all the places that look at :method. --- src/codegen.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index e9a58d25e3e94..e499d1193dee6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6412,13 +6412,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ bp = julia_binding_gv(ctx, bnd); bp = julia_binding_pvalue(ctx, bp); } - else if (jl_is_slotnumber(mn) || jl_is_argument(mn)) { - // XXX: eval_methoddef does not have this code branch - int sl = jl_slot_number(mn)-1; - jl_varinfo_t &vi = ctx.slots[sl]; - bp = vi.boxroot; - name = literal_pointer_val(ctx, (jl_value_t*)slot_symbol(ctx, sl)); - } if (bp) { Value *mdargs[] = { name, literal_pointer_val(ctx, (jl_value_t*)mod), bp, literal_pointer_val(ctx, bnd) }; jl_cgval_t gf = mark_julia_type( From d5bbcc5aaec8033de1dd5b4b0de730ab40575469 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 27 Aug 2024 15:00:55 -0300 Subject: [PATCH 118/548] Initialize threadpools correctly during sysimg build (#55567) I made a mistake with which threadpool was which. --- src/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/init.c b/src/init.c index 9e6a695c71eb0..1d466a0a736f9 100644 --- a/src/init.c +++ b/src/init.c @@ -875,8 +875,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_n_markthreads = 0; jl_n_sweepthreads = 0; jl_n_gcthreads = 0; - jl_n_threads_per_pool[0] = 1; - jl_n_threads_per_pool[1] = 0; + jl_n_threads_per_pool[0] = 0; // Interactive threadpool + jl_n_threads_per_pool[1] = 1; // Default threadpool } else { post_image_load_hooks(); } From 688811d73fb24f5f91a6d01f445207042b991a79 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 27 Aug 2024 14:15:14 -0400 Subject: [PATCH 119/548] re-enable profiling stack switch test (#55553) Removes the warning on platforms where CFI_NORETURN appears likely to be sufficient alone for this to work (from observation in gdb/lldb) and re-enables the test on all platforms so we can see if more work here is needed at all (e.g. similar annotations in jl_setjmp/jl_longjmp). Refs #43124 --- src/task.c | 8 ++++++-- test/threads.jl | 23 ++++++++--------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/task.c b/src/task.c index a88f3b55fc419..86acac23a186a 100644 --- a/src/task.c +++ b/src/task.c @@ -223,6 +223,7 @@ JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_p } void *_y = t->stkbuf; assert(_x != NULL && _y != NULL); +#if defined(_OS_WINDOWS_) // this platform does not implement CFI_NORETURN correctly or at all in libunwind (or equivalent) which requires a workaround #if defined(_CPU_X86_) || defined(_CPU_X86_64_) void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; assert(*return_address == __builtin_return_address(0)); @@ -230,7 +231,9 @@ JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_p #else #pragma message("warning: CFI_NORETURN not implemented for this platform, so profiling of copy_stacks may segfault in this build") #endif +#else CFI_NORETURN +#endif memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe #if defined(_OS_WINDOWS_) @@ -287,14 +290,15 @@ JL_NO_ASAN static void NOINLINE restore_stack3(jl_ucontext_t *t, jl_ptls_t ptls, restore_stack3(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca } #endif +#if defined(_OS_WINDOWS_) // this platform does not implement CFI_NORETURN correctly or at all in libunwind (or equivalent) which requires a workaround #if defined(_CPU_X86_) || defined(_CPU_X86_64_) void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; assert(*return_address == __builtin_return_address(0)); *return_address = NULL; -#else -#pragma message("warning: CFI_NORETURN not implemented for this platform, so profiling of copy_stacks may segfault in this build") #endif +#else CFI_NORETURN +#endif tsan_switch_to_ctx(t); jl_start_fiber_set(t); // (doesn't return) abort(); diff --git a/test/threads.jl b/test/threads.jl index 7b4558091022b..2832f2a0e972c 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -288,18 +288,16 @@ close(proc.in) proc = run(cmd; wait = false) done = Threads.Atomic{Bool}(false) timeout = false - timer = Timer(100) do _ + timer = Timer(200) do _ timeout = true - for sig in [Base.SIGTERM, Base.SIGHUP, Base.SIGKILL] - for _ in 1:1000 + for sig in (Base.SIGQUIT, Base.SIGKILL) + for _ in 1:3 kill(proc, sig) + sleep(1) if done[] - if sig != Base.SIGTERM - @warn "Terminating `$script` required signal $sig" - end + @warn "Terminating `$script` required signal $sig" return end - sleep(0.001) end end end @@ -309,16 +307,11 @@ close(proc.in) done[] = true close(timer) end - if ( !success(proc) ) || ( timeout ) + if !success(proc) || timeout @error "A \"spawn and wait lots of tasks\" test failed" n proc.exitcode proc.termsignal success(proc) timeout end - if Sys.iswindows() || Sys.isapple() - # Known failure: https://github.com/JuliaLang/julia/issues/43124 - @test_skip success(proc) - else - @test success(proc) - @test !timeout - end + @test success(proc) + @test !timeout end end From 16697f369077011d1d694414fc6ced7276a452e5 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 27 Aug 2024 13:49:19 -0700 Subject: [PATCH 120/548] Downgrade patchelf to v0.17.2 (#55602) This should fix https://github.com/JuliaLang/julia/issues/55423 --- deps/checksums/patchelf | 4 ++-- deps/patchelf.version | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/deps/checksums/patchelf b/deps/checksums/patchelf index e2029b83f14fc..6392e44d8f2e8 100644 --- a/deps/checksums/patchelf +++ b/deps/checksums/patchelf @@ -1,2 +1,2 @@ -patchelf-0.18.0.tar.bz2/md5/9b091a689583fdc7c3206679586322d5 -patchelf-0.18.0.tar.bz2/sha512/bf26194ca3435b141dd330890fcc0c9d805d0ad6a537901dabe6707a13cd28e7e6217462f3ebb3cb4861302dd8632342ec988fc18246c35332a94f2b349d4f4f +patchelf-0.17.2.tar.bz2/md5/d76db4f1a27b0934d0b0d0585b081c0f +patchelf-0.17.2.tar.bz2/sha512/8277adf95513f88fb190536a38bdfdf438a4cc7685d8a130bdffbe064441f0f25095b6c83bbb190133e1a138963776d15b46c247dd2f1a073a1bfe1d1dbdd503 diff --git a/deps/patchelf.version b/deps/patchelf.version index 9038338d45faf..6e4f32a0c2fe4 100644 --- a/deps/patchelf.version +++ b/deps/patchelf.version @@ -1,3 +1,4 @@ ## source build # Patchelf (we don't ship this or even use a JLL, we just always build it) -PATCHELF_VER := 0.18.0 +# NOTE: Do not upgrade this to 0.18+ until https://github.com/NixOS/patchelf/issues/492 is fixed +PATCHELF_VER := 0.17.2 From 878f62113d488dd3e742d68784f46a9c5a163c11 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 28 Aug 2024 12:41:45 +0530 Subject: [PATCH 121/548] Convert triangular inv test comparison to isapprox (#55609) The failing test was noticed in https://github.com/JuliaLang/julia/pull/55607. The comparison will only hold approximately in general, as the inverses are computed differently. --- stdlib/LinearAlgebra/test/triangular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index a9aeab5650f44..5f0a829f9cdda 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -903,7 +903,7 @@ end function test_one_oneunit_triangular(a) b = Matrix(a) @test (@inferred a^1) == b^1 - @test (@inferred a^-1) == b^-1 + @test (@inferred a^-1) ≈ b^-1 @test one(a) == one(b) @test one(a)*a == a @test a*one(a) == a From d882d622e920fab62f0e6fbaddaec87aefb53236 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 28 Aug 2024 04:22:55 -0300 Subject: [PATCH 122/548] Use native tls model in macos for better performance (#55576) Macos has a native tls implementation in clang since at least clang 3.7 which much older than what we require so lets enable it for some small performance gains. We may want to turn on the ifunc optimization that's there as well but I haven't tested it and it's only in clang 18 and up so it would be off for most since Apple clang is 16 on their latest beta https://github.com/llvm/llvm-project/pull/73687 --- cli/loader_lib.c | 2 +- src/gc-stacks.c | 2 +- src/julia_fasttls.h | 7 +------ src/julia_internal.h | 4 +--- src/scheduler.c | 15 +++++++++------ src/threading.c | 43 +++++-------------------------------------- 6 files changed, 18 insertions(+), 55 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 65a5e7621a714..af2a36cfce8ab 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -546,7 +546,7 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { (*jl_codegen_exported_func_addrs[symbol_idx]) = addr; } // Next, if we're on Linux/FreeBSD, set up fast TLS. -#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_) && !defined(_OS_OPENBSD_) +#if !defined(_OS_WINDOWS_) && !defined(_OS_OPENBSD_) void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey"); if (jl_pgcstack_setkey == NULL) { jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n"); diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 65173d3a8df37..08425019a4daf 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -160,7 +160,7 @@ static unsigned select_pool(size_t nb) JL_NOTSAFEPOINT } -static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT +void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); diff --git a/src/julia_fasttls.h b/src/julia_fasttls.h index 1c0929717b293..1f35d3693fefd 100644 --- a/src/julia_fasttls.h +++ b/src/julia_fasttls.h @@ -22,14 +22,9 @@ extern "C" { typedef struct _jl_gcframe_t jl_gcframe_t; -#if defined(_OS_DARWIN_) -#include -typedef void *(jl_get_pgcstack_func)(pthread_key_t); // aka typeof(pthread_getspecific) -#else typedef jl_gcframe_t **(jl_get_pgcstack_func)(void); -#endif -#if !defined(_OS_DARWIN_) && !defined(_OS_WINDOWS_) +#if !defined(_OS_WINDOWS_) #define JULIA_DEFINE_FAST_TLS \ static __attribute__((tls_model("local-exec"))) __thread jl_gcframe_t **jl_pgcstack_localexec; \ JL_DLLEXPORT _Atomic(char) jl_pgcstack_static_semaphore; \ diff --git a/src/julia_internal.h b/src/julia_internal.h index dad28791f8c35..8ea1940224e66 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1006,9 +1006,7 @@ int jl_safepoint_consume_sigint(void); void jl_wake_libuv(void) JL_NOTSAFEPOINT; void jl_set_pgcstack(jl_gcframe_t **) JL_NOTSAFEPOINT; -#if defined(_OS_DARWIN_) -typedef pthread_key_t jl_pgcstack_key_t; -#elif defined(_OS_WINDOWS_) +#if defined(_OS_WINDOWS_) typedef DWORD jl_pgcstack_key_t; #else typedef jl_gcframe_t ***(*jl_pgcstack_key_t)(void) JL_NOTSAFEPOINT; diff --git a/src/scheduler.c b/src/scheduler.c index 71943a25f3233..bd7da13aa42e3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -253,10 +253,7 @@ static void wake_libuv(void) JL_NOTSAFEPOINT JULIA_DEBUG_SLEEPWAKE( io_wakeup_leave = cycleclock() ); } -/* ensure thread tid is awake if necessary */ -JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT -{ - jl_task_t *ct = jl_current_task; +void wakeup_thread(jl_task_t *ct, int16_t tid) JL_NOTSAFEPOINT { // Pass in ptls when we have it already available to save a lookup int16_t self = jl_atomic_load_relaxed(&ct->tid); if (tid != self) jl_fence(); // [^store_buffering_1] @@ -311,6 +308,12 @@ JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT JULIA_DEBUG_SLEEPWAKE( wakeup_leave = cycleclock() ); } +/* ensure thread tid is awake if necessary */ +JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT +{ + jl_task_t *ct = jl_current_task; + wakeup_thread(ct, tid); +} // get the next runnable task static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) @@ -447,7 +450,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, // 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); + wakeup_thread(ct, 0); } if (uvlock) { int enter_eventloop = may_sleep(ptls); @@ -575,7 +578,7 @@ void scheduler_delete_thread(jl_ptls_t ptls) JL_NOTSAFEPOINT else { jl_atomic_fetch_add_relaxed(&n_threads_running, 1); } - jl_wakeup_thread(0); // force thread 0 to see that we do not have the IO lock (and am dead) + wakeup_thread(jl_atomic_load_relaxed(&ptls->current_task), 0); // force thread 0 to see that we do not have the IO lock (and am dead) jl_atomic_fetch_add_relaxed(&n_threads_running, -1); } diff --git a/src/threading.c b/src/threading.c index 8f37ee814056c..a6050ace01833 100644 --- a/src/threading.c +++ b/src/threading.c @@ -82,51 +82,17 @@ JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr) // The tls_states buffer: // // On platforms that do not use ELF (i.e. where `__thread` is emulated with -// lower level API) (Mac, Windows), we use the platform runtime API to create +// lower level API) (Windows), we use the platform runtime API to create // TLS variable directly. // This is functionally equivalent to using `__thread` but can be // more efficient since we can have better control over the creation and // initialization of the TLS buffer. // -// On platforms that use ELF (Linux, FreeBSD), we use a `__thread` variable +// On platforms that support native TLS (ELF platforms + Macos) we use a `__thread` variable // as the fallback in the shared object. For better efficiency, we also // create a `__thread` variable in the main executable using a static TLS // model. -#if defined(_OS_DARWIN_) -// Mac doesn't seem to have static TLS model so the runtime TLS getter -// registration will only add overhead to TLS access. The `__thread` variables -// are emulated with `pthread_key_t` so it is actually faster to use it directly. -static pthread_key_t jl_pgcstack_key; - -__attribute__((constructor)) void jl_init_tls(void) -{ - pthread_key_create(&jl_pgcstack_key, NULL); -} - -JL_CONST_FUNC jl_gcframe_t **jl_get_pgcstack(void) JL_NOTSAFEPOINT -{ - return (jl_gcframe_t**)pthread_getspecific(jl_pgcstack_key); -} - -void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT -{ - pthread_setspecific(jl_pgcstack_key, (void*)pgcstack); -} - -void jl_pgcstack_getkey(jl_get_pgcstack_func **f, pthread_key_t *k) -{ - // for codegen - *f = pthread_getspecific; - *k = jl_pgcstack_key; -} - - -JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, pthread_key_t k) -{ - jl_safe_printf("ERROR: Attempt to change TLS address.\n"); -} - -#elif defined(_OS_WINDOWS_) +#if defined(_OS_WINDOWS_) // Apparently windows doesn't have a static TLS model (or one that can be // reliably used from a shared library) either..... Use `TLSAlloc` instead. @@ -464,6 +430,7 @@ void jl_safepoint_resume_all_threads(jl_task_t *ct) void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT; void scheduler_delete_thread(jl_ptls_t ptls) JL_NOTSAFEPOINT; +void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT; static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER { @@ -492,7 +459,7 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER } if (signal_stack != NULL) { if (signal_stack_size) - jl_free_stack(signal_stack, signal_stack_size); + _jl_free_stack(ptls ,signal_stack, signal_stack_size); else free(signal_stack); } From 644029282cfeafcb3b80d999b0054cdaa9278e3d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 28 Aug 2024 14:05:58 +0530 Subject: [PATCH 123/548] Fast bounds-check for CartesianIndex ranges (#55596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `StepRangeLen{<:CartesianIndex}` indices have been supported since v1.11, but bounds-checking for such indices currently falls back to iterating over the entire range. This PR adds a quick `checkindex` for such ranges. The performance improvement as a consequence: ```julia julia> D = Diagonal(1:10_000); julia> @btime checkbounds($D, diagind($D, IndexCartesian())); 6.697 μs (0 allocations: 0 bytes) # nightly, O(n) 4.044 ns (0 allocations: 0 bytes) # This PR, O(1) ``` --- base/multidimensional.jl | 2 ++ stdlib/LinearAlgebra/test/diagonal.jl | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 5e32a19c2cafb..99f41f2404e47 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -730,6 +730,8 @@ end end @inline checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndex) = checkbounds_indices(Bool, inds, I.I) +@inline checkindex(::Type{Bool}, inds::Tuple, i::AbstractRange{<:CartesianIndex}) = + isempty(i) | (checkindex(Bool, inds, first(i)) & checkindex(Bool, inds, last(i))) # Indexing into Array with mixtures of Integers and CartesianIndices is # extremely performance-sensitive. While the abstract fallbacks support this, diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index afb49b696d968..866c11b9931cd 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1354,4 +1354,9 @@ end end end +@testset "bounds-check with CartesianIndex ranges" begin + D = Diagonal(1:typemax(Int)) + @test checkbounds(Bool, D, diagind(D, IndexCartesian())) +end + end # module TestDiagonal From ec2d6960050eb482b581023d9dcbeb6b11b92f29 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Wed, 28 Aug 2024 08:33:17 -0400 Subject: [PATCH 124/548] Test more UTF-8 characters in transcode (#55580) Previous test just covered 2-byte UTF8. This one should hit more branches for ASCII and 3-byte UTF-8. --- test/strings/basic.jl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 511b498e5cd89..a7266f52f16fc 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1392,8 +1392,15 @@ end end @testset "transcode" begin - str = "αβγ" - @test transcode(String, transcode(UInt16, str)) == str - @test transcode(String, transcode(UInt16, transcode(UInt8, str))) == str - @test transcode(String, transcode(UInt8, transcode(UInt16, str))) == str + # string starting with an ASCII character + str_1 = "zβγ" + # string starting with a 2 byte UTF-8 character + str_2 = "αβγ" + # string starting with a 3 byte UTF-8 character + str_3 = "आख" + @testset for str in (str_1, str_2, str_3) + @test transcode(String, transcode(UInt16, str)) == str + @test transcode(String, transcode(UInt16, transcode(UInt8, str))) == str + @test transcode(String, transcode(UInt8, transcode(UInt16, str))) == str + end end From 378f1920db0243224fbea8aea4e69ce36cd85173 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:58:17 -0400 Subject: [PATCH 125/548] precompile: Ensure CI has inference results available for `jl_create_native` (#55528) `jl_emit_codeinst` expects inference results to be available, so `jl_ci_cache_lookup` needs to provide a CI that has them. I noticed this because it was causing missing code when, e.g., compiling `NetworkOptions.__init__` using `--trim` (#55047). Unfortunately `jl_emit_codeinst` failures are silently ignored (they just leave the LLVM module empty), so it was easy to miss that the looked-up CIs sometimes fail to compile. --- src/aotcompile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 48afa360c0037..b4c8ef6095a55 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -284,7 +284,7 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ jl_value_t *ci = cgparams.lookup(mi, world, world); JL_GC_PROMISE_ROOTED(ci); jl_code_instance_t *codeinst = NULL; - if (ci != jl_nothing) { + if (ci != jl_nothing && jl_atomic_load_relaxed(&((jl_code_instance_t *)ci)->inferred) != jl_nothing) { codeinst = (jl_code_instance_t*)ci; } else { From 2ce69eaa6c19046e59e9479b737813ce47b89b61 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 28 Aug 2024 22:45:34 -0400 Subject: [PATCH 126/548] Make jl_static_show_func_sig more robust (#55620) Fixes a crash in existing compiler tests when build with debug symbols. More generally, this function is used for C-side debugging so should be robust to unexpected data. (In this particular case the signature in question was a plain `Tuple{Any}` from the opaque closure test). --- src/rtutils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtutils.c b/src/rtutils.c index 4a2e5c230883e..a60597827b92c 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1403,6 +1403,7 @@ size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_c return n; } if ((jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) && + ((jl_datatype_t*)ftype)->name->mt && ((jl_datatype_t*)ftype)->name->mt != jl_type_type_mt && ((jl_datatype_t*)ftype)->name->mt != jl_nonfunction_mt) { n += jl_static_show_symbol(s, ((jl_datatype_t*)ftype)->name->mt->name); From a732dc30f1d1a7f27ab7aa75465e8306ec7870b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20=C5=BBygie=C5=82o?= <11896137+pzygielo@users.noreply.github.com> Date: Thu, 29 Aug 2024 04:49:55 +0200 Subject: [PATCH 127/548] Synchronize arg name with description and --help (#55619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotrek Żygieło --- doc/man/julia.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 049543d795acd..ebac4362b39a6 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -28,7 +28,7 @@ julia - a high-level, high-performance dynamic programming language for technical computing .SH SYNOPSIS -\fBjulia\fR [OPTIONS...] \fB--\fR [PROGRAMMFILE] [ARGS...] +\fBjulia\fR [OPTIONS...] \fB--\fR [PROGRAMFILE] [ARGS...] If a Julia source file is given as a \fIPROGRAMFILE\fP (optionally followed by arguments in \fIARGS\fP) Julia will execute the program and exit. From 950a87f15bb7da9eb383c025ec1bd5ab550620f0 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 29 Aug 2024 01:44:49 -0400 Subject: [PATCH 128/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20d1d2fc986=20to=2043e7849ce=20(#55618)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: d1d2fc986 New commit: 43e7849ce Julia version: 1.12.0-DEV Pkg version: 1.12.0 Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/d1d2fc986e7249909b450979acc4d359aacfc88e...43e7849ce37545493d0da3226cd7449f5f88563e ``` $ git log --oneline d1d2fc986..43e7849ce 43e7849ce make some test_logs match any because of new Downloads debugs (#4007) 8b2c0f329 Better error messages if artifact rename fails (#3827) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 | 1 + .../Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 | 1 + .../Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 | 1 - .../Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 create mode 100644 deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 new file mode 100644 index 0000000000000..2d5f5888e777f --- /dev/null +++ b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 @@ -0,0 +1 @@ +d992a5c629199747d68baa1593a7c37d diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 new file mode 100644 index 0000000000000..4201ee05347a7 --- /dev/null +++ b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 @@ -0,0 +1 @@ +27ea738dbc4db8e4a02b00fbbdc4e2047906fe0561dd4c7f9e5ce5ea9b0b7b8ef9e29234f8e435deaa6cb3e29861130b06cb0da11118c40d78f4c475ac48db3f diff --git a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 deleted file mode 100644 index 097013569ceae..0000000000000 --- a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -725181b382adb22ad4f1f5e78db526ed diff --git a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 b/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 deleted file mode 100644 index d6d8155431023..0000000000000 --- a/deps/checksums/Pkg-d1d2fc986e7249909b450979acc4d359aacfc88e.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9ab56f368d5075a6f514ab8d2169239b439610c9bc9aca67a45a8a834b4d4ae7988910de3c78a687e40623fcd8bc9ba4aeee64ae7edf2cc84f1945b7e543a559 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index cc38c67021224..60d2914b7f853 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = d1d2fc986e7249909b450979acc4d359aacfc88e +PKG_SHA1 = 43e7849ce37545493d0da3226cd7449f5f88563e PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From ac0161a67b421e2016d3c6759c486a633a1c70a5 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:51:21 +0900 Subject: [PATCH 129/548] optimizer: don't insert `:throw_undef_if_not` for defined slots (#55600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As an application of JuliaLang/julia#55545, this commit avoids the insertion of `:throw_undef_if_not` nodes when the defined-ness of a slot is guaranteed by abstract interpretation. ```julia julia> function isdefined_nothrow(c, x) local val if c val = x end if @isdefined val return val end return zero(Int) end; julia> @code_typed isdefined_nothrow(true, 42) ``` ```diff diff --git a/old b/new index c4980a5c9c..3d1d6d30f0 100644 --- a/old +++ b/new @@ -4,7 +4,6 @@ CodeInfo( 3 ┄ %3 = φ (#2 => x, #1 => #undef)::Int64 │ %4 = φ (#2 => true, #1 => false)::Bool └── goto #5 if not %4 -4 ─ $(Expr(:throw_undef_if_not, :val, :(%4)))::Any -└── return %3 +4 ─ return %3 5 ─ return 0 ) => Int64 ``` --- base/compiler/ssair/slot2ssa.jl | 9 ++++++--- test/compiler/inference.jl | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 756dc98863af5..e70633ffecf6a 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -88,6 +88,9 @@ function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecializ insert_node!(ir, idx, NewInstruction( Expr(:throw_undef_if_not, ci.slotnames[slot], false), Any)) return UNDEF_TOKEN + elseif has_flag(ir.stmts[idx], IR_FLAG_NOTHROW) + # if the `isdefined`-ness of this slot is guaranteed by abstract interpretation, + # there is no need to form a `:throw_undef_if_not` elseif def_ssa !== true insert_node!(ir, idx, NewInstruction( Expr(:throw_undef_if_not, ci.slotnames[slot], def_ssa), Any)) @@ -153,12 +156,12 @@ end function fixup_uses!(ir::IRCode, ci::CodeInfo, code::Vector{Any}, uses::Vector{Int}, slot::Int, @nospecialize(ssa)) for use in uses - code[use] = fixemup!(x::SlotNumber->slot_id(x)==slot, stmt::SlotNumber->(ssa, true), ir, ci, use, code[use]) + code[use] = fixemup!(x::SlotNumber->slot_id(x)==slot, ::SlotNumber->Pair{Any,Any}(ssa, true), ir, ci, use, code[use]) end end function rename_uses!(ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt), renames::Vector{Pair{Any, Any}}) - return fixemup!(stmt::SlotNumber->true, stmt::SlotNumber->renames[slot_id(stmt)], ir, ci, idx, stmt) + return fixemup!(::SlotNumber->true, x::SlotNumber->renames[slot_id(x)], ir, ci, idx, stmt) end # maybe use expr_type? @@ -656,7 +659,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, visited = BitSet() new_nodes = ir.new_nodes @timeit "SSA Rename" while !isempty(worklist) - (item::Int, pred, incoming_vals) = pop!(worklist) + (item, pred, incoming_vals) = pop!(worklist) if sv.bb_vartables[item] === nothing continue end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7cb97db7b9cf4..485ee579abd52 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6051,7 +6051,7 @@ end |> Core.Compiler.is_nothrow end |> Core.Compiler.is_nothrow # refine `undef` information from `@isdefined` check -@test Base.infer_effects((Bool,Int)) do c, x +function isdefined_nothrow(c, x) local val if c val = x @@ -6060,7 +6060,11 @@ end |> Core.Compiler.is_nothrow return val end return zero(Int) -end |> Core.Compiler.is_nothrow +end +@test Core.Compiler.is_nothrow(Base.infer_effects(isdefined_nothrow, (Bool,Int))) +@test !any(first(only(code_typed(isdefined_nothrow, (Bool,Int)))).code) do @nospecialize x + Meta.isexpr(x, :throw_undef_if_not) +end # End to end test case for the partially initialized struct with `PartialStruct` @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) From cebfd7bc66153b82c56715cb1cb52dac7df8eac8 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 29 Aug 2024 05:34:00 -0400 Subject: [PATCH 130/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Downloads=20stdlib=20from=20a9d274f=20to=201061ecc=20(#55614)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Downloads.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 create mode 100644 deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 delete mode 100644 deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 delete mode 100644 deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 new file mode 100644 index 0000000000000..f42bbedb6d415 --- /dev/null +++ b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 @@ -0,0 +1 @@ +70878dd96911d6960537dfee2a820d98 diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 new file mode 100644 index 0000000000000..83164cad9a89d --- /dev/null +++ b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 @@ -0,0 +1 @@ +87d2bdc6c85cbbce5302aab8ffe92fc542c9c71a396844fcc04c0416be059b00298b4816ab5e5491dbf865660a3a6152f1c245875a1ec75fb49b4c7ba0d303d8 diff --git a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 b/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 deleted file mode 100644 index fc3bce951cafb..0000000000000 --- a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -97bb33510fadec7f4cc4c718c739e9a0 diff --git a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 b/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 deleted file mode 100644 index bf2821e8252b0..0000000000000 --- a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a362aaf762f42deebb8632a7a7980cd22b2777e8c4dc629e418580269e24a64217ad846d61acad70438cfdc190e47ba2ff7716edd4e04d8d10c1d765efce604d diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index 7805348a4b2f5..cb041d86d7f66 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = a9d274ff6588cc5dbfa90e908ee34c2408bab84a +DOWNLOADS_SHA1 = 1061ecc377a053fce0df94e1a19e5260f7c030f5 DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 From 4f1af7f39d05a6c2babafae8c5b1f122ee768604 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:59:19 -0400 Subject: [PATCH 131/548] Allow opting out of `PartialOpaque` support via `Expr(:opaque_closure, ...)` (#55068) This can be useful for when an OpaqueClosure is desired, e.g., as an invalidation barrier or when you know it passes through an inference barrier naturally (in my case, a mutable type). This is intentionally left undocumented for now. --- base/opaque_closure.jl | 4 ++-- src/julia-syntax.scm | 10 ++++++---- test/precompile.jl | 7 +++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index 779cbf55ceaf3..0f1fdf47afed8 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -18,7 +18,7 @@ the argument type may be fixed length even if the function is variadic. This interface is experimental and subject to change or removal without notice. """ macro opaque(ex) - esc(Expr(:opaque_closure, nothing, nothing, nothing, ex)) + esc(Expr(:opaque_closure, nothing, nothing, nothing, #= allow_partial =# true, ex)) end macro opaque(ty, ex) @@ -34,7 +34,7 @@ macro opaque(ty, ex) end AT = (AT !== :_) ? AT : nothing RT = (RT !== :_) ? RT : nothing - return esc(Expr(:opaque_closure, AT, RT, RT, ex)) + return esc(Expr(:opaque_closure, AT, RT, RT, #= allow_partial =# true, ex)) end # OpaqueClosure construction from pre-inferred CodeInfo/IRCode diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 5636caa48e6e6..a2d3ffdd66f67 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2435,7 +2435,8 @@ (let* ((argt (something (list (expand-forms (cadr e)) #f))) (rt_lb (something (list (expand-forms (caddr e)) #f))) (rt_ub (something (list (expand-forms (cadddr e)) #f))) - (F (caddddr e)) + (allow-partial (caddddr e)) + (F (cadddddr e)) (isva (let* ((arglist (function-arglist F)) (lastarg (and (pair? arglist) (last arglist)))) (if (and argt (any (lambda (arg) @@ -2460,7 +2461,7 @@ (let* ((argtype (foldl (lambda (var ex) `(call (core UnionAll) ,var ,ex)) (expand-forms `(curly (core Tuple) ,@argtypes)) (reverse tvars)))) - `(_opaque_closure ,(or argt argtype) ,rt_lb ,rt_ub ,isva ,(length argtypes) ,functionloc ,lam)))) + `(_opaque_closure ,(or argt argtype) ,rt_lb ,rt_ub ,isva ,(length argtypes) ,allow-partial ,functionloc ,lam)))) 'block (lambda (e) @@ -4028,7 +4029,8 @@ f(x) = yt(x) ((_opaque_closure) (let* ((isva (car (cddddr e))) (nargs (cadr (cddddr e))) - (functionloc (caddr (cddddr e))) + (allow-partial (caddr (cddddr e))) + (functionloc (cadddr (cddddr e))) (lam2 (last e)) (vis (lam:vinfo lam2)) (cvs (map car (cadr vis)))) @@ -4040,7 +4042,7 @@ f(x) = yt(x) v))) cvs))) `(new_opaque_closure - ,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) (true) + ,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) ,allow-partial (opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs))) ,@var-exprs)))) ((method) diff --git a/test/precompile.jl b/test/precompile.jl index f45eb4bb1e79e..3e8fe44e1b2f0 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2012,6 +2012,13 @@ precompile_test_harness("Generated Opaque") do load_path Expr(:opaque_closure_method, nothing, 0, false, lno, ci)) end @assert oc_re_generated_no_partial()() === 1 + @generated function oc_re_generated_no_partial_macro() + AT = nothing + RT = nothing + allow_partial = false # makes this legal to generate during pre-compile + return Expr(:opaque_closure, AT, RT, RT, allow_partial, :(()->const_int_barrier())) + end + @assert oc_re_generated_no_partial_macro()() === 1 end """) Base.compilecache(Base.PkgId("GeneratedOpaque")) From 19ac316951d0aa3ffdddbb84cde62648a109259c Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 29 Aug 2024 20:00:25 +0530 Subject: [PATCH 132/548] Indexing in diag for structured matrices (#55610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This would support a wider range of types, and won't be limited to ones that support `zero(T)`. The following would work after this: ```julia julia> D = Diagonal(fill([1 2; 3 4], 4)) 4×4 Diagonal{Matrix{Int64}, Vector{Matrix{Int64}}}: [1 2; 3 4] ⋅ ⋅ ⋅ ⋅ [1 2; 3 4] ⋅ ⋅ ⋅ ⋅ [1 2; 3 4] ⋅ ⋅ ⋅ ⋅ [1 2; 3 4] julia> diag(D,2) 2-element Vector{Matrix{Int64}}: [0 0; 0 0] [0 0; 0 0] ``` Performance in the common case seems unaffected (the variation is probably noise): ```julia julia> D = Diagonal(rand(1000)); julia> @btime diag($D, 2); 543.836 ns (3 allocations: 7.88 KiB) # nightly 429.551 ns (3 allocations: 7.88 KiB) # This PR ``` --------- Co-authored-by: Daniel Karrasch --- stdlib/LinearAlgebra/src/bidiag.jl | 6 +++++- stdlib/LinearAlgebra/src/diagonal.jl | 10 +++++++--- stdlib/LinearAlgebra/src/tridiag.jl | 12 ++++++++++-- stdlib/LinearAlgebra/test/bidiag.jl | 3 +++ stdlib/LinearAlgebra/test/diagonal.jl | 3 +++ 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index a997347eabd58..d86bad7e41435 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -409,7 +409,11 @@ function diag(M::Bidiagonal{T}, n::Integer=0) where T elseif (n == 1 && M.uplo == 'U') || (n == -1 && M.uplo == 'L') return copyto!(similar(M.ev, length(M.ev)), M.ev) elseif -size(M,1) <= n <= size(M,1) - return fill!(similar(M.dv, size(M,1)-abs(n)), zero(T)) + v = similar(M.dv, size(M,1)-abs(n)) + for i in eachindex(v) + v[i] = M[BandIndex(n,i)] + end + return v else 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"))) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 92f399bb774ff..526ec20ddafa1 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -753,10 +753,14 @@ function diag(D::Diagonal{T}, k::Integer=0) where T if k == 0 return copyto!(similar(D.diag, length(D.diag)), D.diag) elseif -size(D,1) <= k <= size(D,1) - return fill!(similar(D.diag, size(D,1)-abs(k)), zero(T)) + v = similar(D.diag, size(D,1)-abs(k)) + for i in eachindex(v) + v[i] = D[BandIndex(k, i)] + end + return v else - throw(ArgumentError(string("requested diagonal, $k, must be at least $(-size(D, 1)) ", - "and at most $(size(D, 2)) for an $(size(D, 1))-by-$(size(D, 2)) matrix"))) + throw(ArgumentError(LazyString(lazy"requested diagonal, $k, must be at least $(-size(D, 1)) ", + lazy"and at most $(size(D, 2)) for an $(size(D, 1))-by-$(size(D, 2)) matrix"))) end end tr(D::Diagonal) = sum(tr, D.diag) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index c2806c21c00ff..3f8eb5da9fc9d 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -198,7 +198,11 @@ function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number elseif absn == 1 return copyto!(similar(M.ev, length(M.dv)-1), _evview(M)) elseif absn <= size(M,1) - return fill!(similar(M.dv, size(M,1)-absn), zero(T)) + v = similar(M.dv, size(M,1)-absn) + for i in eachindex(v) + v[i] = M[BandIndex(n,i)] + end + return v else throw_diag_outofboundserror(n, size(M)) end @@ -660,7 +664,11 @@ function diag(M::Tridiagonal{T}, n::Integer=0) where T elseif n == 1 return copyto!(similar(M.du, length(M.du)), M.du) elseif abs(n) <= size(M,1) - return fill!(similar(M.d, size(M,1)-abs(n)), zero(T)) + v = similar(M.d, size(M,1)-abs(n)) + for i in eachindex(v) + v[i] = M[BandIndex(n,i)] + end + return v else 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"))) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 387657ba12d04..ef50658a642fb 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -829,6 +829,9 @@ end end end + @test diag(BU, -1) == [zeros(size(dv[i+1], 1), size(dv[i],2)) for i in 1:length(dv)-1] + @test diag(BL, 1) == [zeros(size(dv[i], 1), size(dv[i+1],2)) for i in 1:length(dv)-1] + M = ones(2,2) for n in 0:1 dv = fill(M, n) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 866c11b9931cd..83d5e4fcdf170 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -781,6 +781,9 @@ end @test transpose(Dherm) == Diagonal([[1 1-im; 1+im 1], [1 1-im; 1+im 1]]) @test adjoint(Dsym) == Diagonal([[1 1-im; 1-im 1], [1 1-im; 1-im 1]]) @test transpose(Dsym) == Dsym + @test diag(D, 0) == diag(D) == [[1 2; 3 4], [1 2; 3 4]] + @test diag(D, 1) == diag(D, -1) == [zeros(Int,2,2)] + @test diag(D, 2) == diag(D, -2) == [] v = [[1, 2], [3, 4]] @test Dherm' * v == Dherm * v From b5af119a6c608de43d6591a6c4129e9369239898 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 29 Aug 2024 20:43:47 +0530 Subject: [PATCH 133/548] Fix algebra for block SymTridiagonal matrices (#55383) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, algebra between a `SymTridiagonal` and another structured matrix uses the bands of the `SymTridiagonal` directly to compute the result. However, this may lead to incorrect results for block matrices, where the elements are symmetrized or transposed. This PR resolves such issues by introducing some internal functions that apply the transforms before the addition/subtraction or equality check is carried out. Fixes issues like ```julia julia> using LinearAlgebra, StaticArrays julia> m = SMatrix{2,2}(1:4); julia> S = SymTridiagonal(fill(m,4), fill(m,3)) 4×4 SymTridiagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] julia> D = Diagonal(fill(m,4)) 4×4 Diagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [1 3; 2 4] ⋅ ⋅ ⋅ ⋅ [1 3; 2 4] ⋅ ⋅ ⋅ ⋅ [1 3; 2 4] ⋅ ⋅ ⋅ ⋅ [1 3; 2 4] julia> S + D 4×4 SymTridiagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [2 6; 6 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 6 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 6 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 6 8] julia> S[1] + D[1] 2×2 SMatrix{2, 2, Int64, 4} with indices SOneTo(2)×SOneTo(2): 2 6 5 8 julia> S[1] + D[1] == (S + D)[1] false ``` With this PR, ```julia julia> S + D 4×4 Tridiagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [2 6; 5 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 5 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 5 8] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [2 6; 5 8] julia> S[1] + D[1] == (S + D)[1] true ``` This would also make https://github.com/JuliaLang/julia/pull/50423 simpler. --- stdlib/LinearAlgebra/src/special.jl | 77 +++++++++++++++++++--------- stdlib/LinearAlgebra/test/special.jl | 60 ++++++++++++++++++++++ test/testhelpers/SizedArrays.jl | 8 ++- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 28a1b4b1b2eab..5fea1e32460ff 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -21,16 +21,19 @@ function Tridiagonal(A::Bidiagonal) Tridiagonal(A.uplo == 'U' ? z : A.ev, A.dv, A.uplo == 'U' ? A.ev : z) end +_diagview(S::SymTridiagonal{<:Number}) = S.dv +_diagview(S::SymTridiagonal) = view(S, diagind(S, IndexStyle(S))) + # conversions from SymTridiagonal to other special matrix types -Diagonal(A::SymTridiagonal) = Diagonal(A.dv) +Diagonal(A::SymTridiagonal) = Diagonal(_diagview(A)) # These can fail when ev has the same length as dv # TODO: Revisit when a good solution for #42477 is found -Bidiagonal(A::SymTridiagonal) = +Bidiagonal(A::SymTridiagonal{<:Number}) = iszero(A.ev) ? Bidiagonal(A.dv, A.ev, :U) : throw(ArgumentError("matrix cannot be represented as Bidiagonal")) -Tridiagonal(A::SymTridiagonal) = - Tridiagonal(copy(A.ev), A.dv, A.ev) +Tridiagonal(A::SymTridiagonal{<:Number}) = + Tridiagonal(A.ev, A.dv, A.ev) # conversions from Tridiagonal to other special matrix types Diagonal(A::Tridiagonal) = Diagonal(A.d) @@ -163,26 +166,45 @@ function (-)(A::Diagonal, B::Bidiagonal) Bidiagonal(newdv, typeof(newdv)(-B.ev), B.uplo) end +# Return a SymTridiagonal if the elements of `newdv` are +# statically known to be symmetric. Return a Tridiagonal otherwise +function _symtri_or_tri(dl, d, du) + new_du = oftype(d, du) + new_dl = oftype(d, dl) + if symmetric_type(eltype(d)) == eltype(d) + SymTridiagonal(d, new_du) + else + Tridiagonal(new_dl, d, new_du) + end +end + @commutative function (+)(A::Diagonal, B::SymTridiagonal) - newdv = A.diag + B.dv - SymTridiagonal(A.diag + B.dv, typeof(newdv)(B.ev)) + newdv = A.diag + _diagview(B) + _symtri_or_tri(_evview_transposed(B), newdv, _evview(B)) end function (-)(A::Diagonal, B::SymTridiagonal) - newdv = A.diag - B.dv - SymTridiagonal(newdv, typeof(newdv)(-B.ev)) + newdv = A.diag - _diagview(B) + _symtri_or_tri(-_evview_transposed(B), newdv, -_evview(B)) end function (-)(A::SymTridiagonal, B::Diagonal) - newdv = A.dv - B.diag - SymTridiagonal(newdv, typeof(newdv)(A.ev)) + newdv = _diagview(A) - B.diag + _symtri_or_tri(_evview_transposed(A), newdv, _evview(A)) end # this set doesn't have the aforementioned problem - -@commutative (+)(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl+_evview(B), A.d+B.dv, A.du+_evview(B)) --(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl-_evview(B), A.d-B.dv, A.du-_evview(B)) --(A::SymTridiagonal, B::Tridiagonal) = Tridiagonal(_evview(A)-B.dl, A.dv-B.d, _evview(A)-B.du) +_evview_transposed(S::SymTridiagonal{<:Number}) = _evview(S) +_evview_transposed(S::SymTridiagonal) = transpose.(_evview(S)) +@commutative function (+)(A::Tridiagonal, B::SymTridiagonal) + Tridiagonal(A.dl+_evview_transposed(B), A.d+_diagview(B), A.du+_evview(B)) +end +function -(A::Tridiagonal, B::SymTridiagonal) + Tridiagonal(A.dl-_evview_transposed(B), A.d-_diagview(B), A.du-_evview(B)) +end +function -(A::SymTridiagonal, B::Tridiagonal) + Tridiagonal(_evview_transposed(A)-B.dl, _diagview(A)-B.d, _evview(A)-B.du) +end @commutative function (+)(A::Diagonal, B::Tridiagonal) newdv = A.diag + B.d @@ -215,18 +237,18 @@ function (-)(A::Tridiagonal, B::Bidiagonal) end @commutative function (+)(A::Bidiagonal, B::SymTridiagonal) - newdv = A.dv + B.dv - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(_evview(B)), A.dv+B.dv, A.ev+_evview(B)) : (A.ev+_evview(B), A.dv+B.dv, typeof(newdv)(_evview(B))))...) + newdv = A.dv + _diagview(B) + Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(_evview_transposed(B)), newdv, A.ev+_evview(B)) : (A.ev+_evview_transposed(B), newdv, typeof(newdv)(_evview(B))))...) end function (-)(A::Bidiagonal, B::SymTridiagonal) - newdv = A.dv - B.dv - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-_evview(B)), newdv, A.ev-_evview(B)) : (A.ev-_evview(B), newdv, typeof(newdv)(-_evview(B))))...) + newdv = A.dv - _diagview(B) + Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-_evview_transposed(B)), newdv, A.ev-_evview(B)) : (A.ev-_evview_transposed(B), newdv, typeof(newdv)(-_evview(B))))...) end function (-)(A::SymTridiagonal, B::Bidiagonal) - newdv = A.dv - B.dv - Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(_evview(A)), newdv, _evview(A)-B.ev) : (_evview(A)-B.ev, newdv, typeof(newdv)(_evview(A))))...) + newdv = _diagview(A) - B.dv + Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(_evview_transposed(A)), newdv, _evview(A)-B.ev) : (_evview_transposed(A)-B.ev, newdv, typeof(newdv)(_evview(A))))...) end @commutative function (+)(A::Tridiagonal, B::UniformScaling) @@ -256,7 +278,7 @@ function (-)(A::UniformScaling, B::Tridiagonal) end function (-)(A::UniformScaling, B::SymTridiagonal) dv = Ref(A) .- B.dv - SymTridiagonal(dv, convert(typeof(dv), -B.ev)) + SymTridiagonal(dv, convert(typeof(dv), -_evview(B))) end function (-)(A::UniformScaling, B::Bidiagonal) dv = Ref(A) .- B.dv @@ -286,7 +308,14 @@ _small_enough(A::Union{Diagonal, Bidiagonal}) = size(A, 1) <= 1 _small_enough(A::Tridiagonal) = size(A, 1) <= 2 _small_enough(A::SymTridiagonal) = size(A, 1) <= 2 -function fill!(A::Union{Diagonal,Bidiagonal,Tridiagonal,SymTridiagonal}, x) +function fill!(A::Union{Diagonal,Bidiagonal,Tridiagonal}, x) + xT = convert(eltype(A), x) + (iszero(xT) || _small_enough(A)) && return fillstored!(A, xT) + throw(ArgumentError(lazy"array of type $(typeof(A)) and size $(size(A)) can + not be filled with $x, since some of its entries are constrained.")) +end +function fill!(A::SymTridiagonal, x) + issymmetric(x) || throw(ArgumentError("cannot fill a SymTridiagonal with an asymmetric value")) xT = convert(eltype(A), x) (iszero(xT) || _small_enough(A)) && return fillstored!(A, xT) throw(ArgumentError(lazy"array of type $(typeof(A)) and size $(size(A)) can @@ -399,7 +428,7 @@ end # SymTridiagonal == Tridiagonal is already defined in tridiag.jl ==(A::Diagonal, B::Bidiagonal) = iszero(B.ev) && A.diag == B.dv -==(A::Diagonal, B::SymTridiagonal) = iszero(_evview(B)) && A.diag == B.dv +==(A::Diagonal, B::SymTridiagonal) = iszero(_evview(B)) && A.diag == _diagview(B) ==(B::Bidiagonal, A::Diagonal) = A == B ==(A::Diagonal, B::Tridiagonal) = iszero(B.dl) && iszero(B.du) && A.diag == B.d ==(B::Tridiagonal, A::Diagonal) = A == B @@ -413,7 +442,7 @@ function ==(A::Bidiagonal, B::Tridiagonal) end ==(B::Tridiagonal, A::Bidiagonal) = A == B -==(A::Bidiagonal, B::SymTridiagonal) = iszero(_evview(B)) && iszero(A.ev) && A.dv == B.dv +==(A::Bidiagonal, B::SymTridiagonal) = iszero(_evview(B)) && iszero(A.ev) && A.dv == _diagview(B) ==(B::SymTridiagonal, A::Bidiagonal) = A == B # TODO: remove these deprecations (used by SparseArrays in the past) diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index 9bb84ba0e9d03..8d3733e6b1289 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -5,6 +5,10 @@ module TestSpecial using Test, LinearAlgebra, Random using LinearAlgebra: rmul!, BandIndex +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) +using .Main.SizedArrays + n= 10 #Size of matrix to test Random.seed!(1) @@ -786,4 +790,60 @@ end end end +@testset "block SymTridiagonal" begin + m = SizedArrays.SizedArray{(2,2)}(reshape([1:4;;],2,2)) + S = SymTridiagonal(fill(m,4), fill(m,3)) + SA = Array(S) + D = Diagonal(fill(m,4)) + DA = Array(D) + BU = Bidiagonal(fill(m,4), fill(m,3), :U) + BUA = Array(BU) + BL = Bidiagonal(fill(m,4), fill(m,3), :L) + BLA = Array(BL) + T = Tridiagonal(fill(m,3), fill(m,4), fill(m,3)) + TA = Array(T) + IA = Array(Diagonal(fill(one(m), 4))) + @test S + D == D + S == SA + DA + @test S - D == -(D - S) == SA - DA + @test S + BU == SA + BUA + @test S - BU == -(BU - S) == SA - BUA + @test S + BL == SA + BLA + @test S - BL == -(BL - S) == SA - BLA + @test S + T == SA + TA + @test S - T == -(T - S) == SA - TA + @test S + S == SA + SA + @test S - S == -(S - S) == SA - SA + @test S + I == I + S == SA + IA + @test S - I == -(I - S) == SA - IA + + @test S == S + @test S != D + @test S != BL + @test S != BU + @test S != T + + @test_throws ArgumentError fill!(S, m) + S_small = SymTridiagonal(fill(m,2), fill(m,1)) + @test_throws "cannot fill a SymTridiagonal with an asymmetric value" fill!(S, m) + fill!(S_small, Symmetric(m)) + @test all(==(Symmetric(m)), S_small) + + @testset "diag" begin + m = SizedArrays.SizedArray{(2,2)}([1 3; 3 4]) + D = Diagonal(fill(m,4)) + z = fill(zero(m),3) + d = fill(m,4) + BU = Bidiagonal(d, z, :U) + BL = Bidiagonal(d, z, :L) + T = Tridiagonal(z, d, z) + for ev in (fill(zero(m),3), fill(zero(m),4)) + SD = SymTridiagonal(fill(m,4), ev) + @test SD == D == SD + @test SD == BU == SD + @test SD == BL == SD + @test SD == T == SD + end + end +end + end # module TestSpecial diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index a435ca7591cac..495fe03347ee7 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -36,10 +36,16 @@ struct SizedArray{SZ,T,N,A<:AbstractArray} <: AbstractArray{T,N} SZ == size(data) || throw(ArgumentError("size mismatch!")) new{SZ,T,N,A}(A(data)) end + function SizedArray{SZ,T,N}(data::A) where {SZ,T,N,A<:AbstractArray{T,N}} + SizedArray{SZ,T,N,A}(data) + end + function SizedArray{SZ,T}(data::A) where {SZ,T,N,A<:AbstractArray{T,N}} + SizedArray{SZ,T,N,A}(data) + end end SizedMatrix{SZ,T,A<:AbstractArray} = SizedArray{SZ,T,2,A} SizedVector{SZ,T,A<:AbstractArray} = SizedArray{SZ,T,1,A} -Base.convert(::Type{SizedArray{SZ,T,N,A}}, data::AbstractArray) where {SZ,T,N,A} = SizedArray{SZ,T,N,A}(data) +Base.convert(::Type{S}, data::AbstractArray) where {S<:SizedArray} = data isa S ? data : S(data) # Minimal AbstractArray interface Base.size(a::SizedArray) = size(typeof(a)) From 7ea8a9a3a69faae0202ab54c0fde3b111bd48b97 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 29 Aug 2024 13:21:22 -0400 Subject: [PATCH 134/548] Refactor `Binding` data structures in preparation for partition (#54788) This is a re-worked extraction of #54654, adjusted to support the new semantics arrived at over the course of that thread. Note that this is the data-structure change only. The semantics in this PR still match current master to the greatest extent possible. The core idea here is to split `Binding` in two: A new `Binding` with minimal data and a `BindingPartition` that holds all data that is world-age partitioned. In the present PR, these are always in 1:1 correspondednce, but after #54654, there will be multiple `BindingPartition`s for every `Binding`. Essentially the `owner` and `ty` fields have been merged into a new `restriction` field of `BindingPartition`, which may also hold the value of a constant (consistent with the final semantics reached in #54654). The non-partitioned binding->value field is now used exclusively for non-constant globals. The disambiguation for how to interpret the `restriction` field happens via flags. `->imported` grew to 2 bits and can now be one of `NONE`/`IMPLICIT`/ `EXPLICIT`/`GUARD`. `GUARD` corresponds to the old `b->owner == NULL` case. `NONE` corresponds to the old `b->owner == b` case, while IMPLICIT/EXPLICIT correspond to `b->owner != b` and the old `imported` flag. Other than that, the behavior of the flags is unchanged. Additionally, fields are provided for `min_world`/`max_world`/`next`, but these are unused in this PR and prepratory only. --- base/client.jl | 2 +- base/reflection.jl | 21 ++ doc/src/manual/variables-and-scoping.md | 6 +- src/ast.c | 13 +- src/builtins.c | 18 +- src/cgutils.cpp | 17 - src/codegen.cpp | 210 ++++++----- src/dlload.c | 2 +- src/gc-heap-snapshot.cpp | 7 + src/gc-heap-snapshot.h | 9 + src/gc-interface.h | 8 + src/gc-stock.c | 10 + src/gf.c | 56 ++- src/interpreter.c | 4 +- src/jl_exported_data.inc | 1 + src/jl_exported_funcs.inc | 3 +- src/jltypes.c | 22 +- src/julia.h | 92 ++++- src/julia_internal.h | 88 ++++- src/method.c | 45 +-- src/module.c | 479 +++++++++++++++--------- src/rtutils.c | 12 +- src/staticdata.c | 121 ++++-- src/support/dtypes.h | 7 + src/toplevel.c | 196 ++++++---- stdlib/REPL/src/REPL.jl | 40 +- stdlib/REPL/src/precompile.jl | 1 + test/core.jl | 2 +- test/precompile.jl | 2 +- test/syntax.jl | 79 +++- 30 files changed, 1031 insertions(+), 542 deletions(-) diff --git a/base/client.jl b/base/client.jl index 3aebfaf6de696..0290d27b09cf0 100644 --- a/base/client.jl +++ b/base/client.jl @@ -417,7 +417,7 @@ function load_REPL() return nothing end -global active_repl +global active_repl::Any global active_repl_backend = nothing function run_fallback_repl(interactive::Bool) diff --git a/base/reflection.jl b/base/reflection.jl index 4b491ca9f6bd4..2ddd34b0f73c1 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -207,6 +207,27 @@ function _fieldnames(@nospecialize t) return t.name.names end +const BINDING_KIND_GLOBAL = 0x0 +const BINDING_KIND_CONST = 0x1 +const BINDING_KIND_CONST_IMPORT = 0x2 +const BINDING_KIND_IMPLICIT = 0x3 +const BINDING_KIND_EXPLICIT = 0x4 +const BINDING_KIND_IMPORTED = 0x5 +const BINDING_KIND_FAILED = 0x6 +const BINDING_KIND_DECLARED = 0x7 +const BINDING_KIND_GUARD = 0x8 + +function lookup_binding_partition(world::UInt, b::Core.Binding) + ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) +end + +function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) + ccall(:jl_get_globalref_partition, Ref{Core.BindingPartition}, (Any, UInt), gr, world) +end + +binding_kind(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_kind, UInt8, (Any,), bpart) +binding_kind(m::Module, s::Symbol) = binding_kind(lookup_binding_partition(tls_world_age(), GlobalRef(m, s))) + """ fieldname(x::DataType, i::Integer) diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index de97ff296e37e..64a12ea88c7dd 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -743,7 +743,7 @@ ERROR: invalid redefinition of constant x julia> const y = 1.0 1.0 -julia> y = 2.0 +julia> const y = 2.0 WARNING: redefinition of constant y. This may fail, cause incorrect answers, or produce other errors. 2.0 ``` @@ -761,7 +761,7 @@ julia> const a = [1] 1-element Vector{Int64}: 1 -julia> a = [1] +julia> const a = [1] WARNING: redefinition of constant a. This may fail, cause incorrect answers, or produce other errors. 1-element Vector{Int64}: 1 @@ -782,7 +782,7 @@ f (generic function with 1 method) julia> f() 1 -julia> x = 2 +julia> const x = 2 WARNING: redefinition of constant x. This may fail, cause incorrect answers, or produce other errors. 2 diff --git a/src/ast.c b/src/ast.c index 7c775bf25d486..26b95225fbf1c 100644 --- a/src/ast.c +++ b/src/ast.c @@ -175,7 +175,8 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); jl_sym_t *var = scmsym_to_julia(fl_ctx, args[0]); jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); - return (b != NULL && jl_atomic_load_relaxed(&b->owner) == b) ? fl_ctx->T : fl_ctx->F; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + return (bpart != NULL && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F; } static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) @@ -204,8 +205,14 @@ static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint var = scmsym_to_julia(fl_ctx, args[1]); } jl_binding_t *b = jl_get_module_binding(mod, var, 0); - b = b ? jl_atomic_load_relaxed(&b->owner) : NULL; - return b != NULL && jl_atomic_load_relaxed(&b->value) != NULL ? fl_ctx->T : fl_ctx->F; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (!bpart) + return fl_ctx->F; + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return fl_ctx->F; + return (jl_bkind_is_some_constant(decode_restriction_kind(pku)) ? + decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F; } static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT diff --git a/src/builtins.c b/src/builtins.c index 045a9914f5078..8019ee3c0e2c6 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1369,19 +1369,10 @@ JL_CALLABLE(jl_f_get_binding_type) jl_sym_t *var = (jl_sym_t*)args[1]; JL_TYPECHK(get_binding_type, module, (jl_value_t*)mod); JL_TYPECHK(get_binding_type, symbol, (jl_value_t*)var); - jl_value_t *ty = jl_get_binding_type(mod, var); - if (ty == (jl_value_t*)jl_nothing) { - jl_binding_t *b = jl_get_module_binding(mod, var, 0); - if (b == NULL) - return (jl_value_t*)jl_any_type; - jl_binding_t *b2 = jl_atomic_load_relaxed(&b->owner); - if (b2 != b) - return (jl_value_t*)jl_any_type; - jl_value_t *old_ty = NULL; - jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type); - return jl_atomic_load_relaxed(&b->ty); - } - return ty; + jl_value_t *ret = jl_get_binding_type(mod, var); + if (ret == jl_nothing) + return (jl_value_t*)jl_any_type; + return ret; } JL_CALLABLE(jl_f_swapglobal) @@ -2509,6 +2500,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("QuoteNode", (jl_value_t*)jl_quotenode_type); add_builtin("NewvarNode", (jl_value_t*)jl_newvarnode_type); add_builtin("Binding", (jl_value_t*)jl_binding_type); + add_builtin("BindingPartition", (jl_value_t*)jl_binding_partition_type); add_builtin("GlobalRef", (jl_value_t*)jl_globalref_type); add_builtin("NamedTuple", (jl_value_t*)jl_namedtuple_type); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index f911ef17eea38..2d2d2aed22069 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -568,23 +568,6 @@ static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p) return load; } -// Returns ctx.types().T_pjlvalue -static Value *literal_pointer_val(jl_codectx_t &ctx, jl_binding_t *p) -{ - // emit a pointer to any jl_value_t which will be valid across reloading code - if (p == NULL) - return Constant::getNullValue(ctx.types().T_pjlvalue); - // bindings are prefixed with jl_bnd# - jl_globalref_t *gr = p->globalref; - Value *pgv = gr ? julia_pgv(ctx, "jl_bnd#", gr->name, gr->mod, p) : julia_pgv(ctx, "jl_bnd#", p); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - auto load = ai.decorateInst(maybe_mark_load_dereferenceable( - ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, pgv, Align(sizeof(void*))), - false, sizeof(jl_binding_t), alignof(jl_binding_t))); - setName(ctx.emission_context, load, pgv->getName()); - return load; -} - // bitcast a value, but preserve its address space when dealing with pointer types static Value *emit_bitcast(jl_codectx_t &ctx, Value *v, Type *jl_value) { diff --git a/src/codegen.cpp b/src/codegen.cpp index e499d1193dee6..4091ec6c03db0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -907,14 +907,6 @@ static const auto jlcheckassignonce_func = new JuliaFunction<>{ {T_pjlvalue, T_pjlvalue, T_pjlvalue, PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, nullptr, }; -static const auto jldeclareconst_func = new JuliaFunction<>{ - XSTR(jl_declare_constant), - [](LLVMContext &C) { - auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); - return FunctionType::get(getVoidTy(C), - {T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); }, - nullptr, -}; static const auto jldeclareconstval_func = new JuliaFunction<>{ XSTR(jl_declare_constant_val), [](LLVMContext &C) { @@ -951,6 +943,16 @@ static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ }, nullptr, }; +static const auto jlgetbindingvalue_func = new JuliaFunction<>{ + XSTR(jl_reresolve_binding_value_seqcst), + [](LLVMContext &C) { + auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); + return FunctionType::get(T_prjlvalue, + {T_pjlvalue}, false); + }, + nullptr, +}; static const auto jlboundp_func = new JuliaFunction<>{ XSTR(jl_boundp), [](LLVMContext &C) { @@ -1010,13 +1012,12 @@ static const auto jlmethod_func = new JuliaFunction<>{ nullptr, }; static const auto jlgenericfunction_func = new JuliaFunction<>{ - XSTR(jl_generic_function_def), + XSTR(jl_declare_const_gf), [](LLVMContext &C) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); auto T_pjlvalue = PointerType::get(T_jlvalue, 0); auto T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); - auto T_pprjlvalue = PointerType::get(T_prjlvalue, 0); - return FunctionType::get(T_prjlvalue, {T_pjlvalue, T_pjlvalue, T_pprjlvalue, T_pjlvalue}, false); + return FunctionType::get(T_prjlvalue, {T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; @@ -2930,10 +2931,11 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) if (jl_is_globalref(ex)) { s = jl_globalref_name(ex); jl_binding_t *b = jl_get_binding(jl_globalref_mod(ex), s); - if (b && b->constp) { + jl_value_t *v = jl_get_binding_value_if_const(b); + if (v) { if (b->deprecated) cg_bdw(ctx, s, b); - return jl_atomic_load_relaxed(&b->value); + return v; } return NULL; } @@ -2952,10 +2954,11 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) s = (jl_sym_t*)static_eval(ctx, jl_exprarg(e, 2)); if (s && jl_is_symbol(s)) { jl_binding_t *b = jl_get_binding(m, s); - if (b && b->constp) { + jl_value_t *v = jl_get_binding_value_if_const(b); + if (v) { if (b->deprecated) cg_bdw(ctx, s, b); - return jl_atomic_load_relaxed(&b->value); + return v; } } } @@ -3192,18 +3195,53 @@ 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, false); - if (bp == NULL) - return jl_cgval_t(); - bp = julia_binding_pvalue(ctx, bp); - jl_value_t *ty = nullptr; - if (bnd) { - jl_value_t *v = jl_atomic_load_acquire(&bnd->value); // acquire value for ty - if (v != NULL && bnd->constp) - return mark_julia_const(ctx, v); - ty = jl_atomic_load_relaxed(&bnd->ty); + jl_binding_t *bnd = jl_get_module_binding(mod, name, 1); + jl_binding_partition_t *bpart = jl_get_binding_partition(bnd, ctx.max_world); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + // try to look this up now. + // TODO: This is bad and we'd like to delete it. + jl_get_binding(mod, name); + } + assert(bnd); + Value *bp = NULL; + // bpart was updated in place - this will change with full partition + pku = jl_atomic_load_acquire(&bpart->restriction); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + // Redo the lookup at runtime + bp = julia_binding_gv(ctx, bnd); + Value *v = ctx.builder.CreateCall(prepare_call(jlgetbindingvalue_func), { bp }); + undef_var_error_ifnot(ctx, ctx.builder.CreateIsNotNull(v), name, (jl_value_t*)mod); + return mark_julia_type(ctx, v, true, jl_any_type); + } else { + while (true) { + if (!bpart) + break; + if (!jl_bkind_is_some_import(decode_restriction_kind(pku))) + break; + if (bnd->deprecated) { + cg_bdw(ctx, name, bnd); + } + bnd = (jl_binding_t*)decode_restriction_value(pku); + bpart = jl_get_binding_partition(bnd, ctx.max_world); + pku = jl_atomic_load_acquire(&bpart->restriction); + } + if (bpart && jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_value_t *constval = decode_restriction_value(pku); + if (!constval) { + undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); + return jl_cgval_t(); + } + return mark_julia_const(ctx, constval); + } + } + bp = julia_binding_gv(ctx, bnd); + if (bnd->deprecated) { + cg_bdw(ctx, name, bnd); } + assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL); + jl_value_t *ty = decode_restriction_value(pku); + bp = julia_binding_pvalue(ctx, bp); if (ty == nullptr) ty = (jl_value_t*)jl_any_type; return update_julia_type(ctx, emit_checked_var(ctx, bp, name, (jl_value_t*)mod, false, ctx.tbaa().tbaa_binding), ty); @@ -3216,43 +3254,47 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s { jl_binding_t *bnd = NULL; Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc); + jl_binding_partition_t *bpart = jl_get_binding_partition(bnd, ctx.max_world); if (bp == NULL) return jl_cgval_t(); - if (bnd && !bnd->constp) { - jl_value_t *ty = jl_atomic_load_relaxed(&bnd->ty); - if (ty != nullptr) { - const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; - if (!ismodifyglobal) { - // TODO: use typeassert in jl_check_binding_wr too - emit_typecheck(ctx, rval, ty, "typeassert"); - rval = update_julia_type(ctx, rval, ty); - if (rval.typ == jl_bottom_type) - return jl_cgval_t(); - } - bool isboxed = true; - bool maybe_null = jl_atomic_load_relaxed(&bnd->value) == NULL; - return typed_store(ctx, - julia_binding_pvalue(ctx, bp), - rval, cmp, ty, - ctx.tbaa().tbaa_binding, - nullptr, - bp, - isboxed, - Order, - FailOrder, - 0, - nullptr, - issetglobal, - isreplaceglobal, - isswapglobal, - ismodifyglobal, - issetglobalonce, - maybe_null, - modifyop, - fname, - mod, - sym); + if (bpart) { + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_value_t *ty = decode_restriction_value(pku); + if (ty != nullptr) { + const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; + if (!ismodifyglobal) { + // TODO: use typeassert in jl_check_binding_wr too + emit_typecheck(ctx, rval, ty, "typeassert"); + rval = update_julia_type(ctx, rval, ty); + if (rval.typ == jl_bottom_type) + return jl_cgval_t(); + } + bool isboxed = true; + bool maybe_null = jl_atomic_load_relaxed(&bnd->value) == NULL; + return typed_store(ctx, + julia_binding_pvalue(ctx, bp), + rval, cmp, ty, + ctx.tbaa().tbaa_binding, + nullptr, + bp, + isboxed, + Order, + FailOrder, + 0, + nullptr, + issetglobal, + isreplaceglobal, + isswapglobal, + ismodifyglobal, + issetglobalonce, + maybe_null, + modifyop, + fname, + mod, + sym); + } } } Value *m = literal_pointer_val(ctx, (jl_value_t*)mod); @@ -5437,16 +5479,20 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t jl_binding_t **pbnd, bool assign, bool alloc) { jl_binding_t *b = jl_get_module_binding(m, s, 1); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, ctx.max_world); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (assign) { - if (jl_atomic_load_relaxed(&b->owner) == NULL) + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) // not yet declared b = NULL; } else { - b = jl_atomic_load_relaxed(&b->owner); - if (b == NULL) + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { // try to look this up now b = jl_get_binding(m, s); + bpart = jl_get_binding_partition(b, ctx.max_world); + } + pku = jl_walk_binding_inplace(&b, &bpart, ctx.max_world); } if (b == NULL) { // var not found. switch to delayed lookup. @@ -5487,7 +5533,7 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t return p; } if (assign) { - if (jl_atomic_load_relaxed(&b->owner) != b) { + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_guard(decode_restriction_kind(pku))) { // 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), @@ -5606,8 +5652,10 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym, int allow_i name = (jl_sym_t*)sym; } jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0); - if (bnd && jl_atomic_load_relaxed(&bnd->owner) == bnd) { - if (jl_atomic_load_acquire(&bnd->value) != NULL && bnd->constp) + jl_binding_partition_t *bpart = jl_get_binding_partition(bnd, ctx.min_world); + jl_ptr_kind_union_t pku = bpart ? jl_atomic_load_relaxed(&bpart->restriction) : encode_restriction(NULL, BINDING_KIND_GUARD); + if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (jl_get_binding_value_if_const(bnd)) return mark_julia_const(ctx, jl_true); Value *bp = julia_binding_gv(ctx, bnd); bp = julia_binding_pvalue(ctx, bp); @@ -6410,13 +6458,11 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ return ghostValue(ctx, jl_nothing_type); } bp = julia_binding_gv(ctx, bnd); - bp = julia_binding_pvalue(ctx, bp); - } - if (bp) { - Value *mdargs[] = { name, literal_pointer_val(ctx, (jl_value_t*)mod), bp, literal_pointer_val(ctx, bnd) }; jl_cgval_t gf = mark_julia_type( ctx, - ctx.builder.CreateCall(prepare_call(jlgenericfunction_func), ArrayRef(mdargs)), + ctx.builder.CreateCall(prepare_call(jlgenericfunction_func), { bp, + literal_pointer_val(ctx, (jl_value_t*)mod), name + }), true, jl_function_type); return gf; @@ -6449,17 +6495,14 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ sym = jl_globalref_name(sym); } if (jl_is_symbol(sym)) { - jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, true); - if (bp) { - if (nargs == 2) { - jl_cgval_t rhs = emit_expr(ctx, args[1]); - ctx.builder.CreateCall(prepare_call(jldeclareconstval_func), - { bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, rhs) }); - } else { - ctx.builder.CreateCall(prepare_call(jldeclareconst_func), - { bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym) }); - } + jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1); + if (nargs == 2) { + jl_cgval_t rhs = emit_expr(ctx, args[1]); + ctx.builder.CreateCall(prepare_call(jldeclareconstval_func), + { julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, rhs) }); + } else { + ctx.builder.CreateCall(prepare_call(jldeclareconstval_func), + { julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), ConstantPointerNull::get(cast(ctx.types().T_prjlvalue)) }); } } } @@ -10046,7 +10089,6 @@ static void init_jit_functions(void) add_named_global(memcmp_func, &memcmp); add_named_global(jltypeerror_func, &jl_type_error); add_named_global(jlcheckassign_func, &jl_checked_assignment); - add_named_global(jldeclareconst_func, &jl_declare_constant); add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error); add_named_global(jlgetbindingwrorerror_func, &jl_get_binding_wr); add_named_global(jlboundp_func, &jl_boundp); @@ -10060,7 +10102,7 @@ static void init_jit_functions(void) add_named_global(jlcopyast_func, &jl_copy_ast); //add_named_global(jlnsvec_func, &jl_svec); add_named_global(jlmethod_func, &jl_method_def); - add_named_global(jlgenericfunction_func, &jl_generic_function_def); + add_named_global(jlgenericfunction_func, &jl_declare_const_gf); add_named_global(jlenter_func, &jl_enter_handler); add_named_global(jl_current_exception_func, &jl_current_exception); add_named_global(jlleave_noexcept_func, &jl_pop_handler_noexcept); diff --git a/src/dlload.c b/src/dlload.c index 484c36a228886..91980cc4ecbbf 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -309,7 +309,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, */ if (!abspath && !is_atpath && jl_base_module != NULL) { jl_binding_t *b = jl_get_module_binding(jl_base_module, jl_symbol("DL_LOAD_PATH"), 0); - jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_atomic_load_relaxed(&b->value) : NULL); + jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_get_binding_value(b) : NULL); if (DL_LOAD_PATH != NULL) { size_t j; for (j = 0; j < jl_array_nrows(DL_LOAD_PATH); j++) { diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index 4fcc66495fc45..fcda11dad4f8a 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -561,6 +561,13 @@ void _gc_heap_snapshot_record_internal_array_edge(jl_value_t *from, jl_value_t * g_snapshot->names.serialize_if_necessary(g_snapshot->strings, "")); } +void _gc_heap_snapshot_record_binding_partition_edge(jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT +{ + _record_gc_edge("binding", from, to, + g_snapshot->names.serialize_if_necessary(g_snapshot->strings, "")); +} + + void _gc_heap_snapshot_record_hidden_edge(jl_value_t *from, void* to, size_t bytes, uint16_t alloc_type) JL_NOTSAFEPOINT { // valid alloc_type values are 0, 1, 2 diff --git a/src/gc-heap-snapshot.h b/src/gc-heap-snapshot.h index e7fbb36249ec1..dc5b22bb72eb1 100644 --- a/src/gc-heap-snapshot.h +++ b/src/gc-heap-snapshot.h @@ -32,6 +32,8 @@ void _gc_heap_snapshot_record_hidden_edge(jl_value_t *from, void* to, size_t byt void _gc_heap_snapshot_record_gc_roots(jl_value_t *root, char *name) JL_NOTSAFEPOINT; // Used for objects that are reachable from the finalizer list void _gc_heap_snapshot_record_finlist(jl_value_t *finlist, size_t index) JL_NOTSAFEPOINT; +// Used for objects reachable from the binding partition pointer union +void _gc_heap_snapshot_record_binding_partition_edge(jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT; extern int gc_heap_snapshot_enabled; extern int prev_sweep_full; @@ -97,6 +99,13 @@ static inline void gc_heap_snapshot_record_internal_array_edge(jl_value_t *from, } } +static inline void gc_heap_snapshot_record_binding_partition_edge(jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT +{ + if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full)) { + _gc_heap_snapshot_record_binding_partition_edge(from, to); + } +} + static inline void gc_heap_snapshot_record_hidden_edge(jl_value_t *from, void* to, size_t bytes, uint16_t alloc_type) JL_NOTSAFEPOINT { if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full)) { diff --git a/src/gc-interface.h b/src/gc-interface.h index 201c5f6e1741e..e543b4b5879f1 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -235,6 +235,14 @@ STATIC_INLINE void jl_gc_wb_back(const void *ptr) JL_NOTSAFEPOINT; // in different GC generations (i.e. if the first argument points to an old object and the // second argument points to a young object), and if so, call the write barrier slow-path. STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT; +// Freshly allocated objects are known to be in the young generation until the next safepoint, +// so write barriers can be omitted until the next allocation. This function is a no-op that +// can be used to annotate that a write barrier would be required were it not for this property +// (as opposed to somebody just having forgotten to think about write barriers). +STATIC_INLINE void jl_gc_wb_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} +// Used to annotate that a write barrier would be required, but may be omitted because `ptr` +// is known to be an old object. +STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOTSAFEPOINT {} // Write-barrier function that must be used after copying multiple fields of an object into // another. It should be semantically equivalent to triggering multiple write barriers – one // per field of the object being copied, but may be special-cased for performance reasons. diff --git a/src/gc-stock.c b/src/gc-stock.c index 3ae14f378a2e7..d25f8917f302d 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2307,6 +2307,16 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (npointers == 0) return; uintptr_t nptr = (npointers << 2 | (bits & GC_OLD)); + if (vt == jl_binding_partition_type) { + // BindingPartition has a special union of jl_value_t and flag bits + // but is otherwise regular. + jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_valueof(o); + jl_value_t *val = decode_restriction_value( + jl_atomic_load_relaxed(&bpart->restriction)); + if (val) + gc_heap_snapshot_record_binding_partition_edge((jl_value_t*)bpart, val); + gc_try_claim_and_push(mq, val, &nptr); + } assert((layout->nfields > 0 || layout->flags.fielddesc_type == 3) && "opaque types should have been handled specially"); if (layout->flags.fielddesc_type == 0) { diff --git a/src/gf.c b/src/gf.c index 5ae7644c01363..95bab0d0f832e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -700,40 +700,38 @@ int foreach_mtable_in_module( if ((void*)b == jl_nothing) break; jl_sym_t *name = b->globalref->name; - if (jl_atomic_load_relaxed(&b->owner) == b && b->constp) { - jl_value_t *v = jl_atomic_load_relaxed(&b->value); - if (v) { - jl_value_t *uw = jl_unwrap_unionall(v); - if (jl_is_datatype(uw)) { - jl_typename_t *tn = ((jl_datatype_t*)uw)->name; - if (tn->module == m && tn->name == name && tn->wrapper == v) { - // this is the original/primary binding for the type (name/wrapper) - jl_methtable_t *mt = tn->mt; - if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) { - assert(mt->module == m); - if (!visit(mt, env)) - return 0; - } - } - } - else if (jl_is_module(v)) { - jl_module_t *child = (jl_module_t*)v; - if (child != m && child->parent == m && child->name == name) { - // this is the original/primary binding for the submodule - if (!foreach_mtable_in_module(child, visit, env)) - return 0; - } - } - else if (jl_is_mtable(v)) { - jl_methtable_t *mt = (jl_methtable_t*)v; - if (mt->module == m && mt->name == name) { - // this is probably an external method table here, so let's - // assume so as there is no way to precisely distinguish them + jl_value_t *v = jl_get_binding_value_if_const(b); + if (v) { + jl_value_t *uw = jl_unwrap_unionall(v); + if (jl_is_datatype(uw)) { + jl_typename_t *tn = ((jl_datatype_t*)uw)->name; + if (tn->module == m && tn->name == name && tn->wrapper == v) { + // this is the original/primary binding for the type (name/wrapper) + jl_methtable_t *mt = tn->mt; + if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) { + assert(mt->module == m); if (!visit(mt, env)) return 0; } } } + else if (jl_is_module(v)) { + jl_module_t *child = (jl_module_t*)v; + if (child != m && child->parent == m && child->name == name) { + // this is the original/primary binding for the submodule + if (!foreach_mtable_in_module(child, visit, env)) + return 0; + } + } + else if (jl_is_mtable(v)) { + jl_methtable_t *mt = (jl_methtable_t*)v; + if (mt->module == m && mt->name == name) { + // this is probably an external method table here, so let's + // assume so as there is no way to precisely distinguish them + if (!visit(mt, env)) + return 0; + } + } } table = jl_atomic_load_relaxed(&m->bindings); } diff --git a/src/interpreter.c b/src/interpreter.c index 5b96c485aac0d..f9d981687c631 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -94,9 +94,7 @@ static jl_value_t *eval_methoddef(jl_expr_t *ex, interpreter_state *s) jl_error("method: invalid declaration"); } jl_binding_t *b = jl_get_binding_for_method_def(modu, fname); - _Atomic(jl_value_t*) *bp = &b->value; - jl_value_t *gf = jl_generic_function_def(fname, modu, bp, b); - return gf; + return jl_declare_const_gf(b, modu, fname); } jl_value_t *atypes = NULL, *meth = NULL, *fname = NULL; diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index ff79966b2b01b..8711c14514145 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -51,6 +51,7 @@ XX(jl_floatingpoint_type) \ XX(jl_function_type) \ XX(jl_binding_type) \ + XX(jl_binding_partition_type) \ XX(jl_globalref_type) \ XX(jl_gotoifnot_type) \ XX(jl_enternode_type) \ diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 1976dbe709733..11cc8ee6fddd9 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -97,7 +97,6 @@ XX(jl_cstr_to_string) \ XX(jl_current_exception) \ XX(jl_debug_method_invalidation) \ - XX(jl_declare_constant) \ XX(jl_defines_or_exports_p) \ XX(jl_deprecate_binding) \ XX(jl_dlclose) \ @@ -185,7 +184,7 @@ XX(jl_gc_total_hrtime) \ XX(jl_gdblookup) \ XX(jl_generating_output) \ - XX(jl_generic_function_def) \ + XX(jl_declare_const_gf) \ XX(jl_gensym) \ XX(jl_getaffinity) \ XX(jl_getallocationgranularity) \ diff --git a/src/jltypes.c b/src/jltypes.c index a587552aaa011..adf39162cc7f0 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3148,12 +3148,21 @@ void jl_init_types(void) JL_GC_DISABLED assert(jl_module_type->instance == NULL); jl_compute_field_offsets(jl_module_type); + jl_binding_partition_type = + jl_new_datatype(jl_symbol("BindingPartition"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(5, "restriction", "min_world", "max_world", "next", "reserved"), + jl_svec(5, jl_uint64_type /* Special GC-supported union of Any and flags*/, + jl_ulong_type, jl_ulong_type, jl_any_type/*jl_binding_partition_type*/, jl_ulong_type), + jl_emptysvec, 0, 1, 0); + const static uint32_t binding_partition_atomicfields[] = { 0b01101 }; // Set fields 1, 3, 4 as atomic + jl_binding_partition_type->name->atomicfields = binding_partition_atomicfields; + jl_binding_type = jl_new_datatype(jl_symbol("Binding"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(5, "value", "globalref", "owner", "ty", "flags"), - jl_svec(5, jl_any_type, jl_any_type/*jl_globalref_type*/, jl_any_type/*jl_binding_type*/, jl_type_type, jl_uint8_type), + jl_perm_symsvec(4, "globalref", "value", "partitions", "flags"), + jl_svec(4, jl_any_type/*jl_globalref_type*/, jl_any_type, jl_binding_partition_type, jl_uint8_type), jl_emptysvec, 0, 1, 0); - const static uint32_t binding_atomicfields[] = { 0x0015 }; // Set fields 1, 3, 4 as atomic + const static uint32_t binding_atomicfields[] = { 0x0005 }; // Set fields 1, 3 as atomic jl_binding_type->name->atomicfields = binding_atomicfields; const static uint32_t binding_constfields[] = { 0x0002 }; // Set fields 2 as constant jl_binding_type->name->constfields = binding_constfields; @@ -3707,8 +3716,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_method_instance_type->types, 4, jl_code_instance_type); jl_svecset(jl_code_instance_type->types, 15, jl_voidpointer_type); jl_svecset(jl_code_instance_type->types, 16, jl_voidpointer_type); - jl_svecset(jl_binding_type->types, 1, jl_globalref_type); - jl_svecset(jl_binding_type->types, 2, jl_binding_type); + jl_svecset(jl_binding_type->types, 0, jl_globalref_type); + jl_svecset(jl_binding_partition_type->types, 3, jl_binding_partition_type); jl_compute_field_offsets(jl_datatype_type); jl_compute_field_offsets(jl_typename_type); @@ -3720,6 +3729,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_compute_field_offsets(jl_unionall_type); jl_compute_field_offsets(jl_simplevector_type); jl_compute_field_offsets(jl_symbol_type); + jl_compute_field_offsets(jl_binding_partition_type); // override ismutationfree for builtin types that are mutable for identity jl_string_type->ismutationfree = jl_string_type->isidentityfree = 1; @@ -3811,7 +3821,7 @@ void post_boot_hooks(void) for (size_t i = 0; i < jl_svec_len(bindings); i++) { if (table[i] != jl_nothing) { jl_binding_t *b = (jl_binding_t*)table[i]; - jl_value_t *v = jl_atomic_load_relaxed(&b->value); + jl_value_t *v = jl_get_binding_value(b); if (v) { if (jl_is_unionall(v)) v = jl_unwrap_unionall(v); diff --git a/src/julia.h b/src/julia.h index 074c50fd0aa21..caa938ffeb0d6 100644 --- a/src/julia.h +++ b/src/julia.h @@ -611,19 +611,84 @@ typedef struct _jl_weakref_t { jl_value_t *value; } jl_weakref_t; +enum jl_partition_kind { + // Constant: This binding partition is a constant declared using `const` + // ->restriction holds the constant value + BINDING_KIND_CONST = 0x0, + // Import Constant: This binding partition is a constant declared using `import A` + // ->restriction holds the constant value + BINDING_KIND_CONST_IMPORT = 0x1, + // Global: This binding partition is a global variable. + // -> restriction holds the type restriction + BINDING_KIND_GLOBAL = 0x2, + // Implicit: The binding was implicitly imported from a `using`'d module. + // ->restriction holds the imported binding + BINDING_KIND_IMPLICIT = 0x3, + // Explicit: The binding was explicitly `using`'d by name + // ->restriction holds the imported binding + BINDING_KIND_EXPLICIT = 0x4, + // Imported: The binding was explicitly `import`'d by name + // ->restriction holds the imported binding + BINDING_KIND_IMPORTED = 0x5, + // Failed: We attempted to import the binding, but the import was ambiguous + // ->restriction is NULL. + BINDING_KIND_FAILED = 0x6, + // Declared: The binding was declared using `global` or similar + // ->restriction is NULL. + BINDING_KIND_DECLARED = 0x7, + // Guard: The binding was looked at, but no global or import was resolved at the time + // ->restriction is NULL. + BINDING_KIND_GUARD = 0x8 +}; + +#ifdef _P64 +// Union of a ptr and a 3 bit field. +typedef uintptr_t jl_ptr_kind_union_t; +#else +typedef struct __attribute__((aligned(8))) { jl_value_t *val; size_t kind; } jl_ptr_kind_union_t; +#endif +typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { + JL_DATA_TYPE + /* union { + * // For ->kind == BINDING_KIND_GLOBAL + * jl_value_t *type_restriction; + * // For ->kind == BINDING_KIND_CONST(_IMPORT) + * jl_value_t *constval; + * // For ->kind in (BINDING_KIND_IMPLICIT, BINDING_KIND_EXPLICIT, BINDING_KIND_IMPORT) + * jl_binding_t *imported; + * } restriction; + * + * Currently: Low 3 bits hold ->kind on _P64 to avoid needing >8 byte atomics + * + * This field is updated atomically with both kind and restriction. The following + * transitions are allowed and modeled by the system: + * + * GUARD -> any + * (DECLARED, FAILED) -> any non-GUARD + * IMPLICIT -> {EXPLICIT, IMPORTED} (->restriction unchanged only) + * + * In addition, we permit (with warning about undefined behavior) changing the restriction + * pointer for CONST(_IMPORT). + * + * All other kind or restriction transitions are disallowed. + */ + _Atomic(jl_ptr_kind_union_t) restriction; + size_t min_world; + _Atomic(size_t) max_world; + _Atomic(struct _jl_binding_partition_t*) next; + size_t reserved; // Reserved for ->kind. Currently this holds the low bits of ->restriction during serialization +} jl_binding_partition_t; + typedef struct _jl_binding_t { JL_DATA_TYPE - _Atomic(jl_value_t*) value; jl_globalref_t *globalref; // cached GlobalRef for this binding - _Atomic(struct _jl_binding_t*) owner; // for individual imported bindings (NULL until 'resolved') - _Atomic(jl_value_t*) ty; // binding type - uint8_t constp:1; + _Atomic(jl_value_t*) value; + _Atomic(jl_binding_partition_t*) partitions; + uint8_t declared:1; uint8_t exportp:1; // `public foo` sets `publicp`, `export foo` sets both `publicp` and `exportp` uint8_t publicp:1; // exportp without publicp is not allowed. - uint8_t imported:1; - uint8_t usingfailed:1; uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package - uint8_t padding:1; + uint8_t padding:3; } jl_binding_t; typedef struct { @@ -915,6 +980,7 @@ extern JL_DLLIMPORT jl_value_t *jl_memoryref_uint8_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_memoryref_any_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_expr_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_binding_type JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_datatype_t *jl_binding_partition_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_globalref_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_linenumbernode_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_gotonode_type JL_GLOBALLY_ROOTED; @@ -1462,6 +1528,7 @@ static inline int jl_field_isconst(jl_datatype_t *st, int i) JL_NOTSAFEPOINT #define jl_is_slotnumber(v) jl_typetagis(v,jl_slotnumber_type) #define jl_is_expr(v) jl_typetagis(v,jl_expr_type) #define jl_is_binding(v) jl_typetagis(v,jl_binding_type) +#define jl_is_binding_partition(v) jl_typetagis(v,jl_binding_partition_type) #define jl_is_globalref(v) jl_typetagis(v,jl_globalref_type) #define jl_is_gotonode(v) jl_typetagis(v,jl_gotonode_type) #define jl_is_gotoifnot(v) jl_typetagis(v,jl_gotoifnot_type) @@ -1760,10 +1827,9 @@ JL_DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, size_t len) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_gensym(void); JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, size_t len); JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void); -JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, - jl_module_t *module, - _Atomic(jl_value_t*) *bp, - jl_binding_t *bnd); +JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world, jl_code_instance_t **cache); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); @@ -1924,8 +1990,8 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs); JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *op, jl_value_t *rhs); JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED); -JL_DLLEXPORT void jl_declare_constant(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var); -JL_DLLEXPORT void jl_declare_constant_val(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val); +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from); JL_DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s); JL_DLLEXPORT void jl_module_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname); diff --git a/src/julia_internal.h b/src/julia_internal.h index 8ea1940224e66..652aae54860b5 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -805,7 +805,7 @@ JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree); int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val); -void jl_binding_set_type(jl_binding_t *b, jl_value_t *ty, int error); +void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty); void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type); JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno); @@ -860,6 +860,92 @@ jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name int nargs, jl_value_t *functionloc, jl_code_info_t *ci, int isva, int isinferred); JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *source); +EXTERN_INLINE_DECLARE enum jl_partition_kind decode_restriction_kind(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT +{ +#ifdef _P64 + uint8_t bits = (pku & 0x7); + jl_value_t *val = (jl_value_t*)(pku & ~0x7); + + if (val == NULL && bits == BINDING_KIND_IMPLICIT) { + return BINDING_KIND_GUARD; + } + + return (enum jl_partition_kind)bits; +#else + return (enum jl_partition_kind)pku.kind; +#endif +} + +STATIC_INLINE jl_value_t *decode_restriction_value(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT +{ +#ifdef _P64 + jl_value_t *val = (jl_value_t*)(pku & ~0x7); + // This is a little bit of a lie at the moment - it is one of the things that + // can go wrong with binding replacement. + JL_GC_PROMISE_ROOTED(val); + return val; +#else + return pku.val; +#endif +} + +STATIC_INLINE jl_ptr_kind_union_t encode_restriction(jl_value_t *val, enum jl_partition_kind kind) JL_NOTSAFEPOINT +{ +#ifdef _P64 + if (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED) + assert(val == NULL); + if (kind == BINDING_KIND_GUARD) + kind = BINDING_KIND_IMPLICIT; + assert((((uintptr_t)val) & 0x7) == 0); + return ((jl_ptr_kind_union_t)val) | kind; +#else + jl_ptr_kind_union_t ret = { val, kind }; + return ret; +#endif +} + +STATIC_INLINE int jl_bkind_is_some_import(enum jl_partition_kind kind) JL_NOTSAFEPOINT { + return kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED; +} + +STATIC_INLINE int jl_bkind_is_some_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT { + return kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT; +} + +STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFEPOINT { + return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED; +} + +EXTERN_INLINE_DECLARE jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) JL_NOTSAFEPOINT { + if (!b) + return NULL; + assert(jl_is_binding(b)); + return jl_atomic_load_relaxed(&b->partitions); +} + +JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr, size_t world); + +EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { + return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); +} + +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT; + +#ifndef __clang_analyzer__ +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT +{ + while (1) { + if (!*bpart) + return encode_restriction(NULL, BINDING_KIND_GUARD); + jl_ptr_kind_union_t pku = jl_atomic_load_acquire(&(*bpart)->restriction); + if (!jl_bkind_is_some_import(decode_restriction_kind(pku))) + return pku; + *bnd = (jl_binding_t*)decode_restriction_value(pku); + *bpart = jl_get_binding_partition(*bnd, world); + } +} +#endif + STATIC_INLINE int is_anonfn_typename(char *name) { if (name[0] != '#' || name[1] == '#') diff --git a/src/method.c b/src/method.c index d890489c390f9..d4457b1549353 100644 --- a/src/method.c +++ b/src/method.c @@ -237,11 +237,9 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve if (fe_mod->istopmod && !strcmp(jl_symbol_name(fe_sym), "getproperty") && jl_is_symbol(s)) { if (eager_resolve || jl_binding_resolved_p(me_mod, me_sym)) { jl_binding_t *b = jl_get_binding(me_mod, me_sym); - if (b && b->constp) { - jl_value_t *v = jl_atomic_load_relaxed(&b->value); - if (v && jl_is_module(v)) - return jl_module_globalref((jl_module_t*)v, (jl_sym_t*)s); - } + jl_value_t *v = jl_get_binding_value_if_const(b); + if (v && jl_is_module(v)) + return jl_module_globalref((jl_module_t*)v, (jl_sym_t*)s); } } } @@ -254,7 +252,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve if (jl_binding_resolved_p(fe_mod, fe_sym)) { // look at some known called functions jl_binding_t *b = jl_get_binding(fe_mod, fe_sym); - if (b && b->constp && jl_atomic_load_relaxed(&b->value) == jl_builtin_tuple) { + if (jl_get_binding_value_if_const(b) == jl_builtin_tuple) { size_t j; for (j = 1; j < nargs; j++) { if (!jl_is_quotenode(jl_exprarg(e, j))) @@ -1124,29 +1122,24 @@ jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name return m; } -// empty generic function def -JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, - jl_module_t *module, - _Atomic(jl_value_t*) *bp, - jl_binding_t *bnd) +JL_DLLEXPORT void jl_check_gf(jl_value_t *gf, jl_sym_t *name) { - jl_value_t *gf = NULL; - - assert(name && bp); - if (bnd && jl_atomic_load_relaxed(&bnd->value) != NULL && !bnd->constp) + if (!jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(gf)) && !jl_is_type(gf)) jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); - gf = jl_atomic_load_relaxed(bp); - if (gf != NULL) { - if (!jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(gf)) && !jl_is_type(gf)) - jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); - } - if (bnd) - bnd->constp = 1; // XXX: use jl_declare_constant and jl_checked_assignment - if (gf == NULL) { - gf = (jl_value_t*)jl_new_generic_function(name, module); - jl_atomic_store(bp, gf); // TODO: fix constp assignment data race - if (bnd) jl_gc_wb(bnd, gf); +} + +JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name) +{ + jl_value_t *gf = jl_get_binding_value_if_const(b); + if (gf) { + jl_check_gf(gf, b->globalref->name); + return gf; } + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) + jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); + gf = (jl_value_t*)jl_new_generic_function(name, mod); + jl_declare_constant_val(b, mod, name, gf); return gf; } diff --git a/src/module.c b/src/module.c index bfe266ee424f5..7f03fc7e66a30 100644 --- a/src/module.c +++ b/src/module.c @@ -12,6 +12,23 @@ extern "C" { #endif +// In this translation unit and this translation unit only emit this symbol `extern` for use by julia +EXTERN_INLINE_DEFINE jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) JL_NOTSAFEPOINT; +EXTERN_INLINE_DEFINE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT; +extern inline enum jl_partition_kind decode_restriction_kind(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT; + +JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr, size_t world) +{ + if (!gr) + return NULL; + jl_binding_t *b = NULL; + if (gr) + b = gr->binding; + if (!b) + b = jl_get_module_binding(gr->mod, gr->name, 0); + return jl_get_binding_partition(b, world); +} + JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_names) { jl_task_t *ct = jl_current_task; @@ -161,37 +178,51 @@ static jl_globalref_t *jl_new_globalref(jl_module_t *mod, jl_sym_t *name, jl_bin jl_task_t *ct = jl_current_task; jl_globalref_t *g = (jl_globalref_t*)jl_gc_alloc(ct->ptls, sizeof(jl_globalref_t), jl_globalref_type); g->mod = mod; - jl_gc_wb(g, g->mod); + jl_gc_wb_fresh(g, g->mod); g->name = name; + jl_gc_wb_fresh(g, g->name); g->binding = b; + jl_gc_wb_fresh(g, g->binding); return g; } +static jl_binding_partition_t *new_binding_partition(void) +{ + jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_gc_alloc(jl_current_task->ptls, sizeof(jl_binding_partition_t), jl_binding_partition_type); + jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); + bpart->min_world = 0; + jl_atomic_store_relaxed(&bpart->max_world, (size_t)-1); + jl_atomic_store_relaxed(&bpart->next, NULL); +#ifdef _P64 + bpart->reserved = 0; +#endif + return bpart; +} + static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) { jl_task_t *ct = jl_current_task; assert(jl_is_module(mod) && jl_is_symbol(name)); jl_binding_t *b = (jl_binding_t*)jl_gc_alloc(ct->ptls, sizeof(jl_binding_t), jl_binding_type); jl_atomic_store_relaxed(&b->value, NULL); - jl_atomic_store_relaxed(&b->owner, NULL); - jl_atomic_store_relaxed(&b->ty, NULL); + jl_atomic_store_relaxed(&b->partitions, NULL); b->globalref = NULL; - b->constp = 0; b->exportp = 0; b->publicp = 0; - b->imported = 0; b->deprecated = 0; - b->usingfailed = 0; - b->padding = 0; JL_GC_PUSH1(&b); b->globalref = jl_new_globalref(mod, name, b); + jl_gc_wb(b, b->globalref); + jl_binding_partition_t *bpart = new_binding_partition(); + jl_atomic_store_relaxed(&b->partitions, bpart); + jl_gc_wb(b, bpart); JL_GC_POP(); return b; } extern jl_mutex_t jl_modules_mutex; -static void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) +extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) { if (jl_current_task->ptls->in_pure_callback) jl_errorf("new globals cannot be created in a generated function"); @@ -222,14 +253,21 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym 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) { - 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_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); +retry: + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) != BINDING_KIND_DECLARED) { + 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)); + } + jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)jl_any_type, BINDING_KIND_GLOBAL); + if (!jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) + goto retry; + jl_gc_wb_knownold(bpart, jl_any_type); + } else { jl_module_t *from = jl_binding_dbgmodule(b, m, var); if (from == m) jl_errorf("cannot assign a value to imported variable %s.%s", @@ -251,43 +289,88 @@ JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var return b->globalref->mod; // TODO: deprecate this? } +JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return decode_restriction_value(pku); + return jl_atomic_load_relaxed(&b->value); +} + +JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return decode_restriction_value(pku); + return jl_atomic_load(&b->value); +} + +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return NULL; + if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return NULL; + return decode_restriction_value(pku); +} + +typedef struct _modstack_t { + jl_module_t *m; + jl_sym_t *var; + struct _modstack_t *prev; +} modstack_t; +static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st); + +JL_DLLEXPORT jl_value_t *jl_reresolve_binding_value_seqcst(jl_binding_t *b) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) { + jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL); + } + return jl_get_binding_value_seqcst(b); +} + // get binding for adding a method // like jl_get_binding_wr, but has different error paths and messages JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var) { 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) - check_safe_newbinding(m, var); - if (b2 != NULL || (!jl_atomic_cmpswap(&b->owner, &b2, b) && b2 != b)) { - jl_value_t *f = jl_atomic_load_relaxed(&b2->value); - jl_module_t *from = jl_binding_dbgmodule(b, m, var); - if (f == NULL) { - // we must have implicitly imported this with using, so call jl_binding_dbgmodule to try to get the name of the module we got this from - jl_errorf("invalid method definition in %s: exported function %s.%s does not exist", - jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); - } - // TODO: we might want to require explicitly importing types to add constructors - // or we might want to drop this error entirely - if (!b->imported && !(b2->constp && jl_is_type(f) && strcmp(jl_symbol_name(var), "=>") != 0)) { - jl_errorf("invalid method definition in %s: function %s.%s must be explicitly imported to be extended", - jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) != BINDING_KIND_DECLARED) { + check_safe_newbinding(m, var); } - return b2; + return b; + } + jl_value_t *f = jl_get_binding_value_if_const(b); + if (f == NULL) { + jl_module_t *from = jl_binding_dbgmodule(b, m, var); + // we must have implicitly imported this with using, so call jl_binding_dbgmodule to try to get the name of the module we got this from + jl_errorf("invalid method definition in %s: exported function %s.%s does not exist", + jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); } + // TODO: we might want to require explicitly importing types to add constructors + // or we might want to drop this error entirely + if (decode_restriction_kind(pku) != BINDING_KIND_IMPORTED && !(f && jl_is_type(f) && strcmp(jl_symbol_name(var), "=>") != 0)) { + jl_module_t *from = jl_binding_dbgmodule(b, m, var); + jl_errorf("invalid method definition in %s: function %s.%s must be explicitly imported to be extended", + jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); + } + return b; } return b; } -typedef struct _modstack_t { - jl_module_t *m; - jl_sym_t *var; - struct _modstack_t *prev; -} modstack_t; - -static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st); - static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; #ifndef __clang_gcanalyzer__ @@ -298,23 +381,28 @@ static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROO } #endif -static int eq_bindings(jl_binding_t *owner, jl_binding_t *alias) +static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_t world) { - assert(owner == jl_atomic_load_relaxed(&owner->owner)); - if (owner == alias) + jl_ptr_kind_union_t owner_pku = jl_atomic_load_relaxed(&owner->restriction); + assert(decode_restriction_kind(owner_pku) == BINDING_KIND_GLOBAL || + jl_bkind_is_some_constant(decode_restriction_kind(owner_pku))); + jl_binding_partition_t *alias_bpart = jl_get_binding_partition(alias, world); + if (owner == alias_bpart) return 1; - alias = jl_atomic_load_relaxed(&alias->owner); - if (owner == alias) + jl_ptr_kind_union_t alias_pku = jl_walk_binding_inplace(&alias, &alias_bpart, world); + if (jl_bkind_is_some_constant(decode_restriction_kind(owner_pku)) && + jl_bkind_is_some_constant(decode_restriction_kind(alias_pku)) && + decode_restriction_value(owner_pku) && + decode_restriction_value(alias_pku) == decode_restriction_value(owner_pku)) return 1; - if (owner->constp && alias->constp && jl_atomic_load_relaxed(&owner->value) && jl_atomic_load_relaxed(&alias->value) == jl_atomic_load_relaxed(&owner->value)) - return 1; - return 0; + return owner == alias_bpart; } // find a binding from a module's `usings` list static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, jl_module_t **from, modstack_t *st, int warn) { jl_binding_t *b = NULL; + jl_binding_partition_t *bpart = NULL; jl_module_t *owner = NULL; JL_LOCK(&m->lock); int i = (int)m->usings.len - 1; @@ -329,13 +417,17 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl if (tempb == NULL) // couldn't resolve; try next using (see issue #6105) continue; - assert(jl_atomic_load_relaxed(&tempb->owner) == tempb); - if (b != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempb, b)) { + jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + jl_ptr_kind_union_t tempb_pku = jl_atomic_load_relaxed(&tempbpart->restriction); + assert(decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); + (void)tempb_pku; + if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, jl_current_task->world_age)) { if (warn) { // set usingfailed=1 to avoid repeating this warning // the owner will still be NULL, so it can be later imported or defined tempb = jl_get_module_binding(m, var, 1); - tempb->usingfailed = 1; + tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + jl_atomic_store_release(&tempbpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED)); jl_printf(JL_STDERR, "WARNING: both %s and %s export \"%s\"; uses of it in module %s must be qualified\n", jl_symbol_name(owner->name), @@ -347,6 +439,7 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl if (owner == NULL || !tempb->deprecated) { owner = imp; b = tempb; + bpart = tempbpart; } } } @@ -358,13 +451,14 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl // this might not be the same as the owner of the binding, since the binding itself may itself have been imported from elsewhere static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) { - jl_binding_t *b2 = jl_atomic_load_relaxed(&b->owner); - if (b2 != b && !b->imported) { + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) != BINDING_KIND_GLOBAL) { // for implicitly imported globals, try to re-resolve it to find the module we got it from most directly jl_module_t *from = NULL; - b = using_resolve_binding(m, var, &from, NULL, 0); - if (b) { - if (b2 == NULL || jl_atomic_load_relaxed(&b->owner) == jl_atomic_load_relaxed(&b2->owner)) + jl_binding_t *b2 = using_resolve_binding(m, var, &from, NULL, 0); + if (b2) { + jl_binding_partition_t *b2part = jl_get_binding_partition(b2, jl_current_task->world_age); + if (eq_bindings(b2part, b, jl_current_task->world_age)) return from; // if we did not find it (or accidentally found a different one), ignore this } @@ -379,10 +473,16 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * { if (b == NULL) b = jl_get_module_binding(m, var, 1); - jl_binding_t *b2 = jl_atomic_load_relaxed(&b->owner); - if (b2 == NULL) { - if (b->usingfailed) - return NULL; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); +retry: + if (decode_restriction_kind(pku) == BINDING_KIND_FAILED) + return NULL; + if (decode_restriction_kind(pku) == BINDING_KIND_DECLARED) { + return b; + } + if (decode_restriction_kind(pku) == BINDING_KIND_GUARD) { + jl_binding_t *b2 = NULL; modstack_t top = { m, var, st }; modstack_t *tmp = st; for (; tmp != NULL; tmp = tmp->prev) { @@ -397,19 +497,17 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * return NULL; assert(from); JL_GC_PROMISE_ROOTED(from); // gc-analysis does not understand output parameters + JL_GC_PROMISE_ROOTED(b2); if (b2->deprecated) { - if (jl_atomic_load_relaxed(&b2->value) == jl_nothing) { + if (jl_get_binding_value(b2) == jl_nothing) { // silently skip importing deprecated values assigned to nothing (to allow later mutation) return NULL; } } // do a full import to prevent the result of this lookup from // changing, for example if this var is assigned to later. - jl_binding_t *owner = NULL; - if (!jl_atomic_cmpswap(&b->owner, &owner, b2)) { - // concurrent import - return owner; - } + if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction((jl_value_t*)b2, BINDING_KIND_IMPLICIT))) + goto retry; if (b2->deprecated) { b->deprecated = 1; // we will warn about this below, but we might want to warn at the use sites too if (m != jl_main_module && m != jl_base_module && @@ -424,20 +522,26 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * jl_binding_dep_message(from, var, b2); } } + return b2; } - assert(jl_atomic_load_relaxed(&b2->owner) == b2); - return b2; + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + return b; } // get the current likely owner of binding when accessing m.var, without resolving the binding (it may change later) JL_DLLEXPORT jl_binding_t *jl_binding_owner(jl_module_t *m, jl_sym_t *var) { - jl_binding_t *b = jl_get_module_binding(m, var, 0); + jl_binding_t *b = jl_get_module_binding(m, var, 1); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_module_t *from = m; - if (b == NULL || (!b->usingfailed && jl_atomic_load_relaxed(&b->owner) == NULL)) + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (decode_restriction_kind(pku) == BINDING_KIND_GUARD) { b = using_resolve_binding(m, var, &from, NULL, 0); - else - b = jl_atomic_load_relaxed(&b->owner); + bpart = jl_get_binding_partition(b, jl_current_task->world_age); + } + pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return NULL; return b; } @@ -445,13 +549,20 @@ JL_DLLEXPORT jl_binding_t *jl_binding_owner(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) { jl_binding_t *b = jl_get_module_binding(m, var, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (b == NULL) return jl_nothing; - b = jl_atomic_load_relaxed(&b->owner); - if (b == NULL) + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) return jl_nothing; - jl_value_t *ty = jl_atomic_load_relaxed(&b->ty); - return ty ? ty : jl_nothing; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + // TODO: We would like to return the type of the constant, but + // currently code relies on this returning any to bypass conversion + // before an attempted assignment to a constant. + // return jl_typeof(jl_atomic_load_relaxed(&bpart->restriction)); + return (jl_value_t*)jl_any_type; + } + return decode_restriction_value(pku); } JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) @@ -482,7 +593,8 @@ JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); - return b && b->imported; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + return b && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_IMPORTED; } extern const char *jl_filename; @@ -501,7 +613,7 @@ static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t jl_binding_t *dep_message_binding = jl_get_binding(m, jl_symbol(dep_binding_name)); jl_value_t *dep_message = NULL; if (dep_message_binding != NULL) - dep_message = jl_atomic_load_relaxed(&dep_message_binding->value); + dep_message = jl_get_binding_value(dep_message_binding); JL_GC_PUSH1(&dep_message); if (dep_message != NULL) { if (jl_is_string(dep_message)) { @@ -512,7 +624,7 @@ static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t } } else { - jl_value_t *v = jl_atomic_load_relaxed(&b->value); + jl_value_t *v = jl_get_binding_value(b); dep_message = v; // use as gc-root if (v) { if (jl_is_type(v) || jl_is_module(v)) { @@ -549,9 +661,12 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_symbol_name(to->name)); } else { - assert(jl_atomic_load_relaxed(&b->owner) == b); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(pku))); + (void)pku; if (b->deprecated) { - if (jl_atomic_load_relaxed(&b->value) == jl_nothing) { + if (jl_get_binding_value(b) == jl_nothing) { // silently skip importing deprecated values assigned to nothing (to allow later mutation) return; } @@ -575,17 +690,28 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, // importing a binding on top of itself. harmless. return; } - jl_binding_t *ownerto = NULL; - if (jl_atomic_cmpswap(&bto->owner, &ownerto, b)) { - bto->imported |= (explici != 0); + jl_binding_partition_t *btopart = jl_get_binding_partition(bto, jl_current_task->world_age); + jl_ptr_kind_union_t bto_pku = jl_atomic_load_relaxed(&btopart->restriction); +retry: + if (decode_restriction_kind(bto_pku) == BINDING_KIND_GUARD || + decode_restriction_kind(bto_pku) == BINDING_KIND_IMPLICIT || + decode_restriction_kind(bto_pku) == BINDING_KIND_FAILED) { + + jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)b, (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); + if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) + goto retry; bto->deprecated |= b->deprecated; // we already warned about this above, but we might want to warn at the use sites too } else { - if (eq_bindings(b, bto)) { - // already imported - bto->imported |= (explici != 0); + if (eq_bindings(bpart, bto, jl_current_task->world_age)) { + // already imported - potentially upgrade to _IMPORTED or _EXPLICIT + if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { + jl_ptr_kind_union_t new_pku = encode_restriction(decode_restriction_value(bto_pku), (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); + if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) + goto retry; + } } - else if (ownerto != bto) { + else if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { // already imported from somewhere else jl_printf(JL_STDERR, "WARNING: ignoring conflicting import of %s.%s into %s\n", @@ -647,18 +773,24 @@ JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from) jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - if (b->exportp && (jl_atomic_load_relaxed(&b->owner) == b || b->imported)) { + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (b->exportp && (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(pku) == BINDING_KIND_IMPORTED)) { jl_sym_t *var = b->globalref->name; jl_binding_t *tob = jl_get_module_binding(to, var, 0); - if (tob && jl_atomic_load_relaxed(&tob->owner) != NULL && - // don't warn for conflicts with the module name itself. - // see issue #4715 - var != to->name && - !eq_bindings(jl_atomic_load_relaxed(&tob->owner), b)) { - jl_printf(JL_STDERR, - "WARNING: using %s.%s in module %s conflicts with an existing identifier.\n", - jl_symbol_name(from->name), jl_symbol_name(var), - jl_symbol_name(to->name)); + if (tob) { + jl_binding_partition_t *tobpart = jl_get_binding_partition(tob, jl_current_task->world_age); + jl_ptr_kind_union_t tobpku = jl_walk_binding_inplace(&tob, &tobpart, jl_current_task->world_age); + if (tob && decode_restriction_kind(tobpku) != BINDING_KIND_GUARD && + // don't warn for conflicts with the module name itself. + // see issue #4715 + var != to->name && + !eq_bindings(tobpart, b, jl_current_task->world_age)) { + jl_printf(JL_STDERR, + "WARNING: using %s.%s in module %s conflicts with an existing identifier.\n", + jl_symbol_name(from->name), jl_symbol_name(var), + jl_symbol_name(to->name)); + } } } table = jl_atomic_load_relaxed(&from->bindings); @@ -683,14 +815,23 @@ JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exported) JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // unlike most queries here, this is currently seq_cst { - jl_binding_t *b = allow_import ? jl_get_binding(m, var) : jl_get_module_binding(m, var, 0); - return b && (jl_atomic_load_relaxed(&b->owner) == b) && (jl_atomic_load(&b->value) != NULL); + jl_binding_t *b = jl_get_module_binding(m, var, allow_import); + if (!b) + return 0; + if (!allow_import) { + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!bpart || jl_bkind_is_some_import(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) + return 0; + return jl_get_binding_value(b) != NULL; + } + return jl_reresolve_binding_value_seqcst(b) != NULL; } JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); - return b && (b->exportp || jl_atomic_load_relaxed(&b->owner) == b); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + return b && (b->exportp || decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL); } JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var) @@ -708,7 +849,11 @@ JL_DLLEXPORT int jl_module_public_p(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); - return b && jl_atomic_load_relaxed(&b->owner) != NULL; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!bpart) + return 0; + enum jl_partition_kind kind = decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); + return kind == BINDING_KIND_DECLARED || !jl_bkind_is_some_guard(kind); } static uint_t bindingkey_hash(size_t idx, jl_value_t *data) @@ -736,6 +881,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m, jl_sym_t *var, ssize_t idx = jl_smallintset_lookup(bindingkeyset, bindingkey_eq, var, (jl_value_t*)bindings, hv, 0); // acquire if (idx != -1) { jl_binding_t *b = (jl_binding_t*)jl_svecref(bindings, idx); // relaxed + JL_GC_PROMISE_ROOTED(b); if (locked) JL_UNLOCK(&m->lock); return b; @@ -780,7 +926,7 @@ JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr) jl_binding_t *b = gr->binding; b = jl_resolve_owner(b, gr->mod, gr->name, NULL); // ignores b->deprecated - return b == NULL ? NULL : jl_atomic_load_relaxed(&b->value); + return b == NULL ? NULL : jl_get_binding_value(b); } JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) @@ -791,7 +937,7 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) // XXX: this only considers if the original is deprecated, not the binding in m if (b->deprecated) jl_binding_deprecation_warning(m, var, b); - return jl_atomic_load_relaxed(&b->value); + return jl_get_binding_value(b); } JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) @@ -804,43 +950,33 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var { // this function is mostly only used during initialization, so the data races here are not too important to us jl_binding_t *bp = jl_get_module_binding(m, var, 1); - jl_binding_t *b2 = NULL; - if (!jl_atomic_cmpswap(&bp->owner, &b2, bp) && b2 != bp) - jl_errorf("invalid redefinition of constant %s", jl_symbol_name(var)); - if (jl_atomic_load_relaxed(&bp->value) == NULL) { - jl_value_t *old_ty = NULL; - jl_atomic_cmpswap_relaxed(&bp->ty, &old_ty, (jl_value_t*)jl_any_type); - uint8_t constp = 0; - // if (jl_atomic_cmpswap(&bp->constp, &constp, 1)) { - if (constp = bp->constp, bp->constp = 1, constp == 0) { - jl_value_t *old = NULL; - if (jl_atomic_cmpswap(&bp->value, &old, val)) { - jl_gc_wb(bp, val); - return; - } - } - } - jl_errorf("invalid redefinition of constant %s", jl_symbol_name(var)); + jl_binding_partition_t *bpart = jl_get_binding_partition(bp, jl_current_task->world_age); + jl_atomic_store_release(&bpart->restriction, encode_restriction(val, BINDING_KIND_CONST)); + jl_gc_wb(bpart, val); } JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; b = jl_resolve_owner(b, gr->mod, gr->name, NULL); - return b && b->constp; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!bpart) + return 0; + return jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); } JL_DLLEXPORT int jl_globalref_boundp(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; b = jl_resolve_owner(b, gr->mod, gr->name, NULL); - return b && jl_atomic_load_relaxed(&b->value) != NULL; + return b && jl_get_binding_value(b) != NULL; } JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_binding(m, var); - return b && b->constp; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + return b && jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); } // set the deprecated flag for a binding: @@ -870,7 +1006,6 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b if (b->deprecated == 1 && jl_options.depwarn) { if (jl_options.depwarn != JL_OPTIONS_DEPWARN_ERROR) jl_printf(JL_STDERR, "WARNING: "); - assert(jl_atomic_load_relaxed(&b->owner) == b); jl_printf(JL_STDERR, "%s.%s is deprecated", jl_symbol_name(m->name), jl_symbol_name(s)); jl_binding_dep_message(m, s, b); @@ -889,39 +1024,29 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b } } -jl_value_t *jl_check_binding_wr(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, int reassign) +jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, int reassign) { - jl_value_t *old_ty = NULL; - if (!jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type)) { - if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { - JL_GC_PUSH1(&rhs); // callee-rooted - if (!jl_isa(rhs, old_ty)) - jl_errorf("cannot assign an incompatible value to the global %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(var)); - JL_GC_POP(); - } - } - else { - old_ty = (jl_value_t*)jl_any_type; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_value_t *old = decode_restriction_value(pku); + if (jl_egal(rhs, old)) + return NULL; + if (jl_typeof(rhs) == jl_typeof(old)) + jl_errorf("invalid redefinition of constant %s.%s. This redefinition may be permitted using the `const` keyword.", + jl_symbol_name(mod->name), jl_symbol_name(var)); + else + jl_errorf("invalid redefinition of constant %s.%s.", + jl_symbol_name(mod->name), jl_symbol_name(var)); } - if (b->constp) { - if (reassign) { - jl_value_t *old = NULL; - if (jl_atomic_cmpswap(&b->value, &old, rhs)) { - jl_gc_wb(b, rhs); - return NULL; - } - if (jl_egal(rhs, old)) - return NULL; - if (jl_typeof(rhs) != jl_typeof(old) || jl_is_type(rhs) || jl_is_module(rhs)) - reassign = 0; - else - jl_safe_printf("WARNING: redefinition of constant %s.%s. This may fail, cause incorrect answers, or produce other errors.\n", - jl_symbol_name(mod->name), jl_symbol_name(var)); - } - if (!reassign) - jl_errorf("invalid redefinition of constant %s.%s", - jl_symbol_name(mod->name), jl_symbol_name(var)); + jl_value_t *old_ty = decode_restriction_value(pku); + if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { + JL_GC_PUSH1(&rhs); // callee-rooted + if (!jl_isa(rhs, old_ty)) + jl_errorf("cannot assign an incompatible value to the global %s.%s.", + jl_symbol_name(mod->name), jl_symbol_name(var)); + JL_GC_POP(); } return old_ty; } @@ -952,12 +1077,13 @@ JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, j JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *op, jl_value_t *rhs) { - jl_value_t *ty = NULL; - if (jl_atomic_cmpswap_relaxed(&b->ty, &ty, (jl_value_t*)jl_any_type)) - ty = (jl_value_t*)jl_any_type; - if (b->constp) + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) jl_errorf("invalid redefinition of constant %s.%s", jl_symbol_name(mod->name), jl_symbol_name(var)); + jl_value_t *ty = decode_restriction_value(pku); return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var); } @@ -970,16 +1096,6 @@ JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod return old; } -JL_DLLEXPORT void jl_declare_constant(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var) -{ - // n.b. jl_get_binding_wr should have ensured b->owner == b as mod.var - if (jl_atomic_load_relaxed(&b->owner) != b || (jl_atomic_load_relaxed(&b->value) != NULL && !b->constp)) { - jl_errorf("cannot declare %s.%s constant; it already has a value", - jl_symbol_name(mod->name), jl_symbol_name(var)); - } - b->constp = 1; -} - JL_DLLEXPORT jl_value_t *jl_module_usings(jl_module_t *m) { JL_LOCK(&m->lock); @@ -996,11 +1112,6 @@ JL_DLLEXPORT jl_value_t *jl_module_usings(jl_module_t *m) return (jl_value_t*)a; } -uint8_t _binding_is_from_explicit_using(jl_binding_t *b) { - jl_binding_t *owner = jl_atomic_load_relaxed(&b->owner); - return (owner != NULL && owner != b && !b->imported); -} - void _append_symbol_to_bindings_array(jl_array_t* a, jl_sym_t *name) { jl_array_grow_end(a, 1); //XXX: change to jl_arrayset if array storage allocation for Array{Symbols,1} changes: @@ -1017,10 +1128,12 @@ void append_module_names(jl_array_t* a, jl_module_t *m, int all, int imported, i jl_sym_t *asname = b->globalref->name; int hidden = jl_symbol_name(asname)[0]=='#'; int main_public = (m == jl_main_module && !(asname == jl_eval_sym || asname == jl_include_sym)); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + enum jl_partition_kind kind = decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); if (((b->publicp) || - (imported && b->imported) || - (usings && _binding_is_from_explicit_using(b)) || - (jl_atomic_load_relaxed(&b->owner) == b && !b->imported && (all || main_public))) && + (imported && (kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_IMPORTED)) || + (usings && kind == BINDING_KIND_EXPLICIT) || + ((kind == BINDING_KIND_GLOBAL || kind == BINDING_KIND_CONST || kind == BINDING_KIND_DECLARED) && (all || main_public))) && (all || (!b->deprecated && !hidden))) _append_symbol_to_bindings_array(a, asname); } @@ -1095,8 +1208,10 @@ JL_DLLEXPORT void jl_clear_implicit_imports(jl_module_t *m) jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - if (jl_atomic_load_relaxed(&b->owner) && jl_atomic_load_relaxed(&b->owner) != b && !b->imported) - jl_atomic_store_relaxed(&b->owner, NULL); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_IMPLICIT) { + jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); + } } JL_UNLOCK(&m->lock); } diff --git a/src/rtutils.c b/src/rtutils.c index a60597827b92c..a6a7fd5614de0 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -553,7 +553,7 @@ JL_DLLEXPORT jl_value_t *jl_stderr_obj(void) JL_NOTSAFEPOINT if (jl_base_module == NULL) return NULL; jl_binding_t *stderr_obj = jl_get_module_binding(jl_base_module, jl_symbol("stderr"), 0); - return stderr_obj ? jl_atomic_load_relaxed(&stderr_obj->value) : NULL; + return stderr_obj ? jl_get_binding_value(stderr_obj) : NULL; } // toys for debugging --------------------------------------------------------- @@ -648,12 +648,10 @@ static int is_globname_binding(jl_value_t *v, jl_datatype_t *dv) JL_NOTSAFEPOINT jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL; if (globname && dv->name->module) { jl_binding_t *b = jl_get_module_binding(dv->name->module, globname, 0); - if (b && jl_atomic_load_relaxed(&b->owner) && b->constp) { - jl_value_t *bv = jl_atomic_load_relaxed(&b->value); - // The `||` makes this function work for both function instances and function types. - if (bv == v || jl_typeof(bv) == v) - return 1; - } + jl_value_t *bv = jl_get_binding_value_if_const(b); + // The `||` makes this function work for both function instances and function types. + if (bv && (bv == v || jl_typeof(bv) == v)) + return 1; } return 0; } diff --git a/src/staticdata.c b/src/staticdata.c index 1fb8c8ec79460..6dfe5e91a9c55 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -100,7 +100,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 191 +#define NUM_TAGS 192 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -122,6 +122,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_array_type); INSERT_TAG(jl_expr_type); INSERT_TAG(jl_binding_type); + INSERT_TAG(jl_binding_partition_type); INSERT_TAG(jl_globalref_type); INSERT_TAG(jl_string_type); INSERT_TAG(jl_module_type); @@ -349,6 +350,18 @@ arraylist_t eytzinger_idxs; static uintptr_t img_min; static uintptr_t img_max; +// HT_NOTFOUND is a valid integer ID, so we store the integer ids mangled. +// This pair of functions mangles/demanges +static size_t from_seroder_entry(void *entry) +{ + return (size_t)((char*)entry - (char*)HT_NOTFOUND - 1); +} + +static void *to_seroder_entry(size_t idx) +{ + return (void*)((char*)HT_NOTFOUND + 1 + idx); +} + static int ptr_cmp(const void *l, const void *r) { uintptr_t left = *(const uintptr_t*)l; @@ -563,6 +576,8 @@ enum RefTags { ExternalLinkage // reference to some other pkgimage }; +#define SYS_EXTERNAL_LINK_UNIT sizeof(void*) + // calling conventions for internal entry points. // this is used to set the method-instance->invoke field typedef enum { @@ -768,7 +783,7 @@ static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_ if ((void*)b == jl_nothing) break; jl_sym_t *name = b->globalref->name; - if (name == jl_docmeta_sym && jl_atomic_load_relaxed(&b->value)) + if (name == jl_docmeta_sym && jl_get_binding_value(b)) record_field_change((jl_value_t**)&b->value, jl_nothing); } } @@ -922,14 +937,17 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ else if (jl_typetagis(v, jl_module_tag << 4)) { jl_queue_module_for_serialization(s, (jl_module_t*)v); } + else if (jl_is_binding_partition(v)) { + jl_binding_partition_t *bpart = (jl_binding_partition_t*)v; + jl_queue_for_serialization_(s, decode_restriction_value(jl_atomic_load_relaxed(&bpart->restriction)), 1, immediate); + jl_queue_for_serialization_(s, get_replaceable_field((jl_value_t**)&bpart->next, 0), 1, immediate); + } else if (layout->nfields > 0) { char *data = (char*)jl_data_ptr(v); size_t i, np = layout->npointers; for (i = 0; i < np; i++) { uint32_t ptr = jl_ptr_offset(t, i); int mutabl = t->name->mutabl; - if (jl_is_binding(v) && ((jl_binding_t*)v)->constp && i == 0) // value field depends on constp field - mutabl = 0; jl_value_t *fld = get_replaceable_field(&((jl_value_t**)data)[ptr], mutabl); jl_queue_for_serialization_(s, fld, 1, immediate); } @@ -943,7 +961,7 @@ done_fields: ; arraylist_push(&serialization_queue, (void*) v); size_t idx = serialization_queue.len - 1; assert(serialization_queue.len < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "too many items to serialize"); - *bp = (void*)((char*)HT_NOTFOUND + 1 + idx); + *bp = to_seroder_entry(idx); // DataType is very unusual, in that some of the fields need to be pre-order, and some // (notably super) must not be (even if `jl_queue_for_serialization_` would otherwise @@ -1064,8 +1082,8 @@ static uintptr_t add_external_linkage(jl_serializer_state *s, jl_value_t *v, jl_ // We found the sysimg/pkg that this item links against // Compute the relocation code size_t offset = (uintptr_t)v - (uintptr_t)jl_linkage_blobs.items[2*i]; - offset /= sizeof(void*); - assert(offset < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "offset to external image too large"); + assert((offset % SYS_EXTERNAL_LINK_UNIT) == 0); + offset /= SYS_EXTERNAL_LINK_UNIT; assert(n_linkage_blobs() == jl_array_nrows(s->buildid_depmods_idxs)); size_t depsidx = jl_array_data(s->buildid_depmods_idxs, uint32_t)[i]; // map from build_id_idx -> deps_idx assert(depsidx < INT32_MAX); @@ -1077,6 +1095,7 @@ static uintptr_t add_external_linkage(jl_serializer_state *s, jl_value_t *v, jl_ jl_array_grow_end(link_ids, 1); uint32_t *link_id_data = jl_array_data(link_ids, uint32_t); // wait until after the `grow` link_id_data[jl_array_nrows(link_ids) - 1] = depsidx; + assert(offset < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "offset to external image too large"); return ((uintptr_t)ExternalLinkage << RELOC_TAG_OFFSET) + offset; } return 0; @@ -1089,19 +1108,19 @@ static uintptr_t add_external_linkage(jl_serializer_state *s, jl_value_t *v, jl_ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t *link_ids) JL_NOTSAFEPOINT { assert(v != NULL && "cannot get backref to NULL object"); - void *idx = HT_NOTFOUND; if (jl_is_symbol(v)) { void **pidx = ptrhash_bp(&symbol_table, v); - idx = *pidx; + void *idx = *pidx; if (idx == HT_NOTFOUND) { size_t l = strlen(jl_symbol_name((jl_sym_t*)v)); write_uint32(s->symbols, l); ios_write(s->symbols, jl_symbol_name((jl_sym_t*)v), l + 1); size_t offset = ++nsym_tag; assert(offset < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "too many symbols"); - idx = (void*)((char*)HT_NOTFOUND + ((uintptr_t)SymbolRef << RELOC_TAG_OFFSET) + offset); + idx = to_seroder_entry(offset - 1); *pidx = idx; } + return ((uintptr_t)SymbolRef << RELOC_TAG_OFFSET) + from_seroder_entry(idx); } else if (v == (jl_value_t*)s->ptls->root_task) { return (uintptr_t)TagRef << RELOC_TAG_OFFSET; @@ -1129,17 +1148,15 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t * assert(item && "no external linkage identified"); return item; } + void *idx = ptrhash_get(&serialization_order, v); if (idx == HT_NOTFOUND) { - idx = ptrhash_get(&serialization_order, v); - if (idx == HT_NOTFOUND) { - jl_(jl_typeof(v)); - jl_(v); - } - assert(idx != HT_NOTFOUND && "object missed during jl_queue_for_serialization pass"); - assert(idx != (void*)(uintptr_t)-1 && "object missed during jl_insert_into_serialization_queue pass"); - assert(idx != (void*)(uintptr_t)-2 && "object missed during jl_insert_into_serialization_queue pass"); + jl_(jl_typeof(v)); + jl_(v); } - return (char*)idx - 1 - (char*)HT_NOTFOUND; + assert(idx != HT_NOTFOUND && "object missed during jl_queue_for_serialization pass"); + assert(idx != (void*)(uintptr_t)-1 && "object missed during jl_insert_into_serialization_queue pass"); + assert(idx != (void*)(uintptr_t)-2 && "object missed during jl_insert_into_serialization_queue pass"); + return ((uintptr_t)DataRef << RELOC_TAG_OFFSET) + from_seroder_entry(idx); } @@ -1341,7 +1358,15 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED if (s->incremental) { if (needs_uniquing(v)) { - if (jl_is_method_instance(v)) { + if (jl_typetagis(v, jl_binding_type)) { + jl_binding_t *b = (jl_binding_t*)v; + if (b->globalref == NULL) + jl_error("Binding cannot be serialized"); // no way (currently) to recover its identity + write_pointerfield(s, (jl_value_t*)b->globalref->mod); + write_pointerfield(s, (jl_value_t*)b->globalref->name); + continue; + } + else if (jl_is_method_instance(v)) { assert(f == s->s); jl_method_instance_t *mi = (jl_method_instance_t*)v; write_pointerfield(s, mi->def.value); @@ -1364,17 +1389,6 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED else if (needs_recaching(v)) { arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); } - else if (jl_typetagis(v, jl_binding_type)) { - jl_binding_t *b = (jl_binding_t*)v; - if (b->globalref == NULL) - jl_error("Binding cannot be serialized"); // no way (currently) to recover its identity - // Assign type Any to any owned bindings that don't have a type. - // We don't want these accidentally managing to diverge later in different compilation units. - if (jl_atomic_load_relaxed(&b->owner) == b) { - jl_value_t *old_ty = NULL; - jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type); - } - } } // write data @@ -1560,6 +1574,26 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED ios_write(s->const_data, (char*)pdata, nb); write_pointer(f); } + else if (jl_is_binding_partition(v)) { + jl_binding_partition_t *bpart = (jl_binding_partition_t*)v; + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + jl_value_t *restriction_val = decode_restriction_value(pku); + static_assert(offsetof(jl_binding_partition_t, restriction) == 0, "BindingPartition layout mismatch"); + write_pointerfield(s, restriction_val); +#ifndef _P64 + write_uint(f, decode_restriction_kind(pku)); +#endif + write_uint(f, bpart->min_world); + write_uint(f, jl_atomic_load_relaxed(&bpart->max_world)); + write_pointerfield(s, (jl_value_t*)jl_atomic_load_relaxed(&bpart->next)); +#ifdef _P64 + write_uint(f, decode_restriction_kind(pku)); // This will be moved back into place during deserialization (if necessary) + static_assert(sizeof(jl_binding_partition_t) == 5*sizeof(void*), "BindingPartition layout mismatch"); +#else + write_uint(f, 0); + static_assert(sizeof(jl_binding_partition_t) == 6*sizeof(void*), "BindingPartition layout mismatch"); +#endif + } else { // Generic object::DataType serialization by field const char *data = (const char*)v; @@ -1586,8 +1620,6 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED for (i = 0; i < np; i++) { size_t offset = jl_ptr_offset(t, i) * sizeof(jl_value_t*); int mutabl = t->name->mutabl; - if (jl_is_binding(v) && ((jl_binding_t*)v)->constp && i == 0) // value field depends on constp field - mutabl = 0; jl_value_t *fld = get_replaceable_field((jl_value_t**)&data[offset], mutabl); size_t fld_pos = offset + reloc_offset; if (fld != NULL) { @@ -1763,7 +1795,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } void *superidx = ptrhash_get(&serialization_order, dt->super); - if (s->incremental && superidx != HT_NOTFOUND && (char*)superidx - 1 - (char*)HT_NOTFOUND > item && needs_uniquing((jl_value_t*)dt->super)) + if (s->incremental && superidx != HT_NOTFOUND && from_seroder_entry(superidx) > item && needs_uniquing((jl_value_t*)dt->super)) arraylist_push(&s->uniquing_super, dt->super); } else if (jl_is_typename(v)) { @@ -1957,7 +1989,7 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas assert(s->buildid_depmods_idxs && depsidx < jl_array_len(s->buildid_depmods_idxs)); size_t i = jl_array_data(s->buildid_depmods_idxs, uint32_t)[depsidx]; assert(2*i < jl_linkage_blobs.len); - return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*sizeof(void*); + return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*SYS_EXTERNAL_LINK_UNIT; } case ExternalLinkage: { assert(link_ids); @@ -1968,7 +2000,7 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas assert(depsidx < jl_array_len(s->buildid_depmods_idxs)); size_t i = jl_array_data(s->buildid_depmods_idxs, uint32_t)[depsidx]; assert(2*i < jl_linkage_blobs.len); - return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*sizeof(void*); + return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*SYS_EXTERNAL_LINK_UNIT; } } abort(); @@ -2352,11 +2384,11 @@ static jl_svec_t *jl_prune_type_cache_hash(jl_svec_t *cache) JL_GC_DISABLED void *idx = ptrhash_get(&serialization_order, cache); assert(idx != HT_NOTFOUND && idx != (void*)(uintptr_t)-1); - assert(serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] == cache); + assert(serialization_queue.items[from_seroder_entry(idx)] == cache); cache = cache_rehash_set(cache, sz); // redirect all references to the old cache to relocate to the new cache object ptrhash_put(&serialization_order, cache, idx); - serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] = cache; + serialization_queue.items[from_seroder_entry(idx)] = cache; return cache; } @@ -3561,6 +3593,19 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl memcpy(newitems, mod->usings.items, mod->usings.len * sizeof(void*)); mod->usings.items = newitems; } + // Move the binding bits back to their correct place +#ifdef _P64 + jl_svec_t *table = jl_atomic_load_relaxed(&mod->bindings); + for (size_t i = 0; i < jl_svec_len(table); i++) { + jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); + if ((jl_value_t*)b == jl_nothing) + continue; + jl_binding_partition_t *bpart = jl_atomic_load_relaxed(&b->partitions); + jl_atomic_store_relaxed(&bpart->restriction, + encode_restriction((jl_value_t*)jl_atomic_load_relaxed(&bpart->restriction), bpart->reserved)); + bpart->reserved = 0; + } +#endif } else { abort(); diff --git a/src/support/dtypes.h b/src/support/dtypes.h index 57f4fa99f0016..6513370da4dae 100644 --- a/src/support/dtypes.h +++ b/src/support/dtypes.h @@ -123,6 +123,13 @@ typedef intptr_t ssize_t; #define STATIC_INLINE static inline #define FORCE_INLINE static inline __attribute__((always_inline)) +#ifdef _OS_WINDOWS_ +#define EXTERN_INLINE_DECLARE inline +#else +#define EXTERN_INLINE_DECLARE inline __attribute__ ((visibility("default"))) +#endif +#define EXTERN_INLINE_DEFINE extern inline JL_DLLEXPORT + #if defined(_OS_WINDOWS_) && !defined(_COMPILER_GCC_) # define NOINLINE __declspec(noinline) # define NOINLINE_DECL(f) __declspec(noinline) f diff --git a/src/toplevel.c b/src/toplevel.c index 85d922016a4f8..5d17a3fcf89a7 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -155,25 +155,31 @@ 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, 1); - jl_declare_constant(b, parent_module, name); - jl_value_t *old = NULL; - if (!jl_atomic_cmpswap(&b->value, &old, (jl_value_t*)newm)) { - if (!jl_is_module(old)) { - jl_errorf("invalid redefinition of constant %s", jl_symbol_name(name)); + jl_binding_t *b = jl_get_module_binding(parent_module, name, 1); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, ct->world_age); + jl_ptr_kind_union_t pku = encode_restriction(NULL, BINDING_KIND_CONST); + jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)newm, BINDING_KIND_CONST); + if (!jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) { + if (decode_restriction_kind(pku) != BINDING_KIND_CONST) { + jl_declare_constant_val(b, parent_module, name, (jl_value_t*)newm); + } else { + // As a special exception allow binding replacement of modules + if (!jl_is_module(decode_restriction_value(pku))) { + jl_errorf("invalid redefinition of constant %s", jl_symbol_name(name)); + } + if (jl_generating_output()) + jl_errorf("cannot replace module %s during compilation", jl_symbol_name(name)); + jl_printf(JL_STDERR, "WARNING: replacing module %s.\n", jl_symbol_name(name)); + pku = jl_atomic_exchange(&bpart->restriction, new_pku); + } + jl_gc_wb(bpart, newm); + if (decode_restriction_value(pku) != NULL && jl_is_module(decode_restriction_value(pku))) { + // create a hidden gc root for the old module + JL_LOCK(&jl_modules_mutex); + uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, decode_restriction_value(pku)); + *refcnt += 1; + JL_UNLOCK(&jl_modules_mutex); } - if (jl_generating_output()) - jl_errorf("cannot replace module %s during compilation", jl_symbol_name(name)); - jl_printf(JL_STDERR, "WARNING: replacing module %s.\n", jl_symbol_name(name)); - old = jl_atomic_exchange(&b->value, (jl_value_t*)newm); - } - jl_gc_wb(b, newm); - if (old != NULL) { - // create a hidden gc root for the old module - JL_LOCK(&jl_modules_mutex); - uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, (void*)old); - *refcnt += 1; - JL_UNLOCK(&jl_modules_mutex); } } @@ -216,27 +222,6 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex } ct->world_age = last_age; -#if 0 - // some optional post-processing steps - size_t i; - jl_svec_t *table = jl_atomic_load_relaxed(&newm->bindings); - for (size_t i = 0; i < jl_svec_len(table); i++) { - jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); - if ((void*)b != jl_nothing) { - // remove non-exported macros - if (jl_symbol_name(b->name)[0]=='@' && - !b->exportp && b->owner == b) - b->value = NULL; - // error for unassigned exports - /* - if (b->exportp && b->owner==b && b->value==NULL) - jl_errorf("identifier %s exported from %s is not initialized", - jl_symbol_name(b->name), jl_symbol_name(newm->name)); - */ - } - } -#endif - JL_LOCK(&jl_modules_mutex); uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, (void*)newm); assert(*refcnt > (uintptr_t)HT_NOTFOUND); @@ -308,18 +293,38 @@ static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f return args[0]; } -void jl_binding_set_type(jl_binding_t *b, jl_value_t *ty, int error) +void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty) { - jl_value_t *old_ty = NULL; - if (jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, ty)) { - jl_gc_wb(b, ty); - } - else if (error && !jl_types_equal(ty, old_ty)) { - jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", - jl_symbol_name(jl_globalref_mod(b->globalref)->name), jl_symbol_name(jl_globalref_name(b->globalref))); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + jl_ptr_kind_union_t new_pku = encode_restriction(ty, BINDING_KIND_GLOBAL); + while (1) { + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) + break; + continue; + } else { + jl_errorf("cannot set type for imported global %s.%s.", + jl_symbol_name(mod->name), jl_symbol_name(sym)); + } + } + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_errorf("cannot set type for imported constant %s.%s.", + jl_symbol_name(mod->name), jl_symbol_name(sym)); + } + jl_value_t *old_ty = decode_restriction_value(pku); + if (!jl_types_equal(ty, old_ty)) { + jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", + jl_symbol_name(mod->name), jl_symbol_name(sym)); + } + if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) + break; } + jl_gc_wb(bpart, ty); } +extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var); void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { // create uninitialized mutable binding for "global x" decl sometimes or probably jl_module_t *gm; @@ -334,11 +339,16 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { gm = m; gs = (jl_sym_t*)arg; } - if (!jl_binding_resolved_p(gm, gs) || set_type) { - jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); - if (set_type) { - jl_binding_set_type(b, set_type, 1); - } + jl_binding_t *b = jl_get_module_binding(gm, gs, 1); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + while (decode_restriction_kind(pku) == BINDING_KIND_GUARD || decode_restriction_kind(pku) == BINDING_KIND_FAILED) { + check_safe_newbinding(gm, gs); + if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(NULL, BINDING_KIND_DECLARED))) + break; + } + if (set_type) { + jl_binding_set_type(b, gm, gs, set_type); } } @@ -413,9 +423,7 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int jl_sym_t *name = jl_globalref_name(f); if (jl_binding_resolved_p(mod, name)) { jl_binding_t *b = jl_get_binding(mod, name); - if (b && b->constp) { - called = jl_atomic_load_relaxed(&b->value); - } + called = jl_get_binding_value_if_const(b); } } else if (jl_is_quotenode(f)) { @@ -645,21 +653,16 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym assert(m); jl_sym_t *name = asname ? asname : import->name; // TODO: this is a bit race-y with what error message we might print - jl_binding_t *b = jl_get_module_binding(m, name, 0); - jl_binding_t *b2; - if (b != NULL && (b2 = jl_atomic_load_relaxed(&b->owner)) != NULL) { - if (b2->constp && jl_atomic_load_relaxed(&b2->value) == (jl_value_t*)import) - return; - if (b2 != b) - jl_errorf("importing %s into %s conflicts with an existing global", - jl_symbol_name(name), jl_symbol_name(m->name)); - } - else { - b = jl_get_binding_wr(m, name, 1); + jl_binding_t *b = jl_get_module_binding(m, name, 1); + if (jl_get_binding_value_if_const(b) == (jl_value_t*)import) + return; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (decode_restriction_kind(pku) != BINDING_KIND_GUARD && decode_restriction_kind(pku) != BINDING_KIND_FAILED) { + jl_errorf("importing %s into %s conflicts with an existing global", + jl_symbol_name(name), jl_symbol_name(m->name)); } - jl_declare_constant(b, m, name); - jl_checked_assignment(b, m, name, (jl_value_t*)import); - b->imported = 1; + jl_declare_constant_val2(b, m, name, (jl_value_t*)import, BINDING_KIND_CONST_IMPORT); } // in `import A.B: x, y, ...`, evaluate the `A.B` part if it exists @@ -675,7 +678,7 @@ static jl_module_t *eval_import_from(jl_module_t *m JL_PROPAGATES_ROOT, jl_expr_ jl_module_t *from = eval_import_path(m, NULL, path->args, &name, keyword); if (name != NULL) { from = (jl_module_t*)jl_eval_global_var(from, name); - if (!jl_is_module(from)) + if (!from || !jl_is_module(from)) jl_errorf("invalid %s path: \"%s\" does not name a module", keyword, jl_symbol_name(name)); } return from; @@ -721,10 +724,49 @@ static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, con JL_GC_POP(); } -JL_DLLEXPORT void jl_declare_constant_val(jl_binding_t *b, jl_module_t *gm, jl_sym_t *gs, jl_value_t *val) +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + int did_warn = 0; + while (1) { + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (!val) + return bpart; + jl_value_t *old = decode_restriction_value(pku); + if (jl_egal(val, old)) + break; + if (!did_warn) { + if (jl_typeof(val) != jl_typeof(old) || jl_is_type(val) || jl_is_module(val)) + jl_errorf("invalid redefinition of constant %s.%s", + jl_symbol_name(mod->name), + jl_symbol_name(var)); + else + jl_safe_printf("WARNING: redefinition of constant %s.%s. This may fail, cause incorrect answers, or produce other errors.\n", + jl_symbol_name(mod->name), + jl_symbol_name(var)); + did_warn = 1; + } + } else if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_import(decode_restriction_kind(pku))) { + jl_errorf("cannot declare %s.%s constant; it was already declared as an import", + jl_symbol_name(mod->name), jl_symbol_name(var)); + } else { + jl_errorf("cannot declare %s.%s constant; it was already declared global", + jl_symbol_name(mod->name), jl_symbol_name(var)); + } + } + if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { + jl_gc_wb(bpart, val); + break; + } + } + return bpart; +} + +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val) { - jl_declare_constant(b, gm, gs); - jl_checked_assignment(b, gm, gs, val); + return jl_declare_constant_val2(b, mod, var, val, BINDING_KIND_CONST); } JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val) @@ -740,12 +782,8 @@ JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t gm = m; gs = (jl_sym_t*)arg; } - jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); - if (val) { - jl_declare_constant_val(b, gm, gs, val); - } else { - jl_declare_constant(b, gm, gs); - } + jl_binding_t *b = jl_get_module_binding(gm, gs, 1); + jl_declare_constant_val(b, gm, gs, val); } JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 585ff1aa775b7..ddf2f55d0b9f7 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -33,35 +33,19 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) if isdefined(ex, :scope) scope = ex.scope if scope isa Module - bnd = ccall(:jl_get_module_binding, Any, (Any, Any, Cint), scope, var, true)::Core.Binding - if isdefined(bnd, :owner) - owner = bnd.owner - if owner === bnd - print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.") - end + bpart = Base.lookup_binding_partition(Base.get_world_counter(), GlobalRef(scope, var)) + kind = Base.binding_kind(bpart) + if kind === Base.BINDING_KIND_GLOBAL || kind === Base.BINDING_KIND_CONST || kind == Base.BINDING_KIND_DECLARED + print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.") + elseif kind === Base.BINDING_KIND_FAILED + print(io, "\nHint: It looks like two or more modules export different ", + "bindings with this name, resulting in ambiguity. Try explicitly ", + "importing it from a particular module, or qualifying the name ", + "with the module it should come from.") + elseif kind === Base.BINDING_KIND_GUARD + print(io, "\nSuggestion: check for spelling errors or missing imports.") else - owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), scope, var) - if C_NULL == owner - # No global of this name exists in this module. - # This is the common case, so do not print that information. - # It could be the binding was exported by two modules, which we can detect - # by the `usingfailed` flag in the binding: - if isdefined(bnd, :flags) && Bool(bnd.flags >> 4 & 1) # magic location of the `usingfailed` flag - print(io, "\nHint: It looks like two or more modules export different ", - "bindings with this name, resulting in ambiguity. Try explicitly ", - "importing it from a particular module, or qualifying the name ", - "with the module it should come from.") - else - print(io, "\nSuggestion: check for spelling errors or missing imports.") - end - owner = bnd - else - owner = unsafe_pointer_to_objref(owner)::Core.Binding - end - end - if owner !== bnd - # this could use jl_binding_dbgmodule for the exported location in the message too - print(io, "\nSuggestion: this global was defined as `$(owner.globalref)` but not assigned a value.") + print(io, "\nSuggestion: this global was defined as `$(bpart.restriction.globalref)` but not assigned a value.") end elseif scope === :static_parameter print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.") diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 7a4044b7f4c6d..a6effb9f013fc 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -100,6 +100,7 @@ let redirect_stdout(isopen(orig_stdout) ? orig_stdout : devnull) close(pts) end + Base.errormonitor(repltask) try Base.REPL_MODULE_REF[] = REPL redirect_stdin(pts) diff --git a/test/core.jl b/test/core.jl index 648dd68602fa5..74df09bcdfd91 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8166,7 +8166,7 @@ let M = @__MODULE__ @test_throws(ErrorException("cannot set type for global $(nameof(M)).a_typed_global. It already has a value or is already set to a different type."), Core.eval(M, :(global a_typed_global::$(Union{Nothing,Tuple{Union{Integer,Nothing}}})))) @test Core.eval(M, :(global a_typed_global)) === nothing - @test Core.get_binding_type(M, :a_typed_global) === Tuple{Union{Integer,Nothing}} + @test Core.get_binding_type(M, :a_typed_global) == Tuple{Union{Integer,Nothing}} end @test Base.unsafe_convert(Ptr{Int}, [1]) !== C_NULL diff --git a/test/precompile.jl b/test/precompile.jl index 3e8fe44e1b2f0..bc738e557bb51 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1925,7 +1925,7 @@ precompile_test_harness("Issue #50538") do load_path ex isa ErrorException || rethrow() ex end - global undefglobal + global undefglobal::Any end """) ji, ofile = Base.compilecache(Base.PkgId("I50538")) diff --git a/test/syntax.jl b/test/syntax.jl index 0855c643e1423..da69bd98dc010 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2645,9 +2645,9 @@ end @test_throws ErrorException("invalid method definition in Mod3: function Mod3.f must be explicitly imported to be extended") Core.eval(Mod3, :(f(x::Int) = x)) @test !isdefined(Mod3, :always_undef) # resolve this binding now in Mod3 @test_throws ErrorException("invalid method definition in Mod3: exported function Mod.always_undef does not exist") Core.eval(Mod3, :(always_undef(x::Int) = x)) -@test_throws ErrorException("cannot assign a value to imported variable Mod.always_undef from module Mod3") Core.eval(Mod3, :(const always_undef = 3)) -@test_throws ErrorException("cannot assign a value to imported variable Mod3.f") Core.eval(Mod3, :(const f = 3)) -@test_throws ErrorException("cannot declare Mod.maybe_undef constant; it already has a value") Core.eval(Mod, :(const maybe_undef = 3)) +@test_throws ErrorException("cannot declare Mod3.always_undef constant; it was already declared as an import") Core.eval(Mod3, :(const always_undef = 3)) +@test_throws ErrorException("cannot declare Mod3.f constant; it was already declared as an import") Core.eval(Mod3, :(const f = 3)) +@test_throws ErrorException("cannot declare Mod.maybe_undef constant; it was already declared global") Core.eval(Mod, :(const maybe_undef = 3)) z = 42 import .z as also_z @@ -3704,7 +3704,8 @@ end module Foreign54607 # Syntactic, not dynamic try_to_create_binding1() = (Foreign54607.foo = 2) - @eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo)) = 2) + # GlobalRef is allowed for same-module assignment + @eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo2)) = 2) function global_create_binding() global bar bar = 3 @@ -3719,6 +3720,11 @@ end @test_throws ErrorException (Foreign54607.foo = 1) @test_throws ErrorException Foreign54607.try_to_create_binding1() @test_throws ErrorException Foreign54607.try_to_create_binding2() +function assign_in_foreign_module() + (Foreign54607.foo = 1) + nothing +end +@test !Core.Compiler.is_nothrow(Base.infer_effects(assign_in_foreign_module)) @test_throws ErrorException begin @Base.Experimental.force_compile (Foreign54607.foo = 1) @@ -3904,3 +3910,68 @@ module ExtendedIsDefined @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x4), false)) end end + +# Test importing the same module twice using two different paths +module FooDualImport +end +module BarDualImport +import ..FooDualImport +import ..FooDualImport.FooDualImport +end + +# Test trying to define a constant and then importing the same constant +const ImportConstant = 1 +module ImportConstantTestModule + using Test + const ImportConstant = 1 + import ..ImportConstant + @test ImportConstant == 1 + @test isconst(@__MODULE__, :ImportConstant) +end + +# Test trying to define a constant and then trying to assign to the same value +module AssignConstValueTest + const x = 1 + x = 1 +end +@test isconst(AssignConstValueTest, :x) + +# Module Replacement +module ReplacementContainer + module ReplaceMe + const x = 1 + end + const Old = ReplaceMe + module ReplaceMe + const x = 2 + end +end +@test ReplacementContainer.Old !== ReplacementContainer.ReplaceMe +@test ReplacementContainer.ReplaceMe.x === 2 + +# Setglobal of previously declared global +module DeclareSetglobal + using Test + @test_throws ErrorException setglobal!(@__MODULE__, :DeclareMe, 1) + global DeclareMe + setglobal!(@__MODULE__, :DeclareMe, 1) + @test DeclareMe === 1 +end + +# Binding type of const (N.B.: This may change in the future) +module ConstBindingType + using Test + const x = 1 + @test Core.get_binding_type(@__MODULE__, :x) === Any +end + +# Explicit import may resolve using failed +module UsingFailedExplicit + using Test + module A; export x; x = 1; end + module B; export x; x = 2; end + using .A, .B + @test_throws UndefVarError x + using .A: x as x + @test x === 1 +end From e921dc8fb8af13c65b9f5d14fa48b39073741c1a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Aug 2024 14:12:40 -0400 Subject: [PATCH 135/548] Revert "Don't expose guard pages to malloc_stack API consumers" (#55555) Reverts JuliaLang/julia#54591 This cause the runtime to misbehave and crash, since all of the consumers of this information in the runtime assumed that the guard pages are accounted for correctly as part of the reserved allocation. Nothing in the runtime ever promised that it is valid to access the pages beyond the current redzone (indeed, ASAN would forbid it as well). --- src/gc-stacks.c | 47 +++++------------------------------------------ 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 08425019a4daf..783129ea97693 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -23,22 +23,13 @@ // 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; @@ -49,7 +40,6 @@ 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_relaxed(&num_stack_mappings, 1); return stk; @@ -58,68 +48,41 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT static void free_stack(void *stkbuf, 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; - stkbuf = (char *)stkbuf - guard_size; -#endif - VirtualFree(stkbuf, 0, MEM_RELEASE); jl_atomic_fetch_add_relaxed(&num_stack_mappings, -1); } #else -# ifdef _OS_OPENBSD_ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT { - void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - if (stk == MAP_FAILED) - return MAP_FAILED; - +# ifdef _OS_OPENBSD_ // we don't set up a guard page to detect stack overflow: on OpenBSD, any // mmap-ed region has guard page managed by the kernel, so there is no // need for it. Additionally, a memory region used as stack (memory // allocated with MAP_STACK option) has strict permission, and you can't // "create" a guard page on such memory by using `mprotect` on it - - jl_atomic_fetch_add_relaxed(&num_stack_mappings, 1); - return stk; -} + void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (stk == MAP_FAILED) + return MAP_FAILED; # else -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; -#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 +# endif jl_atomic_fetch_add_relaxed(&num_stack_mappings, 1); return stk; } -# endif static void free_stack(void *stkbuf, 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; - stkbuf = (char *)stkbuf - guard_size; -#endif - munmap(stkbuf, bufsz); jl_atomic_fetch_add_relaxed(&num_stack_mappings, -1); } From da3468c1208b087161af5b69a26a92a91967a367 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Aug 2024 14:45:01 -0400 Subject: [PATCH 136/548] add pending state back to jl_thread_suspend_and_get_state-machine (#55622) Fixes an issue with #55500, where signals may abruptly abort the process as they observe it is still processing the resume SIGUSR2 message and are not able to wait for that processing to end before setting the new message to exit. --- src/signals-unix.c | 65 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 005422bea03d3..d719ac7fa452d 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -448,6 +448,7 @@ static int signal_caught_cond = -1; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { + int err; pthread_mutex_lock(&in_signal_lock); jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; @@ -456,22 +457,51 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - sig_atomic_t request = 0; - if (!jl_atomic_cmpswap(&ptls2->signal_request, &request, 1)) { + if (jl_atomic_load(&ptls2->signal_request) != 0) { // something is wrong, or there is already a usr2 in flight elsewhere + // try to wait for it to finish or wait for timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + do { + err = poll(&event, 1, timeout * 1000); + } while (err == -1 && errno == EINTR); + if (err == -1 || (event.revents & POLLIN) == 0) { + // not ready after timeout: cancel this request + pthread_mutex_unlock(&in_signal_lock); + return 0; + } + } + // check for any stale signal_caught_cond events + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + do { + err = poll(&event, 1, 0); + } while (err == -1 && errno == EINTR); + if (err == -1) { pthread_mutex_unlock(&in_signal_lock); return 0; } + if ((event.revents & POLLIN) != 0) { + // consume it before continuing + eventfd_t got; + do { + err = read(signal_caught_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); (void) got; + } + sig_atomic_t request = jl_atomic_exchange(&ptls2->signal_request, 1); + assert(request == 0 || request == -1); request = 1; - int err = pthread_kill(ptls2->system_id, SIGUSR2); - // wait for thread to acknowledge or timeout - struct pollfd event = {signal_caught_cond, POLLIN, 0}; + err = pthread_kill(ptls2->system_id, SIGUSR2); if (err == 0) { + // wait for thread to acknowledge or timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; do { err = poll(&event, 1, timeout * 1000); } while (err == -1 && errno == EINTR); + if (err != 1 || (event.revents & POLLIN) == 0) + err = -1; } - if ((event.revents & POLLIN) == 0) { + if (err == -1) { // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { pthread_mutex_unlock(&in_signal_lock); @@ -487,7 +517,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) // Now the other thread is waiting on exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) request = jl_atomic_load_acquire(&ptls2->signal_request); - assert(request == 0); (void) request; + assert(request == 0 || request == -1); (void) request; jl_atomic_store_release(&ptls2->signal_request, 4); // prepare to resume normally, but later code may change this *ctx = *signal_context; return 1; @@ -546,6 +576,7 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) } // request: +// -1: processing // 0: nothing [not from here] // 1: get state & wait for request // 2: throw sigint if `!defer_signal && io_wait` or if force throw threshold @@ -561,22 +592,36 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) if (ptls == NULL) return; int errno_save = errno; - // acknowledge that we saw the signal_request - sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); + sig_atomic_t request = jl_atomic_load(&ptls->signal_request); + if (request == 0) + return; + if (!jl_atomic_cmpswap(&ptls->signal_request, &request, -1)) + return; if (request == 1) { signal_context = jl_to_bt_context(ctx); + // acknowledge that we saw the signal_request and set signal_context int err; eventfd_t got = 1; err = write(signal_caught_cond, &got, sizeof(eventfd_t)); if (err != sizeof(eventfd_t)) abort(); + sig_atomic_t processing = -1; + jl_atomic_cmpswap(&ptls->signal_request, &processing, 0); + // wait for exit signal do { err = read(exit_signal_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); assert(got == 1); - request = jl_atomic_exchange(&ptls->signal_request, 0); + request = jl_atomic_exchange(&ptls->signal_request, -1); + signal_context = NULL; assert(request == 2 || request == 3 || request == 4); } + int err; + eventfd_t got = 1; + err = write(signal_caught_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); + sig_atomic_t processing = -1; + jl_atomic_cmpswap(&ptls->signal_request, &processing, 0); if (request == 2) { int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { From fdc109088e7a25c7b412546634f2db70a2d584ad Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 30 Aug 2024 08:45:42 +0530 Subject: [PATCH 137/548] Specialize newindex for StructuredMatrix broadcasting (#55626) This provides most of the benefits seen in https://github.com/JuliaLang/julia/pull/55604. The simpler implementation appears to help with branch-prediction in inferring the zero elements of the structured matrices. The improved performance as a consequence: ```julia julia> using LinearAlgebra julia> U = UpperTriangular(rand(3000,3000)); D = Diagonal(rand(size(U,1))); julia> @btime $U .+ $D; 23.405 ms (3 allocations: 68.66 MiB) # nightly 15.266 ms (3 allocations: 68.66 MiB) # This PR ``` --------- Co-authored-by: Matt Bauman --- stdlib/LinearAlgebra/src/structuredbroadcast.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index 21f6a7414d872..3c60b37959f91 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -199,6 +199,8 @@ function Broadcast.newindex(A::StructuredMatrix, b::BandIndex) # and we apply newindex to both the axes at once to obtain the result size(A,1) > 1 ? b : BandIndex(0, 1) end +# All structured matrices are square, and therefore they only broadcast out if they are size (1, 1) +Broadcast.newindex(D::StructuredMatrix, I::CartesianIndex{2}) = size(D) == (1,1) ? CartesianIndex(1,1) : I function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) From ed084578bf171e223838dd62e91cec315ab2c29a Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Fri, 30 Aug 2024 14:23:08 +0200 Subject: [PATCH 138/548] Document how to set the threadpool sizes with `JULIA_NUM_THREADS` (#55425) Fixes #48960, fixes #50936. --- doc/src/manual/environment-variables.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index 30f2263904f40..1fb11018a22e7 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -328,16 +328,25 @@ a master process to establish a connection before dying. ### [`JULIA_NUM_THREADS`](@id JULIA_NUM_THREADS) -An unsigned 64-bit integer (`uint64_t`) that sets the maximum number of threads -available to Julia. If `$JULIA_NUM_THREADS` is not positive or is not set, or -if the number of CPU threads cannot be determined through system calls, then the -number of threads is set to `1`. +An unsigned 64-bit integer (`uint64_t`) or string that sets the maximum number +of threads available to Julia. If `$JULIA_NUM_THREADS` is not set or is a +non-positive integer, or if the number of CPU threads cannot be determined +through system calls, then the number of threads is set to `1`. If `$JULIA_NUM_THREADS` is set to `auto`, then the number of threads will be set -to the number of CPU threads. +to the number of CPU threads. It can also be set to a comma-separated string to +specify the size of the `:default` and `:interactive` [threadpools](@ref +man-threadpools), respectively: +```bash +# 5 threads in the :default pool and 2 in the :interactive pool +export JULIA_NUM_THREADS=5,2 + +# `auto` threads in the :default pool and 1 in the :interactive pool +export JULIA_NUM_THREADS=auto,1 +``` !!! note - `JULIA_NUM_THREADS` must be defined before starting julia; defining it in + `JULIA_NUM_THREADS` must be defined before starting Julia; defining it in `startup.jl` is too late in the startup process. !!! compat "Julia 1.5" @@ -347,6 +356,9 @@ to the number of CPU threads. !!! compat "Julia 1.7" The `auto` value for `$JULIA_NUM_THREADS` requires Julia 1.7 or above. +!!! compat "Julia 1.9" + The `x,y` format for threadpools requires Julia 1.9 or above. + ### [`JULIA_THREAD_SLEEP_THRESHOLD`](@id JULIA_THREAD_SLEEP_THRESHOLD) If set to a string that starts with the case-insensitive substring `"infinite"`, From 1e21727943915bba7701c668d89aece8a9bdcccb Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 30 Aug 2024 15:37:00 +0200 Subject: [PATCH 139/548] Homogenize the progress bar in Pkg and base package precompilation (#55588) - Fix a missing check to `always_reprint` that has been fixed in Pkg - The header was in `light_green` while in Pkg it was `green`. - The progress bar for downloading packages and artifacts was in green while compiling package was in blue. - The progress bar is now attached to the "Precompiling packages" header like for artifact and package downloads. Co-authored-by: KristofferC --- base/precompilation.jl | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 6997ce12c8d01..32fdb86bbdb07 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -285,7 +285,7 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere return end t = time() - if p.has_shown && (t - p.time_shown) < PROGRESS_BAR_TIME_GRANULARITY[] + if !p.always_reprint && p.has_shown && (t - p.time_shown) < PROGRESS_BAR_TIME_GRANULARITY[] return end p.time_shown = t @@ -301,9 +301,11 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere max_progress_width = max(0, min(termwidth - textwidth(p.header) - textwidth(progress_text) - 10 , p.width)) n_filled = ceil(Int, max_progress_width * perc / 100) n_left = max_progress_width - n_filled + headers = split(p.header, ' ') to_print = sprint(; context=io) do io print(io, " "^p.indent) - printstyled(io, p.header, color=p.color, bold=true) + printstyled(io, headers[1], " "; color=:green, bold=true) + printstyled(io, join(headers[2:end], ' ')) print(io, " ") printstyled(io, "━"^n_filled; color=p.color) printstyled(io, perc >= 95 ? "━" : "╸"; color=p.color) @@ -343,7 +345,7 @@ import Base: StaleCacheKey can_fancyprint(io::IO) = io isa Base.TTY && (get(ENV, "CI", nothing) != "true") -function printpkgstyle(io, header, msg; color=:light_green) +function printpkgstyle(io, header, msg; color=:green) printstyled(io, header; color, bold=true) println(io, " ", msg) end @@ -564,9 +566,6 @@ function precompilepkgs(pkgs::Vector{String}=String[]; if !manifest if isempty(pkgs) pkgs = [pkg.name for pkg in direct_deps] - target = "all packages" - else - target = join(pkgs, ", ") end # restrict to dependencies of given packages function collect_all_deps(depsmap, dep, alldeps=Set{Base.PkgId}()) @@ -602,18 +601,16 @@ function precompilepkgs(pkgs::Vector{String}=String[]; return end end - else - target = "manifest" end nconfigs = length(configs) + target = nothing if nconfigs == 1 if !isempty(only(configs)[1]) - target *= " for configuration $(join(only(configs)[1], " "))" + target = "for configuration $(join(only(configs)[1], " "))" end - target *= "..." else - target *= " for $nconfigs compilation configurations..." + target = "for $nconfigs compilation configurations..." end @debug "precompile: packages filtered" @@ -695,15 +692,19 @@ function precompilepkgs(pkgs::Vector{String}=String[]; try wait(first_started) (isempty(pkg_queue) || interrupted_or_done.set) && return - fancyprint && lock(print_lock) do - printpkgstyle(io, :Precompiling, target) - print(io, ansi_disablecursor) + lock(print_lock) do + if target !== nothing + printpkgstyle(io, :Precompiling, target) + end + if fancyprint + print(io, ansi_disablecursor) + end end t = Timer(0; interval=1/10) anim_chars = ["◐","◓","◑","◒"] i = 1 last_length = 0 - bar = MiniProgressBar(; indent=2, header = "Progress", color = Base.info_color(), percentage=false, always_reprint=true) + bar = MiniProgressBar(; indent=0, header = "Precompiling packages ", color = :green, percentage=false, always_reprint=true) n_total = length(depsmap) * length(configs) bar.max = n_total - n_already_precomp final_loop = false @@ -832,8 +833,10 @@ function precompilepkgs(pkgs::Vector{String}=String[]; config_str = "$(join(flags, " "))" name *= color_string(" $(config_str)", :light_black) end - !fancyprint && lock(print_lock) do - isempty(pkg_queue) && printpkgstyle(io, :Precompiling, target) + lock(print_lock) do + if !fancyprint && target === nothing && isempty(pkg_queue) + printpkgstyle(io, :Precompiling, "packages...") + end end push!(pkg_queue, pkg_config) started[pkg_config] = true From b6d2155e65bbbf511a78aadf8e21fcec51e525c4 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:38:36 +0200 Subject: [PATCH 140/548] add `show` methods for `ReentrantLock`, `Condition`, and `GenericCondition` (#55239) Master: ```julia julia> Condition() Condition(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.AlwaysLockedST(1)) julia> ReentrantLock() ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (34, 0, -1)) julia> Base.Semaphore(1) Base.Semaphore(1, 0, Base.GenericCondition{ReentrantLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (4613101808, 4613101840, -1)))) ``` These are pretty noisy, and adds clutter to the default `show` for any object that includes them. PR: ```julia julia> Condition() Condition() julia> ReentrantLock() ReentrantLock() (unlocked) julia> Base.Semaphore(1) Base.Semaphore(1, 0, Base.GenericCondition(ReentrantLock())) ``` Here I haven't defined a custom `show` for `Base.Semaphore`, but we can see it's printing is much cleaner thanks to the improved printing of its fields. For `ReentrantLock`, it could be potentially interesting to surface some of the fields (like `locked_by` and `havelock`) still but I'm not sure anyone looks at them via `show`, and I'm not sure if there's a good way to print it compactly. --------- Co-authored-by: Jameson Nash --- base/condition.jl | 4 ++++ base/lock.jl | 14 ++++++++++++++ test/channels.jl | 3 +++ test/copy.jl | 2 ++ test/misc.jl | 10 ++++++++++ 5 files changed, 33 insertions(+) diff --git a/base/condition.jl b/base/condition.jl index bc14b17b3ac6b..fd771c9be346a 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -69,6 +69,8 @@ struct GenericCondition{L<:AbstractLock} GenericCondition(l::AbstractLock) = new{typeof(l)}(IntrusiveLinkedList{Task}(), l) end +show(io::IO, c::GenericCondition) = print(io, GenericCondition, "(", c.lock, ")") + assert_havelock(c::GenericCondition) = assert_havelock(c.lock) lock(c::GenericCondition) = lock(c.lock) unlock(c::GenericCondition) = unlock(c.lock) @@ -194,6 +196,8 @@ This object is NOT thread-safe. See [`Threads.Condition`](@ref) for a thread-saf """ const Condition = GenericCondition{AlwaysLockedST} +show(io::IO, ::Condition) = print(io, Condition, "()") + lock(c::GenericCondition{AlwaysLockedST}) = throw(ArgumentError("`Condition` is not thread-safe. Please use `Threads.Condition` instead for multi-threaded code.")) unlock(c::GenericCondition{AlwaysLockedST}) = diff --git a/base/lock.jl b/base/lock.jl index b69f3c5c03638..b473045e5809d 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -51,6 +51,20 @@ end assert_havelock(l::ReentrantLock) = assert_havelock(l, l.locked_by) +show(io::IO, ::ReentrantLock) = print(io, ReentrantLock, "()") + +function show(io::IO, ::MIME"text/plain", l::ReentrantLock) + show(io, l) + if !(get(io, :compact, false)::Bool) + locked_by = l.locked_by + if locked_by isa Task + print(io, " (locked by ", locked_by === current_task() ? "current " : "", locked_by, ")") + else + print(io, " (unlocked)") + end + end +end + """ islocked(lock) -> Status (Boolean) diff --git a/test/channels.jl b/test/channels.jl index d62c0b581775c..eed7a7ecc0566 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -12,6 +12,9 @@ using Base: n_avail end @test wait(a) == "success" @test fetch(t) == "finished" + + # Test printing + @test repr(a) == "Condition()" end @testset "wait first behavior of wait on Condition" begin diff --git a/test/copy.jl b/test/copy.jl index d2f555604c4d8..559bf5d3e757a 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -282,6 +282,8 @@ end @testset "`deepcopy` a `GenericCondition`" begin a = Base.GenericCondition(ReentrantLock()) + # Test printing + @test repr(a) == "Base.GenericCondition(ReentrantLock())" @test !islocked(a.lock) lock(a.lock) @test islocked(a.lock) diff --git a/test/misc.jl b/test/misc.jl index 87605d685fb3e..66b70956935cd 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -159,6 +159,16 @@ let @test @lock(lockable2, lockable2[]["foo"]) == "hello" end +@testset "`show` for ReentrantLock" begin + l = ReentrantLock() + @test repr(l) == "ReentrantLock()" + @test repr("text/plain", l) == "ReentrantLock() (unlocked)" + @lock l begin + @test startswith(repr("text/plain", l), "ReentrantLock() (locked by current Task (") + end + @test repr("text/plain", l) == "ReentrantLock() (unlocked)" +end + for l in (Threads.SpinLock(), ReentrantLock()) @test get_finalizers_inhibited() == 0 @test lock(get_finalizers_inhibited, l) == 1 From 4c2f728a9976a5651acfe2f7eba703e6d0b64562 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Fri, 30 Aug 2024 10:29:56 -0700 Subject: [PATCH 141/548] Call `pthread_attr_init` in `jl_init_stack_limits` (#55594) PR 55515 seems to have introduced segfaults on FreeBSD during the `atexit` and `ccall` tests. Prior to that PR, we had been calling `pthread_attr_init` in `jl_init_stack_limits` on FreeBSD. According to the [manual page](https://man.freebsd.org/cgi/man.cgi?query=pthread_attr_get_np&apropos=0&sektion=3&manpath=FreeBSD+13.2-RELEASE&arch=default&format=html) for `pthread_attr_get_np`, it is "HIGHLY RECOMMENDED" to use `pthread_attr_init` to allocate storage. Might as well put it back then, as it fixes the segfaults. --- src/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/init.c b/src/init.c index 1d466a0a736f9..86c0877b14289 100644 --- a/src/init.c +++ b/src/init.c @@ -67,6 +67,7 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi) # if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) pthread_attr_t attr; #if defined(_OS_FREEBSD_) + pthread_attr_init(&attr); pthread_attr_get_np(pthread_self(), &attr); #else pthread_getattr_np(pthread_self(), &attr); From e22e4de5de786778b48650f2929fc6f2172d5b81 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 30 Aug 2024 22:18:25 -0400 Subject: [PATCH 142/548] Fix an assertion in new binding code (#55636) This snuck by me late in the new bindings PR and I didn't notice the assert builder handn't finished yet. --- src/module.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/module.c b/src/module.c index 7f03fc7e66a30..96d94049cff13 100644 --- a/src/module.c +++ b/src/module.c @@ -384,7 +384,7 @@ static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROO static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_t world) { jl_ptr_kind_union_t owner_pku = jl_atomic_load_relaxed(&owner->restriction); - assert(decode_restriction_kind(owner_pku) == BINDING_KIND_GLOBAL || + assert(decode_restriction_kind(owner_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(owner_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(owner_pku))); jl_binding_partition_t *alias_bpart = jl_get_binding_partition(alias, world); if (owner == alias_bpart) @@ -419,7 +419,7 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl continue; jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); jl_ptr_kind_union_t tempb_pku = jl_atomic_load_relaxed(&tempbpart->restriction); - assert(decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); + assert(decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(tempb_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); (void)tempb_pku; if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, jl_current_task->world_age)) { if (warn) { @@ -663,7 +663,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, else { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(pku))); + assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(pku))); (void)pku; if (b->deprecated) { if (jl_get_binding_value(b) == jl_nothing) { From 0622123121a33668f4dc771a6183b95fff533a53 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Sat, 31 Aug 2024 13:37:48 +0200 Subject: [PATCH 143/548] fixes/cleanup for use-system-unwind make flags (#55639) Assuming non-windows and libunwind not disabled: The flag `-DLLVMLIBUNWIND` is currently set on macos only for `USE_SYSTEM_UNWIND=0` which seems wrong to me and causes build issues for macos on Yggdrasil in combination with the recent https://github.com/JuliaLang/julia/pull/55049 which should only affect gnu libunwind (`error: call to undeclared function 'unw_ensure_tls'`). This flag is now set independently of the system-libunwind flag (on Darwin and OpenBSD as before). `LIBUNWIND=-lunwind` is set for `USE_SYSTEM_UNWIND=0` || `USE_SYSTEM_UNWIND=1` && `OS != Darwin`. I don't think the check for Darwin make sense and might be a leftover from using osxunwind a (long) while ago. Changed that to always set `-lunwind` if enabled. x-ref: https://github.com/JuliaPackaging/Yggdrasil/pull/9331 --- Make.inc | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Make.inc b/Make.inc index 0da638cfab52e..f078a0c84f806 100644 --- a/Make.inc +++ b/Make.inc @@ -1095,20 +1095,13 @@ LIBUNWIND:= else ifneq ($(DISABLE_LIBUNWIND), 0) LIBUNWIND:= else -ifeq ($(USE_SYSTEM_LIBUNWIND), 1) -ifneq ($(OS),Darwin) LIBUNWIND:=-lunwind -# Only for linux since we want to use not yet released libunwind features -JCFLAGS+=-DSYSTEM_LIBUNWIND -JCPPFLAGS+=-DSYSTEM_LIBUNWIND -endif -else ifneq ($(findstring $(OS),Darwin OpenBSD),) -LIBUNWIND:=-lunwind JCPPFLAGS+=-DLLVMLIBUNWIND -else -LIBUNWIND:=-lunwind -endif +else ifeq ($(USE_SYSTEM_LIBUNWIND), 1) +# Only for linux and freebsd since we want to use not yet released gnu libunwind features +JCFLAGS+=-DSYSTEM_LIBUNWIND +JCPPFLAGS+=-DSYSTEM_LIBUNWIND endif endif From ca72e28f26b7e2fb617ce8f83fd82ade62007e07 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 31 Aug 2024 13:47:48 +0200 Subject: [PATCH 144/548] allow `-m` to run the entry point in a submodule (#55265) --- base/client.jl | 4 ++-- test/loading.jl | 1 + test/project/Rot13/src/Rot13.jl | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/base/client.jl b/base/client.jl index 0290d27b09cf0..2ca88c40aeb7e 100644 --- a/base/client.jl +++ b/base/client.jl @@ -292,12 +292,12 @@ function exec_options(opts) invokelatest(show, Core.eval(Main, parse_input_line(arg))) println() elseif cmd == 'm' - @eval Main import $(Symbol(arg)).main + entrypoint = push!(split(arg, "."), "main") + Base.eval(Main, Expr(:import, Expr(:., Symbol.(entrypoint)...))) if !should_use_main_entrypoint() error("`main` in `$arg` not declared as entry point (use `@main` to do so)") end return false - elseif cmd == 'L' # load file immediately on all processors if !distributed_mode diff --git a/test/loading.jl b/test/loading.jl index 51e0c45d2faf1..fe6f800276547 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1584,6 +1584,7 @@ end @testset "-m" begin rot13proj = joinpath(@__DIR__, "project", "Rot13") @test readchomp(`$(Base.julia_cmd()) --startup-file=no --project=$rot13proj -m Rot13 --project nowhere ABJURER`) == "--cebwrpg abjurer NOWHERE " + @test readchomp(`$(Base.julia_cmd()) --startup-file=no --project=$rot13proj -m Rot13.Rot26 --project nowhere ABJURER`) == "--project nowhere ABJURER " end @testset "workspace loading" begin diff --git a/test/project/Rot13/src/Rot13.jl b/test/project/Rot13/src/Rot13.jl index 1d19cbbe6df91..66f077812d878 100644 --- a/test/project/Rot13/src/Rot13.jl +++ b/test/project/Rot13/src/Rot13.jl @@ -12,4 +12,17 @@ function (@main)(args) return 0 end +module Rot26 # LOL + +import ..rot13 + +rot26(str::AbstractString) = map(rot13 ∘ rot13, str) + +function (@main)(args) + foreach(arg -> print(rot26(arg), " "), args) + return 0 +end + +end + end # module Rot13 From 42d00107b0b3eb1a77dbefc1aea77c53480bf259 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:16:47 -0400 Subject: [PATCH 145/548] Document and test `Base.rename` (#55503) Follow up of #55384 --- base/file.jl | 17 ++- test/file.jl | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 314 insertions(+), 5 deletions(-) diff --git a/base/file.jl b/base/file.jl index 81bca9dd65577..d45f34fb55dc6 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1183,15 +1183,24 @@ function unlink(p::AbstractString) end """ - rename(oldpath::AbstractString, newpath::AbstractString) + Base.rename(oldpath::AbstractString, newpath::AbstractString) -Change the name of a file from `oldpath` to `newpath`. If `newpath` is an existing file it may be replaced. -Equivalent to [rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html). -Throws an `IOError` on failure. +Change the name of a file or directory from `oldpath` to `newpath`. +If `newpath` is an existing file or empty directory it may be replaced. +Equivalent to [rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html) on Unix. +If a path contains a "\\0" throw an `ArgumentError`. +On other failures throw an `IOError`. Return `newpath`. OS-specific restrictions may apply when `oldpath` and `newpath` are in different directories. +Currently there are a few differences in behavior on Windows which may be resolved in a future release. +Specifically, currently on Windows: +1. `rename` will fail if `oldpath` or `newpath` are opened files. +2. `rename` will fail if `newpath` is an existing directory. +3. `rename` may work if `newpath` is a file and `oldpath` is a directory. +4. `rename` may remove `oldpath` if it is a hardlink to `newpath`. + See also: [`mv`](@ref). """ function rename(oldpath::AbstractString, newpath::AbstractString) diff --git a/test/file.jl b/test/file.jl index 4531cd8e66998..de6d488056a02 100644 --- a/test/file.jl +++ b/test/file.jl @@ -823,6 +823,303 @@ mktempdir() do tmpdir rm(b_tmpdir) end +@testset "rename" begin + # some of the windows specific behavior may be fixed in new versions of julia + mktempdir() do dir + # see if can make symlinks + local can_symlink = try + symlink("foo", joinpath(dir, "link")) + rm(joinpath(dir, "link")) + true + catch + false + end + local f1 = joinpath(dir, "file1") + local f2 = joinpath(dir, "file2") + local d1 = joinpath(dir, "dir1") + local d2 = joinpath(dir, "dir2") + local subd1f1 = joinpath(d1, "file1") + local subd1f2 = joinpath(d1, "file2") + local subd2f1 = joinpath(d2, "file1") + local subd2f2 = joinpath(d2, "file2") + local h1 = joinpath(dir, "hlink1") + local h2 = joinpath(dir, "hlink2") + local s1 = joinpath(dir, "slink1") + local s2 = joinpath(dir, "slink2") + @testset "renaming to non existing newpath in same directory" begin + # file, make sure isexecutable is copied + for mode in (0o644, 0o755) + write(f1, b"data") + chmod(f1, mode) + Base.rename(f1, f2) + @test !isfile(f1) + @test isfile(f2) + @test read(f2) == b"data" + if mode == 0o644 + @test !isexecutable(f2) + else + @test isexecutable(f2) + end + rm(f2) + end + # empty directory + mkdir(d1) + Base.rename(d1, d2) + @test !isdir(d1) + @test isdir(d2) + @test isempty(readdir(d2)) + rm(d2) + # non empty directory + mkdir(d1) + write(subd1f1, b"data") + chmod(subd1f1, 0o644) + write(subd1f2, b"exe") + chmod(subd1f2, 0o755) + Base.rename(d1, d2) + @test !isdir(d1) + @test isdir(d2) + @test read(subd2f1) == b"data" + @test read(subd2f2) == b"exe" + @test !isexecutable(subd2f1) + @test isexecutable(subd2f2) + rm(d2; recursive=true) + # hardlink + write(f1, b"data") + hardlink(f1, h1) + Base.rename(h1, h2) + @test isfile(f1) + @test !isfile(h1) + @test isfile(h2) + @test read(h2) == b"data" + write(h2, b"data2") + @test read(f1) == b"data2" + rm(h2) + rm(f1) + # symlink + if can_symlink + symlink("foo", s1) + Base.rename(s1, s2) + @test !islink(s1) + @test islink(s2) + @test readlink(s2) == "foo" + rm(s2) + end + end + @test isempty(readdir(dir)) # make sure everything got cleaned up + + # Get the error code from failed rename, or nothing if it worked + function rename_errorcodes(oldpath, newpath) + try + Base.rename(oldpath, newpath) + nothing + catch e + e.code + end + end + @testset "errors" begin + # invalid paths + @test_throws ArgumentError Base.rename(f1*"\0", "") + @test Base.UV_ENOENT == rename_errorcodes("", "") + write(f1, b"data") + @test Base.UV_ENOENT == rename_errorcodes(f1, "") + @test read(f1) == b"data" + @test Base.UV_ENOENT == rename_errorcodes("", f1) + @test read(f1) == b"data" + @test Base.UV_ENOENT == rename_errorcodes(f2, f1) + @test read(f1) == b"data" + @test Base.UV_ENOENT == rename_errorcodes(f1, subd1f1) + @test read(f1) == b"data" + rm(f1) + # attempt to make a directory a subdirectory of itself + mkdir(d1) + if Sys.iswindows() + @test rename_errorcodes(d1, joinpath(d1, "subdir")) ∈ (Base.UV_EINVAL, Base.UV_EBUSY) + else + @test Base.UV_EINVAL == rename_errorcodes(d1, joinpath(d1, "subdir")) + end + rm(d1) + # rename to child of a file + mkdir(d1) + write(f2, "foo") + if Sys.iswindows() + @test Base.UV_EINVAL == rename_errorcodes(d1, joinpath(f2, "subdir")) + else + @test Base.UV_ENOTDIR == rename_errorcodes(d1, joinpath(f2, "subdir")) + end + # replace a file with a directory + if !Sys.iswindows() + @test Base.UV_ENOTDIR == rename_errorcodes(d1, f2) + else + # this should work on windows + Base.rename(d1, f2) + @test isdir(f2) + @test !ispath(d1) + end + rm(f2; force=true) + rm(d1; force=true) + # symlink loop + if can_symlink + symlink(s1, s2) + symlink(s2, s1) + @test Base.UV_ELOOP == rename_errorcodes(joinpath(s1, "foo"), f2) + write(f2, b"data") + @test Base.UV_ELOOP == rename_errorcodes(f2, joinpath(s1, "foo")) + rm(s1) + rm(s2) + rm(f2) + end + # newpath is a nonempty directory + mkdir(d1) + mkdir(d2) + write(subd2f1, b"data") + write(f1, b"otherdata") + if Sys.iswindows() + @test Base.UV_EACCES == rename_errorcodes(f1, d1) + @test Base.UV_EACCES == rename_errorcodes(f1, d2) + @test Base.UV_EACCES == rename_errorcodes(d1, d2) + @test Base.UV_EACCES == rename_errorcodes(subd2f1, d2) + else + @test Base.UV_EISDIR == rename_errorcodes(f1, d1) + @test Base.UV_EISDIR == rename_errorcodes(f1, d2) + @test rename_errorcodes(d1, d2) ∈ (Base.UV_ENOTEMPTY, Base.UV_EEXIST) + @test rename_errorcodes(subd2f1, d2) ∈ (Base.UV_ENOTEMPTY, Base.UV_EEXIST, Base.UV_EISDIR) + end + rm(f1) + rm(d1) + rm(d2; recursive=true) + end + @test isempty(readdir(dir)) # make sure everything got cleaned up + + @testset "replacing existing file" begin + write(f2, b"olddata") + chmod(f2, 0o755) + write(f1, b"newdata") + chmod(f1, 0o644) + @test isexecutable(f2) + @test !isexecutable(f1) + Base.rename(f1, f2) + @test !ispath(f1) + @test read(f2) == b"newdata" + @test !isexecutable(f2) + rm(f2) + end + + @testset "replacing file with itself" begin + write(f1, b"data") + Base.rename(f1, f1) + @test read(f1) == b"data" + hardlink(f1, h1) + Base.rename(f1, h1) + if Sys.iswindows() + # On Windows f1 gets deleted + @test !ispath(f1) + else + @test read(f1) == b"data" + end + @test read(h1) == b"data" + rm(h1) + rm(f1; force=true) + end + + @testset "replacing existing file in different directories" begin + mkdir(d1) + mkdir(d2) + write(subd2f2, b"olddata") + chmod(subd2f2, 0o755) + write(subd1f1, b"newdata") + chmod(subd1f1, 0o644) + @test isexecutable(subd2f2) + @test !isexecutable(subd1f1) + Base.rename(subd1f1, subd2f2) + @test !ispath(subd1f1) + @test read(subd2f2) == b"newdata" + @test !isexecutable(subd2f2) + @test isdir(d1) + @test isdir(d2) + rm(d1; recursive=true) + rm(d2; recursive=true) + end + + @testset "rename with open files" begin + # both open + write(f2, b"olddata") + write(f1, b"newdata") + open(f1) do handle1 + open(f2) do handle2 + if Sys.iswindows() + # currently this doesn't work on windows + @test Base.UV_EBUSY == rename_errorcodes(f1, f2) + else + Base.rename(f1, f2) + @test !ispath(f1) + @test read(f2) == b"newdata" + end + # rename doesn't break already opened files + @test read(handle1) == b"newdata" + @test read(handle2) == b"olddata" + end + end + rm(f1; force=true) + rm(f2; force=true) + + # oldpath open + write(f2, b"olddata") + write(f1, b"newdata") + open(f1) do handle1 + if Sys.iswindows() + # currently this doesn't work on windows + @test Base.UV_EBUSY == rename_errorcodes(f1, f2) + else + Base.rename(f1, f2) + @test !ispath(f1) + @test read(f2) == b"newdata" + end + # rename doesn't break already opened files + @test read(handle1) == b"newdata" + end + rm(f1; force=true) + rm(f2; force=true) + + # newpath open + write(f2, b"olddata") + write(f1, b"newdata") + open(f2) do handle2 + if Sys.iswindows() + # currently this doesn't work on windows + @test Base.UV_EACCES == rename_errorcodes(f1, f2) + else + Base.rename(f1, f2) + @test !ispath(f1) + @test read(f2) == b"newdata" + end + # rename doesn't break already opened files + @test read(handle2) == b"olddata" + end + rm(f1; force=true) + rm(f2; force=true) + end + + @testset "replacing empty directory with directory" begin + mkdir(d1) + mkdir(d2) + write(subd1f1, b"data") + if Sys.iswindows() + # currently this doesn't work on windows + @test Base.UV_EACCES == rename_errorcodes(d1, d2) + rm(d1; recursive=true) + rm(d2) + else + Base.rename(d1, d2) + @test isdir(d2) + @test read(subd2f1) == b"data" + @test !ispath(d1) + rm(d2; recursive=true) + end + end + @test isempty(readdir(dir)) # make sure everything got cleaned up + end +end + # issue #10506 #10434 ## Tests for directories and links to directories if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER @@ -1472,7 +1769,7 @@ rm(dir) ################## -# Return values of mkpath, mkdir, cp, mv and touch +# Return values of mkpath, mkdir, cp, mv, rename and touch #################### mktempdir() do dir name1 = joinpath(dir, "apples") @@ -1489,6 +1786,9 @@ mktempdir() do dir @test cp(name2, name1) == name1 @test isfile(name1) @test isfile(name2) + @test Base.rename(name1, name2) == name2 + @test !ispath(name1) + @test isfile(name2) namedir = joinpath(dir, "chalk") namepath = joinpath(dir, "chalk", "cheese", "fresh") @test !ispath(namedir) From 3b97dda8a59bcf3f776b32a3a305cce22ee31625 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sat, 31 Aug 2024 11:14:02 -0400 Subject: [PATCH 146/548] Even MORE tests for transcode --- test/strings/basic.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index a7266f52f16fc..874607f3c1b20 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1398,9 +1398,14 @@ end str_2 = "αβγ" # string starting with a 3 byte UTF-8 character str_3 = "आख" - @testset for str in (str_1, str_2, str_3) + # string starting with a 4 byte UTF-8 character + str_4 = "𒃵𒃰" + @testset for str in (str_1, str_2, str_3, str_4) + @test transcode(String, str) === str @test transcode(String, transcode(UInt16, str)) == str @test transcode(String, transcode(UInt16, transcode(UInt8, str))) == str + @test transcode(String, transcode(Int32, transcode(UInt8, str))) == str + @test transcode(String, transcode(UInt32, transcode(UInt8, str))) == str @test transcode(String, transcode(UInt8, transcode(UInt16, str))) == str end end From 9e07d9100b3259c20cad99b952daaa6138eeaf3e Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sat, 31 Aug 2024 13:57:42 -0400 Subject: [PATCH 147/548] Move lets to testsets and add codeunits tests --- test/strings/types.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/strings/types.jl b/test/strings/types.jl index dbcf65b1d843b..d1e89e0e85196 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -302,8 +302,7 @@ end ## Cstring tests ## -# issue #13974: comparison against pointers -let +@testset "issue #13974: comparison against pointers" begin str = String("foobar") ptr = pointer(str) cstring = Cstring(ptr) @@ -324,10 +323,15 @@ let @test C_NULL != cstring end -# issue #31381: eltype(Cstring) != Cchar -let +@testset "issue #31381: eltype(Cstring) != Cchar" begin s = Cstring(C_NULL) @test eltype(Cstring) == Cchar @test eltype(s) == Cchar @test pointer(s) isa Ptr{Cchar} end + +@testset "Codeunits" begin + s = "I'm a string!" + @test codeunit(s) == UInt8 + @test codeunit(s, Int8(1)) == codeunit(s, 1) +end From 66349d411efd36211524da1741df129fd504b658 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sat, 31 Aug 2024 14:17:04 -0400 Subject: [PATCH 148/548] Few more missing AnnotatedStrings tests --- test/strings/annotated.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index 90aaadd6ede24..ee53c3d5846eb 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -5,14 +5,22 @@ @test str == Base.AnnotatedString(str.string, Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]) @test length(str) == 11 @test ncodeunits(str) == 11 + @test codeunits(str) == codeunits("some string") + @test codeunit(str) == UInt8 + @test codeunit(str, 1) == codeunit("some string", 1) + @test firstindex(str) == firstindex("some string") @test convert(Base.AnnotatedString, str) === str @test eltype(str) == Base.AnnotatedChar{eltype(str.string)} @test first(str) == Base.AnnotatedChar(first(str.string), Pair{Symbol, Any}[]) @test str[1:4] isa SubString{typeof(str)} @test str[1:4] == Base.AnnotatedString("some") + big_byte_str = Base.AnnotatedString("आख") + @test_throws StringIndexError big_byte_str[5] @test "a" * str == Base.AnnotatedString("asome string") @test str * "a" == Base.AnnotatedString("some stringa") @test str * str == Base.AnnotatedString("some stringsome string") + @test cmp(str, "some stringy thingy") == -1 + @test cmp("some stringy thingy", str) == 1 @test str[3:4] == SubString("me") @test SubString("me") == str[3:4] Base.annotate!(str, 1:4, :thing => 0x01) From d81a45bf4e74abec2f4ff3a9fe435d8f64100b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 31 Aug 2024 22:22:26 +0200 Subject: [PATCH 149/548] [LibUV_jll] Update to new build (#55647) Corresponding PR to Yggdrasil: https://github.com/JuliaPackaging/Yggdrasil/pull/9337. This build includes backports of https://github.com/libuv/libuv/pull/4278 (useful for for #46226) and https://github.com/libuv/libuv/pull/4521 (useful for #55592) --- deps/checksums/libuv | 68 +++++++++++++++++------------------ deps/libuv.version | 4 ++- stdlib/LibUV_jll/Project.toml | 2 +- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/deps/checksums/libuv b/deps/checksums/libuv index 41a9a5bdf9722..c2c86a9767463 100644 --- a/deps/checksums/libuv +++ b/deps/checksums/libuv @@ -1,34 +1,34 @@ -LibUV.v2.0.1+16.aarch64-apple-darwin.tar.gz/md5/132266a501144f34eb9b8d5199db43c0 -LibUV.v2.0.1+16.aarch64-apple-darwin.tar.gz/sha512/e466ba8a2fe916f0e2dccb1d1075a6a20fcc5d5068d2375c940353a63522332fa8f665461adbb47ad4d30dabaea011b8e72a603601da29a071d98c7d7d130f46 -LibUV.v2.0.1+16.aarch64-linux-gnu.tar.gz/md5/1ae3018d9ab8bb293dbf6277c2c209cc -LibUV.v2.0.1+16.aarch64-linux-gnu.tar.gz/sha512/6e56876cdf0fdad1aade6435edf980b286438ee9fa695fa4e262b47f7ada6ff69535c59d216daee3eb1d061a90c2c16fd70d21438776c54addda93cf275ef1be -LibUV.v2.0.1+16.aarch64-linux-musl.tar.gz/md5/08243e727c7e957f5972a200b5d89113 -LibUV.v2.0.1+16.aarch64-linux-musl.tar.gz/sha512/4a684f248704b16b882d66ed7af60e2217a0b98f476bfdd1cb545d3e2adb17f6a410bf09e270c1e2623e550b36639c9282a562ab415850dfea98736ec03fd000 -LibUV.v2.0.1+16.armv6l-linux-gnueabihf.tar.gz/md5/c4dfccf5a899782715cbb0ca0197938c -LibUV.v2.0.1+16.armv6l-linux-gnueabihf.tar.gz/sha512/ecdcd655865a532187e4e98cb21ca68e62303813cad585de83382aa226d965213f24fe7a684e1189fad11b0e5f2f4b318c122f557a6117f61bb2948b51e16a76 -LibUV.v2.0.1+16.armv6l-linux-musleabihf.tar.gz/md5/5382dae963f3003aefdb119377a45e82 -LibUV.v2.0.1+16.armv6l-linux-musleabihf.tar.gz/sha512/f901c2965e8f9ca52900180c32cdb70d8adc13f12f076c1b109d57b749cac1ecaac3c72e22531e6fcb79c8f2c7cf952ff563779d3764b015b73db079f2b171cb -LibUV.v2.0.1+16.armv7l-linux-gnueabihf.tar.gz/md5/9c4cd82249c03ebeac670e2c7c8c1078 -LibUV.v2.0.1+16.armv7l-linux-gnueabihf.tar.gz/sha512/ee4b7f866e3f63df303d00d48d36680c490570979bb7174c12cfcf9efaf48ea7ae90aa05b41da8ab686de93c910c5a761f31da22845ad48fd980e9c16437cbfb -LibUV.v2.0.1+16.armv7l-linux-musleabihf.tar.gz/md5/5255d7e320ef37eb63d0e85c4b86d20d -LibUV.v2.0.1+16.armv7l-linux-musleabihf.tar.gz/sha512/5bcd3d22b1e2398879e654bb550fd093891775c64cb48bd179c4f9ff8dcbff23eda91a66ea14852ef5945d5c114732957075e3b3fded4cbd3cca559fead842db -LibUV.v2.0.1+16.i686-linux-gnu.tar.gz/md5/7f0fc52beb13dad773c6ab54deee7a62 -LibUV.v2.0.1+16.i686-linux-gnu.tar.gz/sha512/cb1736eab4fa1be89018b3c77c3551a99d0fa761ad2f1947587c215d87d963d43198ce87574b6eb9d1fb8a93abf1ae89e74fb8a3f3fb9c4fd08a49e04b4335f4 -LibUV.v2.0.1+16.i686-linux-musl.tar.gz/md5/ed22ccd7eaa09ed9c71afc0c6affa423 -LibUV.v2.0.1+16.i686-linux-musl.tar.gz/sha512/7f3ff061c3d7d0c3c0c0be3e4052aeed39f35e1ba0b92f3ee3d9f266f26d064acc153c08054a22d090167f00fef3c27ec54e836de35f348e4849baab301f7fa4 -LibUV.v2.0.1+16.i686-w64-mingw32.tar.gz/md5/7f1fe93df0b741ca30c4fb64ff9ac9bd -LibUV.v2.0.1+16.i686-w64-mingw32.tar.gz/sha512/9d71722c538d8232d8510fa2a43e7a52271b078401dfa838de9eedcfc34a2483aa3b1c221b17c41353b54554fe76d86b4973c5261b288228a91f0cc92820ad93 -LibUV.v2.0.1+16.powerpc64le-linux-gnu.tar.gz/md5/b796de6c75f18f318823e3e1cdd316c8 -LibUV.v2.0.1+16.powerpc64le-linux-gnu.tar.gz/sha512/f8dbb98cb49edfa06a0b48fbe1e658ca5a9bca13fe33d21872a012deaa1052a495faf74f90c0dfa48378b9f4f51f1045e01e563aec427d8c89d50e4eef0e4938 -LibUV.v2.0.1+16.x86_64-apple-darwin.tar.gz/md5/f2d55b315fa1f77b632a461530bb6b3b -LibUV.v2.0.1+16.x86_64-apple-darwin.tar.gz/sha512/eb40a193c3bca5e822a417879e854877b353a2a04b03a721ef4125360f1189a3685d2751e2f975360a2ad4c37e6043485a54b5349b3da423b8aae73d4a095d04 -LibUV.v2.0.1+16.x86_64-linux-gnu.tar.gz/md5/a573ded4f78f8677ef73594be9629638 -LibUV.v2.0.1+16.x86_64-linux-gnu.tar.gz/sha512/c5809635be3ab5dc53c37a028e58695d89ea91eee850af22a0e8db10ea021640f1e618a553848332ee6df66eecd08d34605e335aad46ece82365a3525b69c42f -LibUV.v2.0.1+16.x86_64-linux-musl.tar.gz/md5/5bdad561b5db7d19f198ef090ae3ec84 -LibUV.v2.0.1+16.x86_64-linux-musl.tar.gz/sha512/6662c8226f22f79f8c40857a5a531841f013031dd2e9536568498bfd536f133976ff71d0cc5f56f1e0c0b7f2403a35c2ccef9117d9e0d7819771bd492194f20d -LibUV.v2.0.1+16.x86_64-unknown-freebsd.tar.gz/md5/f4ad9e445e4b14e2b59b2b77c9ed72ad -LibUV.v2.0.1+16.x86_64-unknown-freebsd.tar.gz/sha512/a78deac6d8321f274a229961620da4d069ff2accf7d1ed9edfb01c21ad47eb33d364ba2f310ff4a93b2732dcd16f6d481843dbcb273770d731fd528f9c7a9ddc -LibUV.v2.0.1+16.x86_64-w64-mingw32.tar.gz/md5/72caa067cf24e304955405dcb4de195a -LibUV.v2.0.1+16.x86_64-w64-mingw32.tar.gz/sha512/de80ca98d199d3c5626ebc771325806ce3aae5927220201c2351207c10ff67791d2865f76e41519df88f0be3da534342965e7ba0d055d807c4b2b6c78bd2427d -libuv-ca3a5a431a1c37859b6508e6b2a288092337029a.tar.gz/md5/d1fbca8bcc5819037b8b81ae4f61c357 -libuv-ca3a5a431a1c37859b6508e6b2a288092337029a.tar.gz/sha512/e735861923c0fc597b53eb2efb56b26acec29e3fcae7e76d349fc08f8b9d340df9ac60a1cd245e46a434aa357ed8e377734c1c97bf08bd044c9ba0c02b082a6a +LibUV.v2.0.1+17.aarch64-apple-darwin.tar.gz/md5/f176c76e5e2096dea8443302cf9550b8 +LibUV.v2.0.1+17.aarch64-apple-darwin.tar.gz/sha512/4301b13953a08a758b86e30af3261fd9a291ce3829b4d98e71e2a2c040e322e284c5a6eb4bc7189cc352f4b1cf7041e2cfd3380d511d88c151df3101ad74594e +LibUV.v2.0.1+17.aarch64-linux-gnu.tar.gz/md5/c81515783363702a1bd4b65fd6d7f36b +LibUV.v2.0.1+17.aarch64-linux-gnu.tar.gz/sha512/011429365337f5a45e56ca7a42709866bb994c747a1170d870f5f3ddfff2d36138866ee9278ac01172bc71bde8dee404bcb9cae9c7b44222bf1cc883659df269 +LibUV.v2.0.1+17.aarch64-linux-musl.tar.gz/md5/e74d5ea4912dd326b2705638faa7b805 +LibUV.v2.0.1+17.aarch64-linux-musl.tar.gz/sha512/a26a9f2c9051816230324071c502321f7af3885d581a400615858a93a4cd457226048d15b0e7f6a73d12659763c705b5ab519e9f5b35c6d886b9fd5babbfe352 +LibUV.v2.0.1+17.armv6l-linux-gnueabihf.tar.gz/md5/6df38bcf5d0a61dee63d16b73d0c9a24 +LibUV.v2.0.1+17.armv6l-linux-gnueabihf.tar.gz/sha512/d5354a6532061de0a58965ce0e427bde52f9ae0ee39a98e1a33de4c414fddcba9ba139ddf91be7321a4ccc97bbf7a8a8357ff10cf60f83c0a6bff7d839d6d7a8 +LibUV.v2.0.1+17.armv6l-linux-musleabihf.tar.gz/md5/6f02a24cfbfae3032fadceaea1faed39 +LibUV.v2.0.1+17.armv6l-linux-musleabihf.tar.gz/sha512/7fd107eb9a5ea84b488ea02e4fbedc9fe13bb11be859986a47af38f40ad775dd9f738c790878a3503437bcac1eb26ad9fe26f4aa0d3cb45c980b4c5abc9aec99 +LibUV.v2.0.1+17.armv7l-linux-gnueabihf.tar.gz/md5/96b09dec72f7e9b7409fa2920e67c866 +LibUV.v2.0.1+17.armv7l-linux-gnueabihf.tar.gz/sha512/6a0f79fc15c944fabba5c65180b665bc9769c6ff25863e330049f48b3a4394b448492f5a9a76bb7f8dbd3ce44dfc6f9ccdc2c71c42e1c749e88070fe99b1db69 +LibUV.v2.0.1+17.armv7l-linux-musleabihf.tar.gz/md5/f44e4b2521a813181f943895bdb0dd3c +LibUV.v2.0.1+17.armv7l-linux-musleabihf.tar.gz/sha512/cda1413dca817f772e8b343db0c6de0ef6b8f269e9a6a2ef3403c2582aeab554f46281bbb1eb4659c259198ef47fe26aab648a281e66f80aaf2f2cda0a23ac05 +LibUV.v2.0.1+17.i686-linux-gnu.tar.gz/md5/1f231d89cf9c04515d2d107a5d786cc8 +LibUV.v2.0.1+17.i686-linux-gnu.tar.gz/sha512/089cb8a372cdee0cbc0e78fc201611bb9bafd99af9a78e09d6097a6b70e7c4aa001ebd86f944b0a885c072093c529bf86bcaa32bde4fc1934407a858c1a5a764 +LibUV.v2.0.1+17.i686-linux-musl.tar.gz/md5/01cfc2a9e2536dbd330267917abb19ce +LibUV.v2.0.1+17.i686-linux-musl.tar.gz/sha512/72f3588cb464a60e61f8998242aaa2abdf93df920a2feba5e1d66ef0f2498488df0ec415cbb499d7f75c47bdfc7e3a2fdda6a94383492e0ad13e464eb1314ff8 +LibUV.v2.0.1+17.i686-w64-mingw32.tar.gz/md5/8ba829adad6a45dd71d5f25a7f958e59 +LibUV.v2.0.1+17.i686-w64-mingw32.tar.gz/sha512/dff3b1cfe54e583f8e9536ed02e56180b2cdb391bd108559ed97b2cafa743ebade9ddf04580912436e2efab747e09c79b99e33187ed67a27c5d38113373e1cec +LibUV.v2.0.1+17.powerpc64le-linux-gnu.tar.gz/md5/af0e43d9d0aa91dd82b63220d96991ef +LibUV.v2.0.1+17.powerpc64le-linux-gnu.tar.gz/sha512/9fabe3089e4fc60e910770c32d36300ce8ef36c28e8cc9c72fbecba6eb80285ee8174e84e4452fb4ce90ee7c7f94e99b03fce47d8c579bd614bfffd553f93666 +LibUV.v2.0.1+17.x86_64-apple-darwin.tar.gz/md5/871040e874eedae54553d8f1c91b9133 +LibUV.v2.0.1+17.x86_64-apple-darwin.tar.gz/sha512/d5eee08b65e4bb8b444c61ac277bec9ef944b9279dd7f0732b3cd91d47788c77938e5db71e019e01bbe7785a75df95faf14368764f700c6b7a6b9e4d96d6b4c2 +LibUV.v2.0.1+17.x86_64-linux-gnu.tar.gz/md5/d2d186952c6d017fe33f6a6bea63a3ea +LibUV.v2.0.1+17.x86_64-linux-gnu.tar.gz/sha512/15501534bf5721e6bb668aabe6dc6375349f7a284e28df0609d00982e7e456908bd6868722391afa7f44a5c82faedc8cf544f69a0e4fb9fb0d529b3ae3d44d78 +LibUV.v2.0.1+17.x86_64-linux-musl.tar.gz/md5/271d4d40a1ae53ed5b2376e5936cfcf9 +LibUV.v2.0.1+17.x86_64-linux-musl.tar.gz/sha512/1956f059ed01f66b72349d6561b04e6a89b7257c0f838d7fbdd2cee79bd126bb46b93bf944a042b5a6a235762a7a0cdd117207342dd55a0c58653a70b4a38d48 +LibUV.v2.0.1+17.x86_64-unknown-freebsd.tar.gz/md5/62fe8523948914fbe7e28bf0b8d73594 +LibUV.v2.0.1+17.x86_64-unknown-freebsd.tar.gz/sha512/e6486888028c96975f74bc9313cba9706f6bf2be085aa776c44cbb2886753b2eee62469a0be92eb0542df1d0f51db3b34c7ba5e46842e16c6ff1d20e11b75322 +LibUV.v2.0.1+17.x86_64-w64-mingw32.tar.gz/md5/541601cf27a1d28d25b9ffb00f5aed16 +LibUV.v2.0.1+17.x86_64-w64-mingw32.tar.gz/sha512/58fd5b54694dbddc2abaccfa7291955c75818cdedbd5d9057025bc3e3e6511812e7b03f71cd839c70cd39e77c5ba3e19a07dccd579e2148a2d212fa3829a9a76 +libuv-c57e7f06cbe697ca8ea9215ce054a608c451b193.tar.gz/md5/2b81dbf80d7a9fd10806d1705dbccb7f +libuv-c57e7f06cbe697ca8ea9215ce054a608c451b193.tar.gz/sha512/6796960a58031fa2bc9d72c8f35dff9f213e8d36bbc21ddb65f59bb4dfb074fc18414aece5a046a882dd08b42d5bd32277560c07a1298f68ab8bcd8aadcbf50b diff --git a/deps/libuv.version b/deps/libuv.version index bc8e2e57c9517..db6afd9f1bb51 100644 --- a/deps/libuv.version +++ b/deps/libuv.version @@ -1,7 +1,9 @@ +# -*- makefile -*- + ## jll artifact LIBUV_JLL_NAME := LibUV ## source build LIBUV_VER := 2 LIBUV_BRANCH=julia-uv2-1.48.0 -LIBUV_SHA1=ca3a5a431a1c37859b6508e6b2a288092337029a +LIBUV_SHA1=c57e7f06cbe697ca8ea9215ce054a608c451b193 diff --git a/stdlib/LibUV_jll/Project.toml b/stdlib/LibUV_jll/Project.toml index 7c61fdf89df70..0c1ad8f25f848 100644 --- a/stdlib/LibUV_jll/Project.toml +++ b/stdlib/LibUV_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUV_jll" uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+16" +version = "2.0.1+17" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 13283252747dce9081003f270de48df88d548290 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sun, 1 Sep 2024 05:31:04 +0900 Subject: [PATCH 150/548] fix new type instability from `getindex(::String, r::UnitRange{Int})` (#55625) --- base/compiler/typeinfer.jl | 5 +++-- base/strings/string.jl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index e2f2a1f2cc975..315a068e611fe 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -864,7 +864,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_output(#=incremental=#false) - add_remark!(interp, caller, "Inference is disabled for the target module") + add_remark!(interp, caller, "[typeinf_edge] Inference is disabled for the target module") return EdgeCallResult(Any, Any, nothing, Effects()) end if !is_cached(caller) && frame_parent(caller) === nothing @@ -897,7 +897,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end frame = InferenceState(result, cache_mode, interp) # always use the cache for edge targets if frame === nothing - add_remark!(interp, caller, "Failed to retrieve source") + add_remark!(interp, caller, "[typeinf_edge] Failed to retrieve source") # can't get the source for this, so we know nothing if cache_mode == CACHE_MODE_GLOBAL engine_reject(interp, ci) @@ -918,6 +918,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize return EdgeCallResult(frame.bestguess, exc_bestguess, edge, effects, volatile_inf_result) elseif frame === true # unresolvable cycle + add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle") return EdgeCallResult(Any, Any, nothing, Effects()) end # return the current knowledge about this cycle diff --git a/base/strings/string.jl b/base/strings/string.jl index f5abbead34bd1..90d6e5b26ccd3 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -208,7 +208,7 @@ end i = i′ @inbounds l = codeunit(s, i) (l < 0x80) | (0xf8 ≤ l) && return i+1 - @assert l >= 0xc0 + @assert l >= 0xc0 "invalid codeunit" end # first continuation byte (i += 1) > n && return i From 4b99e990259cbb35e1e9d8ce1ba25eea1c5d6ec5 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 1 Sep 2024 10:32:00 -0400 Subject: [PATCH 151/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=2043e7849ce=20to=20299a35610=20(#55659)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 | 1 + .../Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 | 1 + .../Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 | 1 - .../Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 create mode 100644 deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 diff --git a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 new file mode 100644 index 0000000000000..3c112b99f88d9 --- /dev/null +++ b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 @@ -0,0 +1 @@ +791c9ca37077fdc36b959a17904dd935 diff --git a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 new file mode 100644 index 0000000000000..c7c212047d2b0 --- /dev/null +++ b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 @@ -0,0 +1 @@ +96520326931685d4300e825a302010f113e942aaa55aa4ff12caf3e9df314309df993c97753ae482c2198db67678423885bf5ea40c743c8e4b6ef96d7b8d4472 diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 deleted file mode 100644 index 2d5f5888e777f..0000000000000 --- a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d992a5c629199747d68baa1593a7c37d diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 deleted file mode 100644 index 4201ee05347a7..0000000000000 --- a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -27ea738dbc4db8e4a02b00fbbdc4e2047906fe0561dd4c7f9e5ce5ea9b0b7b8ef9e29234f8e435deaa6cb3e29861130b06cb0da11118c40d78f4c475ac48db3f diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 60d2914b7f853..3d4a627d6e472 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 43e7849ce37545493d0da3226cd7449f5f88563e +PKG_SHA1 = 299a356100f54215388502148979189aff760822 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 58d5263f3d6f8177a14559f4779fcf72c1065b04 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 2 Sep 2024 08:47:59 +0530 Subject: [PATCH 152/548] Fix errant lmul! for tridiagonal and triangular (#55546) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method is incorrect. Firstly, the current implementation doesn't act in-place. Secondly, the result can't be stored in-place into a triangular matrix in general. This PR changes the implementation to convert the tridiagonal matrix to a `Diagonal` or a `Bidiagonal` one before attempting the `lmul!`. Currently, ```julia julia> T = Tridiagonal([1,2], [1,2,3], [1,2]); U = UpperTriangular(fill(2, 3, 3)); julia> lmul!(T, U) 3×3 Matrix{Int64}: 2 4 4 2 6 10 0 4 10 julia> U # not updated 3×3 UpperTriangular{Int64, Matrix{Int64}}: 2 2 2 ⋅ 2 2 ⋅ ⋅ 2 julia> parent(U) # except for the underlying storage 3×3 Matrix{Int64}: 2 2 2 0 2 2 0 0 2 ``` After this, ```julia julia> lmul!(T, U) ERROR: ArgumentError: matrix cannot be represented as Bidiagonal ``` I'm unsure if we want this method at all, since there isn't a corresponding `rmul!`, but I've left it there to avoid breakages, if any. --- stdlib/LinearAlgebra/src/triangular.jl | 2 -- stdlib/LinearAlgebra/test/triangular.jl | 2 -- 2 files changed, 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 923e13e488c85..8d32dac824ce8 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -953,8 +953,6 @@ isunit_char(::UnitUpperTriangular) = 'U' isunit_char(::LowerTriangular) = 'N' isunit_char(::UnitLowerTriangular) = 'U' -lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) - # generic fallback for AbstractTriangular matrices outside of the four subtypes provided here _trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) = lmul!(A, copyto!(C, B)) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 5f0a829f9cdda..b88be00b0209c 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -442,8 +442,6 @@ Base.getindex(A::MyTriangular, i::Int, j::Int) = A.data[i,j] debug && println("elty1: $elty1, A1: $t1, B: $eltyB") - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - @test lmul!(Tri,copy(A1)) ≈ Tri*M1 Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) C = Matrix{promote_type(elty1,eltyB)}(undef, n, n) mul!(C, Tri, A1) From 6170c4b5fe820d9651d61f7e6d3a03ac2800d755 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 2 Sep 2024 17:09:31 +0530 Subject: [PATCH 153/548] Improve type-stability in SymTridiagonal triu!/tril! (#55646) Changing the final `elseif` branch to an `else` makes it clear that the method definite returns a value, and the returned type is now a `Tridiagonal` instead of a `Union{Nothing, Tridiagonal}` --- stdlib/LinearAlgebra/src/tridiag.jl | 4 +-- stdlib/LinearAlgebra/test/tridiag.jl | 48 ++++++++++++++++++---------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 3f8eb5da9fc9d..84c79f57debc7 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -372,7 +372,7 @@ function tril!(M::SymTridiagonal{T}, k::Integer=0) where T return Tridiagonal(M.ev,M.dv,zero(M.ev)) elseif k == 0 return Tridiagonal(M.ev,M.dv,zero(M.ev)) - elseif k >= 1 + else # if k >= 1 return Tridiagonal(M.ev,M.dv,copy(M.ev)) end end @@ -391,7 +391,7 @@ function triu!(M::SymTridiagonal{T}, k::Integer=0) where T return Tridiagonal(zero(M.ev),M.dv,M.ev) elseif k == 0 return Tridiagonal(zero(M.ev),M.dv,M.ev) - elseif k <= -1 + else # if k <= -1 return Tridiagonal(M.ev,M.dv,copy(M.ev)) end end diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 759d692f8bc68..3330fa682fe5e 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -135,27 +135,43 @@ end @test_throws ArgumentError tril!(SymTridiagonal(d, dl), n) @test_throws ArgumentError tril!(Tridiagonal(dl, d, du), -n - 2) @test_throws ArgumentError tril!(Tridiagonal(dl, d, du), n) - @test tril(SymTridiagonal(d,dl)) == Tridiagonal(dl,d,zerosdl) - @test tril(SymTridiagonal(d,dl),1) == Tridiagonal(dl,d,dl) - @test tril(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,zerosd,zerosdl) - @test tril(SymTridiagonal(d,dl),-2) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test tril(Tridiagonal(dl,d,du)) == Tridiagonal(dl,d,zerosdu) - @test tril(Tridiagonal(dl,d,du),1) == Tridiagonal(dl,d,du) - @test tril(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,zerosd,zerosdu) - @test tril(Tridiagonal(dl,d,du),-2) == Tridiagonal(zerosdl,zerosd,zerosdu) + @test @inferred(tril(SymTridiagonal(d,dl))) == Tridiagonal(dl,d,zerosdl) + @test @inferred(tril(SymTridiagonal(d,dl),1)) == Tridiagonal(dl,d,dl) + @test @inferred(tril(SymTridiagonal(d,dl),-1)) == Tridiagonal(dl,zerosd,zerosdl) + @test @inferred(tril(SymTridiagonal(d,dl),-2)) == Tridiagonal(zerosdl,zerosd,zerosdl) + @test @inferred(tril(Tridiagonal(dl,d,du))) == Tridiagonal(dl,d,zerosdu) + @test @inferred(tril(Tridiagonal(dl,d,du),1)) == Tridiagonal(dl,d,du) + @test @inferred(tril(Tridiagonal(dl,d,du),-1)) == Tridiagonal(dl,zerosd,zerosdu) + @test @inferred(tril(Tridiagonal(dl,d,du),-2)) == Tridiagonal(zerosdl,zerosd,zerosdu) + @test @inferred(tril!(copy(SymTridiagonal(d,dl)))) == Tridiagonal(dl,d,zerosdl) + @test @inferred(tril!(copy(SymTridiagonal(d,dl)),1)) == Tridiagonal(dl,d,dl) + @test @inferred(tril!(copy(SymTridiagonal(d,dl)),-1)) == Tridiagonal(dl,zerosd,zerosdl) + @test @inferred(tril!(copy(SymTridiagonal(d,dl)),-2)) == Tridiagonal(zerosdl,zerosd,zerosdl) + @test @inferred(tril!(copy(Tridiagonal(dl,d,du)))) == Tridiagonal(dl,d,zerosdu) + @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),1)) == Tridiagonal(dl,d,du) + @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),-1)) == Tridiagonal(dl,zerosd,zerosdu) + @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),-2)) == Tridiagonal(zerosdl,zerosd,zerosdu) @test_throws ArgumentError triu!(SymTridiagonal(d, dl), -n) @test_throws ArgumentError triu!(SymTridiagonal(d, dl), n + 2) @test_throws ArgumentError triu!(Tridiagonal(dl, d, du), -n) @test_throws ArgumentError triu!(Tridiagonal(dl, d, du), n + 2) - @test triu(SymTridiagonal(d,dl)) == Tridiagonal(zerosdl,d,dl) - @test triu(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,d,dl) - @test triu(SymTridiagonal(d,dl),1) == Tridiagonal(zerosdl,zerosd,dl) - @test triu(SymTridiagonal(d,dl),2) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test triu(Tridiagonal(dl,d,du)) == Tridiagonal(zerosdl,d,du) - @test triu(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,d,du) - @test triu(Tridiagonal(dl,d,du),1) == Tridiagonal(zerosdl,zerosd,du) - @test triu(Tridiagonal(dl,d,du),2) == Tridiagonal(zerosdl,zerosd,zerosdu) + @test @inferred(triu(SymTridiagonal(d,dl))) == Tridiagonal(zerosdl,d,dl) + @test @inferred(triu(SymTridiagonal(d,dl),-1)) == Tridiagonal(dl,d,dl) + @test @inferred(triu(SymTridiagonal(d,dl),1)) == Tridiagonal(zerosdl,zerosd,dl) + @test @inferred(triu(SymTridiagonal(d,dl),2)) == Tridiagonal(zerosdl,zerosd,zerosdl) + @test @inferred(triu(Tridiagonal(dl,d,du))) == Tridiagonal(zerosdl,d,du) + @test @inferred(triu(Tridiagonal(dl,d,du),-1)) == Tridiagonal(dl,d,du) + @test @inferred(triu(Tridiagonal(dl,d,du),1)) == Tridiagonal(zerosdl,zerosd,du) + @test @inferred(triu(Tridiagonal(dl,d,du),2)) == Tridiagonal(zerosdl,zerosd,zerosdu) + @test @inferred(triu!(copy(SymTridiagonal(d,dl)))) == Tridiagonal(zerosdl,d,dl) + @test @inferred(triu!(copy(SymTridiagonal(d,dl)),-1)) == Tridiagonal(dl,d,dl) + @test @inferred(triu!(copy(SymTridiagonal(d,dl)),1)) == Tridiagonal(zerosdl,zerosd,dl) + @test @inferred(triu!(copy(SymTridiagonal(d,dl)),2)) == Tridiagonal(zerosdl,zerosd,zerosdl) + @test @inferred(triu!(copy(Tridiagonal(dl,d,du)))) == Tridiagonal(zerosdl,d,du) + @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),-1)) == Tridiagonal(dl,d,du) + @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),1)) == Tridiagonal(zerosdl,zerosd,du) + @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),2)) == Tridiagonal(zerosdl,zerosd,zerosdu) @test !istril(SymTridiagonal(d,dl)) @test istril(SymTridiagonal(d,zerosdl)) From ae050a674dd632b2dfe6ede45b876980b0f86457 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 2 Sep 2024 17:10:10 +0530 Subject: [PATCH 154/548] Reuse size-check function from `lacpy!` in `copytrito!` (#55664) Since there is a size-check function in `lacpy!` that does the same thing, we may reuse it instead of duplicating the check --- stdlib/LinearAlgebra/src/generic.jl | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index f60016125d2e9..e5f23b4981616 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -2014,20 +2014,12 @@ function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) m1,n1 = size(B) A = Base.unalias(B, A) 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 + LAPACK.lacpy_size_check((m1, n1), (n < m ? n : m, n)) 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 + LAPACK.lacpy_size_check((m1, n1), (m, m < n ? m : n)) for j in 1:n, i in j:m @inbounds B[i,j] = A[i,j] end From 3a2a4d8dd74d4d5780a78e059f767578bb376618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Vanuxem?= <42774576+gvanuxem@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:59:18 +0200 Subject: [PATCH 155/548] Update calling-c-and-fortran-code.md: fix ccall parameters (not a tuple) (#55665) --- doc/src/manual/calling-c-and-fortran-code.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 6f4d69b16bc81..b8d064f698208 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -996,7 +996,7 @@ A table of translations between the macro and function interfaces is given below |------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | `@ccall clock()::Int32` | `ccall(:clock, Int32, ())` | | `@ccall f(a::Cint)::Cint` | `ccall(:a, Cint, (Cint,), a)` | -| `@ccall "mylib".f(a::Cint, b::Cdouble)::Cvoid` | `ccall((:f, "mylib"), Cvoid, (Cint, Cdouble), (a, b))` | +| `@ccall "mylib".f(a::Cint, b::Cdouble)::Cvoid` | `ccall((:f, "mylib"), Cvoid, (Cint, Cdouble), a, b)` | | `@ccall $fptr.f()::Cvoid` | `ccall(fptr, f, Cvoid, ())` | | `@ccall printf("%s = %d\n"::Cstring ; "foo"::Cstring, foo::Cint)::Cint` | `` | | `@ccall printf("%s = %s\n"::Cstring ; "2 + 2"::Cstring, "5"::Cstring)::Cint` | `ccall(:printf, Cint, (Cstring, Cstring...), "%s = %s\n", "2 + 2", "5")` | From 5c706af84ab0cace899e81272258f5b3ee3eb468 Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Mon, 2 Sep 2024 16:32:52 +0200 Subject: [PATCH 156/548] Allow exact redefinition for types with recursive supertype reference (#55380) This PR allows redefining a type when the new type is exactly identical to the previous one (like #17618, #20592 and #21024), even if the type has a reference to itself in its supertype. That particular case used to error (issue #54757), whereas with this PR: ```julia julia> struct Rec <: AbstractVector{Rec} end julia> struct Rec <: AbstractVector{Rec} end # this used to error julia> ``` Fix #54757 by implementing the solution proposed there. Hence, this should also fix downstream Revise bug https://github.com/timholy/Revise.jl/issues/813. --------- Co-authored-by: N5N3 <2642243996@qq.com> --- src/builtins.c | 3 ++ src/jltypes.c | 112 +++++++++++++++++++++++++++++++++++++++++++ src/julia_internal.h | 1 + test/core.jl | 20 ++++++++ 4 files changed, 136 insertions(+) diff --git a/src/builtins.c b/src/builtins.c index 8019ee3c0e2c6..152836bcab6a9 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2197,6 +2197,9 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb) JL_GC_PUSH2(&a, &b); a = jl_rewrap_unionall((jl_value_t*)dta->super, dta->name->wrapper); b = jl_rewrap_unionall((jl_value_t*)dtb->super, dtb->name->wrapper); + // if tb recursively refers to itself in its supertype, assume that it refers to ta + // before checking whether the supertypes are equal + b = jl_substitute_datatype(b, dtb, dta); if (!jl_types_equal(a, b)) goto no; JL_TRY { diff --git a/src/jltypes.c b/src/jltypes.c index adf39162cc7f0..fbc8e9f7f7f16 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1607,6 +1607,118 @@ jl_value_t *jl_rewrap_unionall_(jl_value_t *t, jl_value_t *u) return t; } +// Create a copy of type expression t where any occurrence of data type x is replaced by y. +// If x does not occur in t, return t without any copy. +// For example, jl_substitute_datatype(Foo{Bar}, Foo{T}, Qux{S}) is Qux{Bar}, with T and S +// free type variables. +// To substitute type variables, use jl_substitute_var instead. +jl_value_t *jl_substitute_datatype(jl_value_t *t, jl_datatype_t * x, jl_datatype_t * y) +{ + if jl_is_datatype(t) { + jl_datatype_t *typ = (jl_datatype_t*)t; + // For datatypes call itself recursively on the parameters to form new parameters. + // Then, if typename(t) == typename(x), rewrap the wrapper of y around the new + // parameters. Otherwise, do the same around the wrapper of t. + // This ensures that the types and supertype are properly set. + // Start by check whether there is a parameter that needs replacing. + long i_firstnewparam = -1; + size_t nparams = jl_svec_len(typ->parameters); + jl_value_t *firstnewparam = NULL; + JL_GC_PUSH1(&firstnewparam); + for (size_t i = 0; i < nparams; i++) { + jl_value_t *param = NULL; + JL_GC_PUSH1(¶m); + param = jl_svecref(typ->parameters, i); + firstnewparam = jl_substitute_datatype(param, x, y); + if (param != firstnewparam) { + i_firstnewparam = i; + JL_GC_POP(); + break; + } + JL_GC_POP(); + } + // If one of the parameters needs to be updated, or if the type name is that to + // substitute, create a new datataype + if (i_firstnewparam != -1 || typ->name == x->name) { + jl_datatype_t *uw = typ->name == x->name ? y : typ; // substitution occurs here + jl_value_t *wrapper = uw->name->wrapper; + jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(wrapper); + jl_svec_t *sv = jl_alloc_svec_uninit(jl_svec_len(uw->parameters)); + JL_GC_PUSH1(&sv); + jl_value_t **vals = jl_svec_data(sv); + // no JL_GC_PUSHARGS(vals, ...) since GC is already aware of sv + for (long i = 0; i < i_firstnewparam; i++) { // copy the identical parameters + vals[i] = jl_svecref(typ->parameters, i); // value + } + if (i_firstnewparam != -1) { // insert the first non-identical parameter + vals[i_firstnewparam] = firstnewparam; + } + for (size_t i = i_firstnewparam+1; i < nparams; i++) { // insert the remaining parameters + vals[i] = jl_substitute_datatype(jl_svecref(typ->parameters, i), x, y); + } + if (jl_is_tuple_type(wrapper)) { + // special case for tuples, since the wrapper (Tuple) does not have as + // many parameters as t (it only has a Vararg instead). + t = jl_apply_tuple_type(sv, 0); + } else { + t = jl_instantiate_type_in_env((jl_value_t*)w, (jl_unionall_t*)wrapper, vals); + } + JL_GC_POP(); + } + JL_GC_POP(); + } + else if jl_is_unionall(t) { // recursively call itself on body and var bounds + jl_unionall_t* ut = (jl_unionall_t*)t; + jl_value_t *lb = NULL; + jl_value_t *ub = NULL; + jl_value_t *body = NULL; + JL_GC_PUSH3(&lb, &ub, &body); + lb = jl_substitute_datatype(ut->var->lb, x, y); + ub = jl_substitute_datatype(ut->var->ub, x, y); + body = jl_substitute_datatype(ut->body, x, y); + if (lb != ut->var->lb || ub != ut->var->ub) { + jl_tvar_t *newtvar = jl_new_typevar(ut->var->name, lb, ub); + JL_GC_PUSH1(&newtvar); + body = jl_substitute_var(body, ut->var, (jl_value_t*)newtvar); + t = jl_new_struct(jl_unionall_type, newtvar, body); + JL_GC_POP(); + } + else if (body != ut->body) { + t = jl_new_struct(jl_unionall_type, ut->var, body); + } + JL_GC_POP(); + } + else if jl_is_uniontype(t) { // recursively call itself on a and b + jl_uniontype_t *u = (jl_uniontype_t*)t; + jl_value_t *a = NULL; + jl_value_t *b = NULL; + JL_GC_PUSH2(&a, &b); + a = jl_substitute_datatype(u->a, x, y); + b = jl_substitute_datatype(u->b, x, y); + if (a != u->a || b != u->b) { + t = jl_new_struct(jl_uniontype_type, a, b); + } + JL_GC_POP(); + } + else if jl_is_vararg(t) { // recursively call itself on T + jl_vararg_t *vt = (jl_vararg_t*)t; + if (vt->T) { // vt->T could be NULL + jl_value_t *rT = NULL; + JL_GC_PUSH1(&rT); + rT = jl_substitute_datatype(vt->T, x, y); + if (rT != vt->T) { + jl_task_t *ct = jl_current_task; + t = jl_gc_alloc(ct->ptls, sizeof(jl_vararg_t), jl_vararg_type); + jl_set_typetagof((jl_vararg_t *)t, jl_vararg_tag, 0); + ((jl_vararg_t *)t)->T = rT; + ((jl_vararg_t *)t)->N = vt->N; + } + JL_GC_POP(); + } + } + return t; +} + static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, size_t ntp, jl_value_t **iparams) { diff --git a/src/julia_internal.h b/src/julia_internal.h index 652aae54860b5..d9e1a078c8a03 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -769,6 +769,7 @@ jl_unionall_t *jl_rename_unionall(jl_unionall_t *u); JL_DLLEXPORT jl_value_t *jl_unwrap_unionall(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u); JL_DLLEXPORT jl_value_t *jl_rewrap_unionall_(jl_value_t *t, jl_value_t *u); +jl_value_t* jl_substitute_datatype(jl_value_t *t, jl_datatype_t * x, jl_datatype_t * y); int jl_count_union_components(jl_value_t *v); JL_DLLEXPORT jl_value_t *jl_nth_union_component(jl_value_t *v JL_PROPAGATES_ROOT, int i) JL_NOTSAFEPOINT; int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth) JL_NOTSAFEPOINT; diff --git a/test/core.jl b/test/core.jl index 74df09bcdfd91..4db7f0e401fa0 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5611,6 +5611,26 @@ end x::Array{T} where T<:Integer end +# issue #54757, type redefinitions with recursive reference in supertype +struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{Y,N}} where {X,Y<:T54757}, N} + x::A + y::Union{A,T54757{A,N}} + z::T54757{A} +end + +struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{Y,N}} where {X,Y<:T54757}, N} + x::A + y::Union{A,T54757{A,N}} + z::T54757{A} +end + +@test_throws ErrorException struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A}},Vararg{Y,N}} where {X,Y<:T54757}, N} + x::A + y::Union{A,T54757{A,N}} + z::T54757{A} +end + + let a = Vector{Core.TypeofBottom}(undef, 2) @test a[1] == Union{} @test a == [Union{}, Union{}] From 39f2ad1e941fd092b906d60e8d23c004e8ee5b7e Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 2 Sep 2024 20:12:39 +0530 Subject: [PATCH 157/548] Reroute Symmetric/Hermitian + Diagonal through triangular (#55605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should fix the `Diagonal`-related issue from https://github.com/JuliaLang/julia/issues/55590, although the `SymTridiagonal` one still remains. ```julia julia> using LinearAlgebra julia> a = Matrix{BigFloat}(undef, 2,2) 2×2 Matrix{BigFloat}: #undef #undef #undef #undef julia> a[1] = 1; a[3] = 1; a[4] = 1 1 julia> a = Hermitian(a) 2×2 Hermitian{BigFloat, Matrix{BigFloat}}: 1.0 1.0 1.0 1.0 julia> b = Symmetric(a) 2×2 Symmetric{BigFloat, Matrix{BigFloat}}: 1.0 1.0 1.0 1.0 julia> c = Diagonal([1,1]) 2×2 Diagonal{Int64, Vector{Int64}}: 1 ⋅ ⋅ 1 julia> a+c 2×2 Hermitian{BigFloat, Matrix{BigFloat}}: 2.0 1.0 1.0 2.0 julia> b+c 2×2 Symmetric{BigFloat, Matrix{BigFloat}}: 2.0 1.0 1.0 2.0 ``` --- stdlib/LinearAlgebra/src/diagonal.jl | 15 --------------- stdlib/LinearAlgebra/src/special.jl | 19 +++++++++++++++++++ stdlib/LinearAlgebra/test/special.jl | 13 +++++++++++++ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 526ec20ddafa1..23d2422d13654 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -272,21 +272,6 @@ end (+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag) (-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag) -for f in (:+, :-) - @eval function $f(D::Diagonal{<:Number}, S::Symmetric) - return Symmetric($f(D, S.data), sym_uplo(S.uplo)) - end - @eval function $f(S::Symmetric, D::Diagonal{<:Number}) - return Symmetric($f(S.data, D), sym_uplo(S.uplo)) - end - @eval function $f(D::Diagonal{<:Real}, H::Hermitian) - return Hermitian($f(D, H.data), sym_uplo(H.uplo)) - end - @eval function $f(H::Hermitian, D::Diagonal{<:Real}) - return Hermitian($f(H.data, D), sym_uplo(H.uplo)) - end -end - (*)(x::Number, D::Diagonal) = Diagonal(x * D.diag) (*)(D::Diagonal, x::Number) = Diagonal(D.diag * x) (/)(D::Diagonal, x::Number) = Diagonal(D.diag / x) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 5fea1e32460ff..8263e632a86b8 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -288,6 +288,25 @@ function (-)(A::UniformScaling, B::Diagonal) Diagonal(Ref(A) .- B.diag) end +for f in (:+, :-) + @eval function $f(D::Diagonal{<:Number}, S::Symmetric) + uplo = sym_uplo(S.uplo) + return Symmetric(parentof_applytri($f, Symmetric(D, uplo), S), uplo) + end + @eval function $f(S::Symmetric, D::Diagonal{<:Number}) + uplo = sym_uplo(S.uplo) + return Symmetric(parentof_applytri($f, S, Symmetric(D, uplo)), uplo) + end + @eval function $f(D::Diagonal{<:Real}, H::Hermitian) + uplo = sym_uplo(H.uplo) + return Hermitian(parentof_applytri($f, Hermitian(D, uplo), H), uplo) + end + @eval function $f(H::Hermitian, D::Diagonal{<:Real}) + uplo = sym_uplo(H.uplo) + return Hermitian(parentof_applytri($f, H, Hermitian(D, uplo)), uplo) + end +end + ## Diagonal construction from UniformScaling Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.λ), m)) Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index 8d3733e6b1289..4b91bcfc1a4d5 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -790,6 +790,19 @@ end end end +@testset "Partly filled Hermitian and Diagonal algebra" begin + D = Diagonal([1,2]) + for S in (Symmetric, Hermitian), uplo in (:U, :L) + M = Matrix{BigInt}(undef, 2, 2) + M[1,1] = M[2,2] = M[1+(uplo == :L), 1 + (uplo == :U)] = 3 + H = S(M, uplo) + HM = Matrix(H) + @test H + D == D + H == HM + D + @test H - D == HM - D + @test D - H == D - HM + end +end + @testset "block SymTridiagonal" begin m = SizedArrays.SizedArray{(2,2)}(reshape([1:4;;],2,2)) S = SymTridiagonal(fill(m,4), fill(m,3)) From 04d6d5f2e88cc60707c2287b40a38f26e75102dc Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:31:38 +0900 Subject: [PATCH 158/548] inference: check argtype compatibility in `abstract_call_opaque_closure` (#55672) --- base/compiler/abstractinterpretation.jl | 13 +++++++++---- test/compiler/inference.jl | 13 +++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f3fc4e0423173..f2d4327668137 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2334,11 +2334,16 @@ end function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, check::Bool=true) sig = argtypes_to_type(arginfo.argtypes) - result = abstract_call_method(interp, closure.source::Method, sig, Core.svec(), false, si, sv) - (; rt, edge, effects, volatile_inf_result) = result tt = closure.typ - sigT = (unwrap_unionall(tt)::DataType).parameters[1] - match = MethodMatch(sig, Core.svec(), closure.source, sig <: rewrap_unionall(sigT, tt)) + ocargsig = rewrap_unionall((unwrap_unionall(tt)::DataType).parameters[1], tt) + ocargsig′ = unwrap_unionall(ocargsig) + ocargsig′ isa DataType || return CallMeta(Any, Any, Effects(), NoCallInfo()) + ocsig = rewrap_unionall(Tuple{Tuple, ocargsig′.parameters...}, ocargsig) + hasintersect(sig, ocsig) || return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + ocmethod = closure.source::Method + result = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) + (; rt, edge, effects, volatile_inf_result) = result + match = MethodMatch(sig, Core.svec(), ocmethod, sig <: ocsig) 𝕃ₚ = ipo_lattice(interp) ⊑ₚ = ⊑(𝕃ₚ) const_result = volatile_inf_result diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 485ee579abd52..7e1fea54830c9 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6076,3 +6076,16 @@ end fcondvarargs(a, b, c, d) = isa(d, Int64) gcondvarargs(a, x...) = return fcondvarargs(a, x...) ? isa(a, Int64) : !isa(a, Int64) @test Core.Compiler.return_type(gcondvarargs, Tuple{Vararg{Any}}) === Bool + +# JuliaLang/julia#55627: argtypes check in `abstract_call_opaque_closure` +issue55627_some_method(x) = 2x +issue55627_make_oc() = Base.Experimental.@opaque (x::Int)->issue55627_some_method(x) + +@test Base.infer_return_type() do + f = issue55627_make_oc() + return f(1), f() +end == Union{} +@test Base.infer_return_type((Vector{Int},)) do xs + f = issue55627_make_oc() + return f(1), f(xs...) +end == Tuple{Int,Int} From 34b81fbc90a96c1db7f235a465d2cfdf5937e563 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 3 Sep 2024 13:55:10 +0530 Subject: [PATCH 159/548] Forward istriu/istril for triangular to parent (#55663) --- stdlib/LinearAlgebra/src/special.jl | 4 ++++ stdlib/LinearAlgebra/src/triangular.jl | 22 +++++++++++++++++-- stdlib/LinearAlgebra/test/triangular.jl | 28 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 8263e632a86b8..5a7c98cfdf32c 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -586,3 +586,7 @@ function cholesky(S::RealHermSymComplexHerm{<:Real,<:SymTridiagonal}, ::NoPivot B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo(S.uplo)) cholesky!(Hermitian(B, sym_uplo(S.uplo)), NoPivot(); check = check) end + +# istriu/istril for triangular wrappers of structured matrices +_istril(A::LowerTriangular{<:Any, <:BandedMatrix}, k) = istril(parent(A), k) +_istriu(A::UpperTriangular{<:Any, <:BandedMatrix}, k) = istriu(parent(A), k) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 8d32dac824ce8..71473e0dc1174 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -330,14 +330,32 @@ function Base.replace_in_print_matrix(A::Union{LowerTriangular,UnitLowerTriangul return i >= j ? s : Base.replace_with_centered_mark(s) end -Base.@constprop :aggressive function istril(A::Union{LowerTriangular,UnitLowerTriangular}, k::Integer=0) +istril(A::UnitLowerTriangular, k::Integer=0) = k >= 0 +istriu(A::UnitUpperTriangular, k::Integer=0) = k <= 0 +Base.@constprop :aggressive function istril(A::LowerTriangular, k::Integer=0) k >= 0 && return true return _istril(A, k) end -Base.@constprop :aggressive function istriu(A::Union{UpperTriangular,UnitUpperTriangular}, k::Integer=0) +@inline function _istril(A::LowerTriangular, k) + P = parent(A) + m = size(A, 1) + for j in max(1, k + 2):m + all(iszero, view(P, j:min(j - k - 1, m), j)) || return false + end + return true +end +Base.@constprop :aggressive function istriu(A::UpperTriangular, k::Integer=0) k <= 0 && return true return _istriu(A, k) end +@inline function _istriu(A::UpperTriangular, k) + P = parent(A) + m = size(A, 1) + for j in 1:min(m, m + k - 1) + all(iszero, view(P, max(1, j - k + 1):j, j)) || return false + end + return true +end istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k) istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k) istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index b88be00b0209c..8748d11bd1da4 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1226,4 +1226,32 @@ end end end +@testset "istriu/istril forwards to parent" begin + @testset "$(nameof(typeof(M)))" for M in [Tridiagonal(rand(n-1), rand(n), rand(n-1)), + Tridiagonal(zeros(n-1), zeros(n), zeros(n-1)), + Diagonal(randn(n)), + Diagonal(zeros(n)), + ] + @testset for TriT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) + U = TriT(M) + A = Array(U) + for k in -n:n + @test istriu(U, k) == istriu(A, k) + @test istril(U, k) == istril(A, k) + end + end + end + z = zeros(n,n) + @testset for TriT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) + P = Matrix{BigFloat}(undef, n, n) + copytrito!(P, z, TriT <: Union{UpperTriangular, UnitUpperTriangular} ? 'U' : 'L') + U = TriT(P) + A = Array(U) + @testset for k in -n:n + @test istriu(U, k) == istriu(A, k) + @test istril(U, k) == istril(A, k) + end + end +end + end # module TestTriangular From eebc1e4e083b9b597bef328a5bd8eeda9ec52c1f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 3 Sep 2024 12:24:01 -0400 Subject: [PATCH 160/548] win: move stack_overflow_warning to the backtrace fiber (#55640) There is not enough stack space remaining after a stack overflow on Windows to allocate the 4k page used by `write` to call the WriteFile syscall. This causes it to hard-crash. But we can simply run this on the altstack implementation, where there is plenty of space. --- src/signals-win.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/signals-win.c b/src/signals-win.c index d7288b5d365d8..dbc3f8dad0968 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -109,6 +109,8 @@ static jl_ptls_t stkerror_ptls; static int have_backtrace_fiber; static void JL_NORETURN start_backtrace_fiber(void) { + // print the warning (this mysteriously needs a lot of stack for the WriteFile syscall) + stack_overflow_warning(); // collect the backtrace stkerror_ptls->bt_size = rec_backtrace_ctx(stkerror_ptls->bt_data, JL_MAX_BT_SIZE, stkerror_ctx, @@ -244,7 +246,6 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) case EXCEPTION_STACK_OVERFLOW: if (ct->eh != NULL) { ptls->needs_resetstkoflw = 1; - stack_overflow_warning(); jl_throw_in_ctx(ct, jl_stackovf_exception, ExceptionInfo->ContextRecord); return EXCEPTION_CONTINUE_EXECUTION; } From e474e0b470936110f689282c62a775ed4f0f4f81 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 3 Sep 2024 16:32:48 -0300 Subject: [PATCH 161/548] Check if ct is not null before doing is_addr_on_stack in the macos signal handler. (#55603) Before the check we used to segfault while segfaulting and hang --------- Co-authored-by: Jameson Nash --- src/signals-mach.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index c31b6d506b4e6..2f3e87ece296f 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -297,7 +297,9 @@ static void segv_handler(int sig, siginfo_t *info, void *context) return; } jl_task_t *ct = jl_get_current_task(); - if ((sig != SIGBUS || info->si_code == BUS_ADRERR) && is_addr_on_stack(ct, info->si_addr)) { // stack overflow and not a BUS_ADRALN (alignment error) + if ((sig != SIGBUS || info->si_code == BUS_ADRERR) && + !(ct == NULL || ct->ptls == NULL || jl_atomic_load_relaxed(&ct->ptls->gc_state) == JL_GC_STATE_WAITING || ct->eh == NULL) + && is_addr_on_stack(ct, info->si_addr)) { // stack overflow and not a BUS_ADRALN (alignment error) stack_overflow_warning(); } sigdie_handler(sig, info, context); From 48b40acf1cca3372ddaa4941b326b626146e9d16 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 3 Sep 2024 20:16:24 -0400 Subject: [PATCH 162/548] Profile.print: color Base/Core & packages. Make paths clickable (#55335) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated ## This PR ![Screenshot 2024-09-02 at 1 47 23 PM](https://github.com/user-attachments/assets/1264e623-70b2-462a-a595-1db2985caf64) ## master ![Screenshot 2024-09-02 at 1 49 42 PM](https://github.com/user-attachments/assets/14d62fe1-c317-4df5-86e9-7c555f9ab6f1) Todo: - [ ] ~Maybe drop the `@` prefix when coloring it, given it's obviously special when colored~ If someone copy-pasted the profile into an issue this would make it confusing. - [ ] Figure out why `Profile.print(format=:flat)` is truncating before the terminal width is used up - [x] Make filepaths terminal links (even if they're truncated) --- NEWS.md | 3 + stdlib/Manifest.toml | 19 ++--- stdlib/Profile/Project.toml | 6 ++ stdlib/Profile/src/Allocs.jl | 2 +- stdlib/Profile/src/Profile.jl | 147 +++++++++++++++++++++------------- 5 files changed, 113 insertions(+), 64 deletions(-) diff --git a/NEWS.md b/NEWS.md index b5caaf5376fb5..95a8a51c67ac8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -138,6 +138,9 @@ Standard library changes * `Profile.take_heap_snapshot` takes a new keyword argument, `redact_data::Bool`, that is `true` by default. When set, the contents of Julia objects are not emitted in the heap snapshot. This currently only applies to strings. ([#55326]) +* `Profile.print()` now colors Base/Core/Package modules similarly to how they are in stacktraces. + Also paths, even if truncated, are now clickable in terminals that support URI links + to take you to the specified `JULIA_EDITOR` for the given file & line number. ([#55335]) #### Random diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index c9d2086432a85..f9fb307190838 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -68,12 +68,12 @@ version = "1.11.0" [[deps.JuliaSyntaxHighlighting]] deps = ["StyledStrings"] uuid = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011" -version = "1.11.0" +version = "1.12.0" [[deps.LLD_jll]] deps = ["Artifacts", "Libdl", "Zlib_jll", "libLLVM_jll"] uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "16.0.6+4" +version = "18.1.7+2" [[deps.LLVMLibUnwind_jll]] deps = ["Artifacts", "Libdl"] @@ -113,12 +113,12 @@ version = "1.11.0+1" [[deps.LibUV_jll]] deps = ["Artifacts", "Libdl"] uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+16" +version = "2.0.1+17" [[deps.LibUnwind_jll]] deps = ["Artifacts", "Libdl"] uuid = "745a5e78-f969-53e9-954f-d19f2f74f4e3" -version = "1.8.1+0" +version = "1.8.1+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -163,7 +163,7 @@ version = "1.2.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.26+2" +version = "0.3.28+2" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] @@ -190,6 +190,7 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" [[deps.Profile]] +deps = ["StyledStrings"] uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" version = "1.11.0" @@ -223,7 +224,7 @@ version = "1.11.0" [[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" +version = "1.12.0" [[deps.Statistics]] deps = ["LinearAlgebra"] @@ -242,7 +243,7 @@ version = "1.11.0" [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" +version = "7.8.0+0" [[deps.TOML]] deps = ["Dates"] @@ -281,12 +282,12 @@ version = "2.2.5+0" [[deps.libLLVM_jll]] deps = ["Artifacts", "Libdl"] uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "16.0.6+4" +version = "18.1.7+2" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" +version = "5.11.0+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/stdlib/Profile/Project.toml b/stdlib/Profile/Project.toml index ad0107ecf9404..13cd11f70d9b4 100644 --- a/stdlib/Profile/Project.toml +++ b/stdlib/Profile/Project.toml @@ -2,6 +2,12 @@ name = "Profile" uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" version = "1.11.0" +[deps] +StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b" + +[compat] +StyledStrings = "1.11.0" + [extras] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index 31d703a151ad8..9d0b18cb468ca 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -321,7 +321,7 @@ end function flat(io::IO, data::Vector{Alloc}, cols::Int, fmt::ProfileFormat) fmt.combine || error(ArgumentError("combine=false")) lilist, n, m, totalbytes = parse_flat(fmt.combine ? StackFrame : UInt64, data, fmt.C) - filenamemap = Dict{Symbol,String}() + filenamemap = Profile.FileNameMap() if isempty(lilist) warning_empty() return true diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 799f23034b9ac..a80e6c71e5aef 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -38,6 +38,8 @@ public clear, Allocs import Base.StackTraces: lookup, UNKNOWN, show_spec_linfo, StackFrame +import Base: AnnotatedString +using StyledStrings: @styled_str const nmeta = 4 # number of metadata fields per block (threadid, taskid, cpu_cycle_clock, thread_sleeping) @@ -63,10 +65,10 @@ end # An internal function called to show the report after an information request (SIGINFO or SIGUSR1). function _peek_report() - iob = IOBuffer() + iob = Base.AnnotatedIOBuffer() ioc = IOContext(IOContext(iob, stderr), :displaysize=>displaysize(stderr)) print(ioc, groupby = [:thread, :task]) - Base.print(stderr, String(take!(iob))) + Base.print(stderr, read(seekstart(iob), AnnotatedString)) end # This is a ref so that it can be overridden by other profile info consumers. const peek_report = Ref{Function}(_peek_report) @@ -266,7 +268,7 @@ function print(io::IO, end any_nosamples = true if format === :tree - Base.print(io, "Overhead ╎ [+additional indent] Count File:Line; Function\n") + Base.print(io, "Overhead ╎ [+additional indent] Count File:Line Function\n") Base.print(io, "=========================================================\n") end if groupby == [:task, :thread] @@ -503,9 +505,10 @@ end # Take a file-system path and try to form a concise representation of it # based on the package ecosystem -function short_path(spath::Symbol, filenamecache::Dict{Symbol, String}) +function short_path(spath::Symbol, filenamecache::Dict{Symbol, Tuple{String,String,String}}) return get!(filenamecache, spath) do path = Base.fixup_stdlib_path(string(spath)) + possible_base_path = normpath(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base", path)) if isabspath(path) if ispath(path) # try to replace the file-system prefix with a short "@Module" one, @@ -522,20 +525,21 @@ function short_path(spath::Symbol, filenamecache::Dict{Symbol, String}) pkgid = Base.project_file_name_uuid(project_file, "") isempty(pkgid.name) && return path # bad Project file # return the joined the module name prefix and path suffix - path = path[nextind(path, sizeof(root)):end] - return string("@", pkgid.name, path) + _short_path = path[nextind(path, sizeof(root)):end] + return path, string("@", pkgid.name), _short_path end end end end - return path - elseif isfile(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base", path)) + return path, "", path + elseif isfile(possible_base_path) # do the same mechanic for Base (or Core/Compiler) files as above, # but they start from a relative path - return joinpath("@Base", normpath(path)) + return possible_base_path, "@Base", normpath(path) else # for non-existent relative paths (such as "REPL[1]"), just consider simplifying them - return normpath(path) # drop leading "./" + path = normpath(path) + return "", "", path # drop leading "./" end end end @@ -678,7 +682,7 @@ function add_fake_meta(data; threadid = 1, taskid = 0xf0f0f0f0) !isempty(data) && has_meta(data) && error("input already has metadata") cpu_clock_cycle = UInt64(99) data_with_meta = similar(data, 0) - for i = 1:length(data) + for i in eachindex(data) val = data[i] if iszero(val) # META_OFFSET_THREADID, META_OFFSET_TASKID, META_OFFSET_CPUCYCLECLOCK, META_OFFSET_SLEEPSTATE @@ -756,6 +760,8 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict, return (lilist, n, m, totalshots, nsleeping) end +const FileNameMap = Dict{Symbol,Tuple{String,String,String}} + function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfoFlatDict}, cols::Int, fmt::ProfileFormat, threads::Union{Int,AbstractVector{Int}}, tasks::Union{UInt,AbstractVector{UInt}}, is_subsection::Bool) lilist, n, m, totalshots, nsleeping = parse_flat(fmt.combine ? StackFrame : UInt64, data, lidict, fmt.C, threads, tasks) @@ -766,7 +772,7 @@ function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfo m = m[keep] end util_perc = (1 - (nsleeping / totalshots)) * 100 - filenamemap = Dict{Symbol,String}() + filenamemap = FileNameMap() if isempty(lilist) if is_subsection Base.print(io, "Total snapshots: ") @@ -788,9 +794,34 @@ function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfo return false end +# make a terminal-clickable link to the file and linenum. +# Similar to `define_default_editors` in `Base.Filesystem` but for creating URIs not commands +function editor_link(path::String, linenum::Int) + editor = get(ENV, "JULIA_EDITOR", "") + + if editor == "code" + return "vscode://file/$path:$linenum" + elseif editor == "subl" || editor == "sublime_text" + return "subl://$path:$linenum" + elseif editor == "idea" || occursin("idea", editor) + return "idea://open?file=$path&line=$linenum" + elseif editor == "pycharm" + return "pycharm://open?file=$path&line=$linenum" + elseif editor == "atom" + return "atom://core/open/file?filename=$path&line=$linenum" + elseif editor == "emacsclient" + return "emacs://open?file=$path&line=$linenum" + elseif editor == "vim" || editor == "nvim" + return "vim://open?file=$path&line=$linenum" + else + # TODO: convert the path to a generic URI (line numbers are not supported by generic URI) + return path + end +end + function print_flat(io::IO, lilist::Vector{StackFrame}, n::Vector{Int}, m::Vector{Int}, - cols::Int, filenamemap::Dict{Symbol,String}, + cols::Int, filenamemap::FileNameMap, fmt::ProfileFormat) if fmt.sortedby === :count p = sortperm(n) @@ -802,18 +833,18 @@ function print_flat(io::IO, lilist::Vector{StackFrame}, lilist = lilist[p] n = n[p] m = m[p] - filenames = String[short_path(li.file, filenamemap) for li in lilist] + pkgnames_filenames = Tuple{String,String,String}[short_path(li.file, filenamemap) for li in lilist] funcnames = String[string(li.func) for li in lilist] wcounts = max(6, ndigits(maximum(n))) wself = max(9, ndigits(maximum(m))) maxline = 1 maxfile = 6 maxfunc = 10 - for i in 1:length(lilist) + for i in eachindex(lilist) li = lilist[i] maxline = max(maxline, li.line) - maxfunc = max(maxfunc, length(funcnames[i])) - maxfile = max(maxfile, length(filenames[i])) + maxfunc = max(maxfunc, textwidth(funcnames[i])) + maxfile = max(maxfile, sum(textwidth, pkgnames_filenames[i][2:3]) + 1) end wline = max(5, ndigits(maxline)) ntext = max(20, cols - wcounts - wself - wline - 3) @@ -829,7 +860,7 @@ function print_flat(io::IO, lilist::Vector{StackFrame}, rpad("File", wfile, " "), " ", lpad("Line", wline, " "), " Function") println(io, lpad("=====", wcounts, " "), " ", lpad("========", wself, " "), " ", rpad("====", wfile, " "), " ", lpad("====", wline, " "), " ========") - for i = 1:length(n) + for i in eachindex(n) n[i] < fmt.mincount && continue li = lilist[i] Base.print(io, lpad(string(n[i]), wcounts, " "), " ") @@ -841,16 +872,29 @@ function print_flat(io::IO, lilist::Vector{StackFrame}, Base.print(io, "[any unknown stackframes]") end else - file = filenames[i] + path, pkgname, file = pkgnames_filenames[i] isempty(file) && (file = "[unknown file]") - Base.print(io, rpad(rtruncto(file, wfile), wfile, " "), " ") + pkgcolor = get!(() -> popfirst!(Base.STACKTRACE_MODULECOLORS), PACKAGE_FIXEDCOLORS, pkgname) + Base.printstyled(io, pkgname, color=pkgcolor) + file_trunc = ltruncate(file, max(1, wfile)) + wpad = wfile - textwidth(pkgname) + if !isempty(pkgname) && !startswith(file_trunc, "/") + Base.print(io, "/") + wpad -= 1 + end + if isempty(path) + Base.print(io, rpad(file_trunc, wpad, " ")) + else + link = editor_link(path, li.line) + Base.print(io, rpad(styled"{link=$link:$file_trunc}", wpad, " ")) + end Base.print(io, lpad(li.line > 0 ? string(li.line) : "?", wline, " "), " ") fname = funcnames[i] if !li.from_c && li.linfo !== nothing fname = sprint(show_spec_linfo, li) end isempty(fname) && (fname = "[unknown function]") - Base.print(io, ltruncto(fname, wfunc)) + Base.print(io, rtruncate(fname, wfunc)) end println(io) end @@ -889,21 +933,24 @@ function indent(depth::Int) return indent end -function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, maxes, filenamemap::Dict{Symbol,String}, showpointer::Bool) +# mimics Stacktraces +const PACKAGE_FIXEDCOLORS = Dict{String, Any}("@Base" => :gray, "@Core" => :gray) + +function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, maxes, filenamemap::FileNameMap, showpointer::Bool) nindent = min(cols>>1, level) ndigoverhead = ndigits(maxes.overhead) ndigcounts = ndigits(maxes.count) ndigline = ndigits(maximum(frame.frame.line for frame in frames)) + 6 ntext = max(30, cols - ndigoverhead - nindent - ndigcounts - ndigline - 6) widthfile = 2*ntext÷5 # min 12 - strs = Vector{String}(undef, length(frames)) + strs = Vector{AnnotatedString{String}}(undef, length(frames)) showextra = false if level > nindent nextra = level - nindent nindent -= ndigits(nextra) + 2 showextra = true end - for i = 1:length(frames) + for i in eachindex(frames) frame = frames[i] li = frame.frame stroverhead = lpad(frame.overhead > 0 ? string(frame.overhead) : "", ndigoverhead, " ") @@ -924,7 +971,7 @@ function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, ma else fname = string(li.func) end - filename = short_path(li.file, filenamemap) + path, pkgname, filename = short_path(li.file, filenamemap) if showpointer fname = string( "0x", @@ -932,17 +979,26 @@ function tree_format(frames::Vector{<:StackFrameTree}, level::Int, cols::Int, ma " ", fname) end - strs[i] = string(stroverhead, "╎", base, strcount, " ", - rtruncto(filename, widthfile), - ":", - li.line == -1 ? "?" : string(li.line), - "; ", - fname) + pkgcolor = get!(() -> popfirst!(Base.STACKTRACE_MODULECOLORS), PACKAGE_FIXEDCOLORS, pkgname) + remaining_path = ltruncate(filename, max(1, widthfile - textwidth(pkgname) - 1)) + linenum = li.line == -1 ? "?" : string(li.line) + slash = (!isempty(pkgname) && !startswith(remaining_path, "/")) ? "/" : "" + styled_path = styled"{$pkgcolor:$pkgname}$slash$remaining_path:$linenum" + rich_file = if isempty(path) + styled_path + else + link = editor_link(path, li.line) + styled"{link=$link:$styled_path}" + end + strs[i] = Base.annotatedstring(stroverhead, "╎", base, strcount, " ", rich_file, " ", fname) + if frame.overhead > 0 + strs[i] = styled"{bold:$(strs[i])}" + end end else strs[i] = string(stroverhead, "╎", base, strcount, " [unknown stackframe]") end - strs[i] = ltruncto(strs[i], cols) + strs[i] = rtruncate(strs[i], cols) end return strs end @@ -1101,10 +1157,10 @@ end # avoid stack overflows. function print_tree(io::IO, bt::StackFrameTree{T}, cols::Int, fmt::ProfileFormat, is_subsection::Bool) where T maxes = maxstats(bt) - filenamemap = Dict{Symbol,String}() - worklist = [(bt, 0, 0, "")] + filenamemap = FileNameMap() + worklist = [(bt, 0, 0, AnnotatedString(""))] if !is_subsection - Base.print(io, "Overhead ╎ [+additional indent] Count File:Line; Function\n") + Base.print(io, "Overhead ╎ [+additional indent] Count File:Line Function\n") Base.print(io, "=========================================================\n") end while !isempty(worklist) @@ -1135,7 +1191,7 @@ function print_tree(io::IO, bt::StackFrameTree{T}, cols::Int, fmt::ProfileFormat count = down.count count < fmt.mincount && continue count < noisefloor && continue - str = strs[i] + str = strs[i]::AnnotatedString noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0 pushfirst!(worklist, (down, level + 1, noisefloor_down, str)) end @@ -1196,24 +1252,7 @@ function callersf(matchfunc::Function, bt::Vector, lidict::LineInfoFlatDict) return [(v[i], k[i]) for i in p] end -# Utilities -function rtruncto(str::String, w::Int) - if textwidth(str) <= w - return str - else - return string("…", str[prevind(str, end, w-2):end]) - end -end -function ltruncto(str::String, w::Int) - if textwidth(str) <= w - return str - else - return string(str[1:nextind(str, 1, w-2)], "…") - end -end - - -truncto(str::Symbol, w::Int) = truncto(string(str), w) +## Utilities # Order alphabetically (file, function) and then by line number function liperm(lilist::Vector{StackFrame}) From b1b968e9bdbaa9939c0d45f9f9eb437b2aa3a7fc Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 3 Sep 2024 22:14:41 -0400 Subject: [PATCH 163/548] better signal handling (#55623) Instead of relying on creating a fake stack frame, and having no signals delivered, kernel bugs, accidentally gc_collect, or other issues occur during the delivery and execution of these calls, use the ability we added recently to emulate a longjmp into a unw_context to eliminate any time where there would exist any invalid states. Secondly, when calling jl_exit_thread0_cb, we used to end up completely smashing the unwind info (with CFI_NOUNWIND), but this makes core files from SIGQUIT much less helpful, so we now have a `fake_stack_pop` function with contains the necessary CFI directives such that a minimal unwind from the debugger will likely still succeed up into the frames that were removed. We cannot do this perfectly on AArch64 since that platform's DWARF spec lacks the ability to do so. On other platforms, this should be possible to implement exactly (subject to libunwind implementation quality). This is currently thus only fully implemented for x86_64 on Darwin Apple. --- src/jl_exported_funcs.inc | 1 - src/julia.h | 1 - src/julia_threads.h | 3 + src/rtutils.c | 27 ++- src/signals-mach.c | 128 +++++++--- src/signals-unix.c | 113 ++++----- src/signals-win.c | 76 +++--- src/stackwalk.c | 493 +++++++++++++++++++++----------------- src/task.c | 79 ++---- src/threading.c | 10 + 10 files changed, 519 insertions(+), 412 deletions(-) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 11cc8ee6fddd9..7f1636ad9ad80 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -420,7 +420,6 @@ XX(jl_set_zero_subnormals) \ XX(jl_sigatomic_begin) \ XX(jl_sigatomic_end) \ - XX(jl_sig_throw) \ XX(jl_spawn) \ XX(jl_specializations_get_linfo) \ XX(jl_specializations_lookup) \ diff --git a/src/julia.h b/src/julia.h index caa938ffeb0d6..589a5745ff59f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2310,7 +2310,6 @@ JL_DLLEXPORT int jl_set_task_tid(jl_task_t *task, int16_t tid) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_set_task_threadpoolid(jl_task_t *task, int8_t tpid) JL_NOTSAFEPOINT; JL_DLLEXPORT void JL_NORETURN jl_throw(jl_value_t *e JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_rethrow(void); -JL_DLLEXPORT void JL_NORETURN jl_sig_throw(void); JL_DLLEXPORT void JL_NORETURN jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_no_exc_handler(jl_value_t *e, jl_task_t *ct); JL_DLLEXPORT JL_CONST_FUNC jl_gcframe_t **(jl_get_pgcstack)(void) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; diff --git a/src/julia_threads.h b/src/julia_threads.h index e56ff5edd6176..7c6de1896ca13 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -187,6 +187,9 @@ typedef struct _jl_tls_states_t { // Saved exception for previous *external* API call or NULL if cleared. // Access via jl_exception_occurred(). struct _jl_value_t *previous_exception; +#ifdef _OS_DARWIN_ + jl_jmp_buf *volatile safe_restore; +#endif // currently-held locks, to be released when an exception is thrown small_arraylist_t locks; diff --git a/src/rtutils.c b/src/rtutils.c index a6a7fd5614de0..85a9be5e0b1da 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -269,10 +269,11 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh) // `eh` may be not equal to `ct->eh`. See `jl_pop_handler` // This function should **NOT** have any safepoint before the ones at the // end. - sig_atomic_t old_defer_signal = ct->ptls->defer_signal; + jl_ptls_t ptls = ct->ptls; + sig_atomic_t old_defer_signal = ptls->defer_signal; ct->eh = eh->prev; ct->gcstack = eh->gcstack; - small_arraylist_t *locks = &ct->ptls->locks; + small_arraylist_t *locks = &ptls->locks; int unlocks = locks->len > eh->locks_len; if (unlocks) { for (size_t i = locks->len; i > eh->locks_len; i--) @@ -280,14 +281,26 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh) locks->len = eh->locks_len; } ct->world_age = eh->world_age; - ct->ptls->defer_signal = eh->defer_signal; - int8_t old_gc_state = jl_atomic_load_relaxed(&ct->ptls->gc_state); + ptls->defer_signal = eh->defer_signal; + int8_t old_gc_state = jl_atomic_load_relaxed(&ptls->gc_state); if (old_gc_state != eh->gc_state) - jl_atomic_store_release(&ct->ptls->gc_state, eh->gc_state); + jl_atomic_store_release(&ptls->gc_state, eh->gc_state); if (!old_gc_state || !eh->gc_state) // it was or is unsafe now - jl_gc_safepoint_(ct->ptls); + jl_gc_safepoint_(ptls); + jl_value_t *exception = ptls->sig_exception; + if (exception) { + int8_t oldstate = jl_gc_unsafe_enter(ptls); + /* The temporary ptls->bt_data is rooted by special purpose code in the + GC. This exists only for the purpose of preserving bt_data until we + set ptls->bt_size=0 below. */ + jl_push_excstack(ct, &ct->excstack, exception, + ptls->bt_data, ptls->bt_size); + ptls->bt_size = 0; + ptls->sig_exception = NULL; + jl_gc_unsafe_leave(ptls, oldstate); + } if (old_defer_signal && !eh->defer_signal) - jl_sigint_safepoint(ct->ptls); + jl_sigint_safepoint(ptls); if (jl_atomic_load_relaxed(&jl_gc_have_pending_finalizers) && unlocks && eh->locks_len == 0) { jl_gc_run_pending_finalizers(ct); diff --git a/src/signals-mach.c b/src/signals-mach.c index 2f3e87ece296f..a939e4df71ae0 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -222,38 +222,92 @@ typedef arm_exception_state64_t host_exception_state_t; #define HOST_EXCEPTION_STATE_COUNT ARM_EXCEPTION_STATE64_COUNT #endif -static void jl_call_in_state(jl_ptls_t ptls2, host_thread_state_t *state, - void (*fptr)(void)) +// create a fake function that describes the variable manipulations in jl_call_in_state +__attribute__((naked)) static void fake_stack_pop(void) { #ifdef _CPU_X86_64_ - uintptr_t rsp = state->__rsp; + __asm__ volatile ( + " .cfi_signal_frame\n" + " .cfi_def_cfa %rsp, 0\n" // CFA here uses %rsp directly + " .cfi_offset %rip, 0\n" // previous value of %rip at CFA + " .cfi_offset %rsp, 8\n" // previous value of %rsp at CFA + " nop\n" + ); #elif defined(_CPU_AARCH64_) - uintptr_t rsp = state->__sp; + __asm__ volatile ( + " .cfi_signal_frame\n" + " .cfi_def_cfa sp, 0\n" // use sp as fp here + " .cfi_offset lr, 0\n" + " .cfi_offset sp, 8\n" + // Anything else got smashed, since we didn't explicitly copy all of the + // state object to the stack (to build a real sigreturn frame). + // This is also not quite valid, since the AArch64 DWARF spec lacks the ability to define how to restore the LR register correctly, + // so normally libunwind implementations on linux detect this function specially and hack around the invalid info: + // https://github.com/llvm/llvm-project/commit/c82deed6764cbc63966374baf9721331901ca958 + " nop\n" + ); #else -#error "julia: throw-in-context not supported on this platform" +CFI_NORETURN #endif - if (ptls2 == NULL || is_addr_on_sigstack(ptls2, (void*)rsp)) { - rsp = (rsp - 256) & ~(uintptr_t)15; // redzone and re-alignment - } - else { - rsp = (uintptr_t)ptls2->signal_stack + (ptls2->signal_stack_size ? ptls2->signal_stack_size : sig_stack_size); - } - assert(rsp % 16 == 0); - rsp -= 16; +} +static void jl_call_in_state(host_thread_state_t *state, void (*fptr)(void)) +{ #ifdef _CPU_X86_64_ - rsp -= sizeof(void*); - state->__rsp = rsp; // set stack pointer + uintptr_t sp = state->__rsp; +#elif defined(_CPU_AARCH64_) + uintptr_t sp = state->__sp; +#endif + sp = (sp - 256) & ~(uintptr_t)15; // redzone and re-alignment + assert(sp % 16 == 0); + sp -= 16; +#ifdef _CPU_X86_64_ + // set return address to NULL + *(uintptr_t*)sp = 0; + // pushq %sp + sp -= sizeof(void*); + *(uintptr_t*)sp = state->__rsp; + // pushq %rip + sp -= sizeof(void*); + *(uintptr_t*)sp = state->__rip; + // pushq .fake_stack_pop + 1; aka call from fake_stack_pop + sp -= sizeof(void*); + *(uintptr_t*)sp = (uintptr_t)&fake_stack_pop + 1; + state->__rsp = sp; // set stack pointer state->__rip = (uint64_t)fptr; // "call" the function #elif defined(_CPU_AARCH64_) - state->__sp = rsp; - state->__pc = (uint64_t)fptr; - state->__lr = 0; + // push {%sp, %pc + 4} + sp -= sizeof(void*); + *(uintptr_t*)sp = state->__sp; + sp -= sizeof(void*); + *(uintptr_t*)sp = (uintptr_t)state->__pc; + state->__sp = sp; // x31 + state->__pc = (uint64_t)fptr; // pc + state->__lr = (uintptr_t)&fake_stack_pop + 4; // x30 #else #error "julia: throw-in-context not supported on this platform" #endif } +static void jl_longjmp_in_state(host_thread_state_t *state, jl_jmp_buf jmpbuf) +{ + + if (!jl_simulate_longjmp(jmpbuf, (bt_context_t*)state)) { + // for sanitizer builds, fallback to calling longjmp on the original stack + // (this will fail for stack overflow, but that is hardly sanitizer-legal anyways) +#ifdef _CPU_X86_64_ + state->__rdi = (uintptr_t)jmpbuf; + state->__rsi = 1; +#elif defined(_CPU_AARCH64_) + state->__x[0] = (uintptr_t)jmpbuf; + state->__x[1] = 1; +#else +#error "julia: jl_longjmp_in_state not supported on this platform" +#endif + jl_call_in_state(state, (void (*)(void))longjmp); + } +} + #ifdef _CPU_X86_64_ int is_write_fault(host_exception_state_t exc_state) { return exc_reg_is_write_fault(exc_state.__err); @@ -275,14 +329,26 @@ static void jl_throw_in_thread(jl_ptls_t ptls2, mach_port_t thread, jl_value_t * host_thread_state_t state; kern_return_t ret = thread_get_state(thread, MACH_THREAD_STATE, (thread_state_t)&state, &count); HANDLE_MACH_ERROR("thread_get_state", ret); - if (1) { // XXX: !jl_has_safe_restore(ptls2) + if (ptls2->safe_restore) { + jl_longjmp_in_state(&state, *ptls2->safe_restore); + } + else { assert(exception); ptls2->bt_size = rec_backtrace_ctx(ptls2->bt_data, JL_MAX_BT_SIZE, (bt_context_t *)&state, - NULL /*current_task?*/); + NULL /*current_task?*/); ptls2->sig_exception = exception; + ptls2->io_wait = 0; + jl_task_t *ct = ptls2->current_task; + jl_handler_t *eh = ct->eh; + if (eh != NULL) { + asan_unpoison_task_stack(ct, &eh->eh_ctx); + jl_longjmp_in_state(&state, eh->eh_ctx); + } + else { + jl_no_exc_handler(exception, ct); + } } - jl_call_in_state(ptls2, &state, &jl_sig_throw); ret = thread_set_state(thread, MACH_THREAD_STATE, (thread_state_t)&state, count); HANDLE_MACH_ERROR("thread_set_state", ret); } @@ -290,10 +356,9 @@ static void jl_throw_in_thread(jl_ptls_t ptls2, mach_port_t thread, jl_value_t * static void segv_handler(int sig, siginfo_t *info, void *context) { assert(sig == SIGSEGV || sig == SIGBUS); - if (jl_get_safe_restore()) { // restarting jl_ or jl_unwind_stepn - jl_task_t *ct = jl_get_current_task(); - jl_ptls_t ptls = ct == NULL ? NULL : ct->ptls; - jl_call_in_state(ptls, (host_thread_state_t*)jl_to_bt_context(context), &jl_sig_throw); + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) { // restarting jl_ or jl_unwind_stepn + jl_longjmp_in_state((host_thread_state_t*)jl_to_bt_context(context), *saferestore); return; } jl_task_t *ct = jl_get_current_task(); @@ -354,12 +419,10 @@ kern_return_t catch_mach_exception_raise( jl_safe_printf("ERROR: Exception handler triggered on unmanaged thread.\n"); return KERN_INVALID_ARGUMENT; } - // XXX: jl_throw_in_thread or segv_handler will eventually check this, but - // we would like to avoid some of this work if we could detect this earlier - // if (jl_has_safe_restore(ptls2)) { - // jl_throw_in_thread(ptls2, thread, NULL); - // return KERN_SUCCESS; - // } + if (ptls2->safe_restore) { + jl_throw_in_thread(ptls2, thread, NULL); + return KERN_SUCCESS; + } if (jl_atomic_load_acquire(&ptls2->gc_state) == JL_GC_STATE_WAITING) return KERN_FAILURE; if (exception == EXC_ARITHMETIC) { @@ -518,7 +581,6 @@ static void jl_try_deliver_sigint(void) static void JL_NORETURN jl_exit_thread0_cb(int signo) { -CFI_NORETURN jl_critical_error(signo, 0, NULL, jl_current_task); jl_atexit_hook(128); jl_raise(signo); @@ -550,7 +612,7 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) #else #error Fill in first integer argument here #endif - jl_call_in_state(ptls2, &state, (void (*)(void))&jl_exit_thread0_cb); + jl_call_in_state(&state, (void (*)(void))&jl_exit_thread0_cb); unsigned int count = MACH_THREAD_STATE_COUNT; ret = thread_set_state(thread, MACH_THREAD_STATE, (thread_state_t)&state, count); HANDLE_MACH_ERROR("thread_set_state", ret); diff --git a/src/signals-unix.c b/src/signals-unix.c index d719ac7fa452d..d0885b6bdee3f 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -44,7 +44,7 @@ static const size_t sig_stack_size = 8 * 1024 * 1024; // helper function for returning the unw_context_t inside a ucontext_t // (also used by stackwalk.c) -bt_context_t *jl_to_bt_context(void *sigctx) +bt_context_t *jl_to_bt_context(void *sigctx) JL_NOTSAFEPOINT { #ifdef __APPLE__ return (bt_context_t*)&((ucontext64_t*)sigctx)->uc_mcontext64->__ss; @@ -62,7 +62,11 @@ bt_context_t *jl_to_bt_context(void *sigctx) static int thread0_exit_count = 0; static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size); -static inline __attribute__((unused)) uintptr_t jl_get_rsp_from_ctx(const void *_ctx) +int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT; +static void jl_longjmp_in_ctx(int sig, void *_ctx, jl_jmp_buf jmpbuf); + +#if !defined(_OS_DARWIN_) +static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx) { #if defined(_OS_LINUX_) && defined(_CPU_X86_64_) const ucontext_t *ctx = (const ucontext_t*)_ctx; @@ -76,12 +80,6 @@ static inline __attribute__((unused)) uintptr_t jl_get_rsp_from_ctx(const void * #elif defined(_OS_LINUX_) && defined(_CPU_ARM_) const ucontext_t *ctx = (const ucontext_t*)_ctx; return ctx->uc_mcontext.arm_sp; -#elif defined(_OS_DARWIN_) && defined(_CPU_X86_64_) - const ucontext64_t *ctx = (const ucontext64_t*)_ctx; - return ctx->uc_mcontext64->__ss.__rsp; -#elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) - const ucontext64_t *ctx = (const ucontext64_t*)_ctx; - return ctx->uc_mcontext64->__ss.__sp; #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) const ucontext_t *ctx = (const ucontext_t*)_ctx; return ctx->uc_mcontext.mc_rsp; @@ -97,7 +95,7 @@ static inline __attribute__((unused)) uintptr_t jl_get_rsp_from_ctx(const void * #endif } -static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) +static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) JL_NOTSAFEPOINT { // One guard page for signal_stack. return ptls->signal_stack == NULL || @@ -105,10 +103,8 @@ static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) (char*)ptr <= (char*)ptls->signal_stack + (ptls->signal_stack_size ? ptls->signal_stack_size : sig_stack_size)); } -// Modify signal context `_ctx` so that `fptr` will execute when the signal -// returns. `fptr` will execute on the signal stack, and must not return. -// jl_call_in_ctx is also currently executing on that signal stack, -// so be careful not to smash it +// Modify signal context `_ctx` so that `fptr` will execute when the signal returns +// The function `fptr` itself must not return. JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int sig, void *_ctx) { // Modifying the ucontext should work but there is concern that @@ -118,44 +114,36 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si // checks that the syscall is made in the signal handler and that // the ucontext address is valid. Hopefully the value of the ucontext // will not be part of the validation... - if (!ptls) { - sigset_t sset; - sigemptyset(&sset); - sigaddset(&sset, sig); - pthread_sigmask(SIG_UNBLOCK, &sset, NULL); - fptr(); - return; - } uintptr_t rsp = jl_get_rsp_from_ctx(_ctx); - if (is_addr_on_sigstack(ptls, (void*)rsp)) - rsp = (rsp - 256) & ~(uintptr_t)15; // redzone and re-alignment - else - rsp = (uintptr_t)ptls->signal_stack + (ptls->signal_stack_size ? ptls->signal_stack_size : sig_stack_size); - assert(rsp % 16 == 0); - rsp -= 16; + rsp = (rsp - 256) & ~(uintptr_t)15; // redzone and re-alignment #if defined(_OS_LINUX_) && defined(_CPU_X86_64_) ucontext_t *ctx = (ucontext_t*)_ctx; rsp -= sizeof(void*); + *(uintptr_t*)rsp = 0; ctx->uc_mcontext.gregs[REG_RSP] = rsp; ctx->uc_mcontext.gregs[REG_RIP] = (uintptr_t)fptr; #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) ucontext_t *ctx = (ucontext_t*)_ctx; rsp -= sizeof(void*); + *(uintptr_t*)rsp = 0; ctx->uc_mcontext.mc_rsp = rsp; ctx->uc_mcontext.mc_rip = (uintptr_t)fptr; #elif defined(_OS_LINUX_) && defined(_CPU_X86_) ucontext_t *ctx = (ucontext_t*)_ctx; rsp -= sizeof(void*); + *(uintptr_t*)rsp = 0; ctx->uc_mcontext.gregs[REG_ESP] = rsp; ctx->uc_mcontext.gregs[REG_EIP] = (uintptr_t)fptr; #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_) ucontext_t *ctx = (ucontext_t*)_ctx; rsp -= sizeof(void*); + *(uintptr_t*)rsp = 0; ctx->uc_mcontext.mc_esp = rsp; ctx->uc_mcontext.mc_eip = (uintptr_t)fptr; #elif defined(_OS_OPENBSD_) && defined(_CPU_X86_64_) struct sigcontext *ctx = (struct sigcontext *)_ctx; rsp -= sizeof(void*); + *(uintptr_t*)rsp = 0; ctx->sc_rsp = rsp; ctx->sc_rip = fptr; #elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_) @@ -187,22 +175,6 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si ctx->uc_mcontext.arm_sp = rsp; ctx->uc_mcontext.arm_lr = 0; // Clear link register ctx->uc_mcontext.arm_pc = target; -#elif defined(_OS_DARWIN_) && (defined(_CPU_X86_64_) || defined(_CPU_AARCH64_)) - // Only used for SIGFPE. - // This doesn't seems to be reliable when the SIGFPE is generated - // from a divide-by-zero exception, which is now handled by - // `catch_exception_raise`. It works fine when a signal is received - // due to `kill`/`raise` though. - ucontext64_t *ctx = (ucontext64_t*)_ctx; -#if defined(_CPU_X86_64_) - rsp -= sizeof(void*); - ctx->uc_mcontext64->__ss.__rsp = rsp; - ctx->uc_mcontext64->__ss.__rip = (uintptr_t)fptr; -#else - ctx->uc_mcontext64->__ss.__sp = rsp; - ctx->uc_mcontext64->__ss.__pc = (uintptr_t)fptr; - ctx->uc_mcontext64->__ss.__lr = 0; -#endif #else #pragma message("julia: throw-in-context not supported on this platform") // TODO Add support for PowerPC(64)? @@ -213,22 +185,30 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si fptr(); #endif } +#endif static void jl_throw_in_ctx(jl_task_t *ct, jl_value_t *e, int sig, void *sigctx) { jl_ptls_t ptls = ct->ptls; - if (!jl_get_safe_restore()) { - ptls->bt_size = - rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, jl_to_bt_context(sigctx), - ct->gcstack); - ptls->sig_exception = e; + assert(!jl_get_safe_restore()); + ptls->bt_size = + rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, jl_to_bt_context(sigctx), + ct->gcstack); + ptls->sig_exception = e; + ptls->io_wait = 0; + jl_handler_t *eh = ct->eh; + if (eh != NULL) { + asan_unpoison_task_stack(ct, &eh->eh_ctx); + jl_longjmp_in_ctx(sig, sigctx, eh->eh_ctx); + } + else { + jl_no_exc_handler(e, ct); } - jl_call_in_ctx(ptls, &jl_sig_throw, sig, sigctx); } static pthread_t signals_thread; -static int is_addr_on_stack(jl_task_t *ct, void *addr) +static int is_addr_on_stack(jl_task_t *ct, void *addr) JL_NOTSAFEPOINT { if (ct->ctx.copy_stack) { jl_ptls_t ptls = ct->ptls; @@ -379,7 +359,7 @@ int is_write_fault(void *context) { } #endif -static int jl_is_on_sigstack(jl_ptls_t ptls, void *ptr, void *context) +static int jl_is_on_sigstack(jl_ptls_t ptls, void *ptr, void *context) JL_NOTSAFEPOINT { return (ptls->signal_stack != NULL && is_addr_on_sigstack(ptls, ptr) && @@ -389,8 +369,9 @@ static int jl_is_on_sigstack(jl_ptls_t ptls, void *ptr, void *context) JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) { assert(sig == SIGSEGV || sig == SIGBUS); - if (jl_get_safe_restore()) { // restarting jl_ or profile - jl_call_in_ctx(NULL, &jl_sig_throw, sig, context); + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) { // restarting jl_ or profile + jl_longjmp_in_ctx(sig, context, *saferestore); return; } jl_task_t *ct = jl_get_current_task(); @@ -630,7 +611,11 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) jl_safe_printf("WARNING: Force throwing a SIGINT\n"); // Force a throw jl_clear_force_sigint(); - jl_throw_in_ctx(ct, jl_interrupt_exception, sig, ctx); + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) // restarting jl_ or profile + jl_longjmp_in_ctx(sig, ctx, *saferestore); + else + jl_throw_in_ctx(ct, jl_interrupt_exception, sig, ctx); } } else if (request == 3) { @@ -1100,8 +1085,9 @@ void restore_signals(void) static void fpe_handler(int sig, siginfo_t *info, void *context) { (void)info; - if (jl_get_safe_restore()) { // restarting jl_ or profile - jl_call_in_ctx(NULL, &jl_sig_throw, sig, context); + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) { // restarting jl_ or profile + jl_longjmp_in_ctx(sig, context, *saferestore); return; } jl_task_t *ct = jl_get_current_task(); @@ -1111,6 +1097,21 @@ static void fpe_handler(int sig, siginfo_t *info, void *context) jl_throw_in_ctx(ct, jl_diverror_exception, sig, context); } +static void jl_longjmp_in_ctx(int sig, void *_ctx, jl_jmp_buf jmpbuf) +{ +#if defined(_OS_DARWIN_) + jl_longjmp_in_state((host_thread_state_t*)jl_to_bt_context(_ctx), jmpbuf); +#else + if (jl_simulate_longjmp(jmpbuf, jl_to_bt_context(_ctx))) + return; + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, sig); + pthread_sigmask(SIG_UNBLOCK, &sset, NULL); + jl_longjmp(jmpbuf, 1); +#endif +} + static void sigint_handler(int sig) { jl_sigint_passed = 1; diff --git a/src/signals-win.c b/src/signals-win.c index dbc3f8dad0968..b5f8dd8bd79d9 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -86,9 +86,13 @@ void __cdecl crt_sig_handler(int sig, int num) } break; default: // SIGSEGV, SIGTERM, SIGILL, SIGABRT - if (sig == SIGSEGV && jl_get_safe_restore()) { - signal(sig, (void (__cdecl *)(int))crt_sig_handler); - jl_sig_throw(); + if (sig == SIGSEGV) { // restarting jl_ or profile + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) { + signal(sig, (void (__cdecl *)(int))crt_sig_handler); + jl_longjmp(*saferestore, 1); + return; + } } memset(&Context, 0, sizeof(Context)); RtlCaptureContext(&Context); @@ -126,41 +130,41 @@ void restore_signals(void) SetConsoleCtrlHandler(NULL, 0); } -void jl_throw_in_ctx(jl_task_t *ct, jl_value_t *excpt, PCONTEXT ctxThread) +int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c); + +static void jl_throw_in_ctx(jl_task_t *ct, jl_value_t *excpt, PCONTEXT ctxThread) { -#if defined(_CPU_X86_64_) - DWORD64 Rsp = (ctxThread->Rsp & (DWORD64)-16) - 8; -#elif defined(_CPU_X86_) - DWORD32 Esp = (ctxThread->Esp & (DWORD32)-16) - 4; -#else -#error WIN16 not supported :P -#endif - if (ct && !jl_get_safe_restore()) { - assert(excpt != NULL); - jl_ptls_t ptls = ct->ptls; - ptls->bt_size = 0; - if (excpt != jl_stackovf_exception) { - ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, ctxThread, - ct->gcstack); - } - else if (have_backtrace_fiber) { - uv_mutex_lock(&backtrace_lock); - stkerror_ctx = ctxThread; - stkerror_ptls = ptls; - jl_swapcontext(&error_return_fiber, &collect_backtrace_fiber); - uv_mutex_unlock(&backtrace_lock); - } - ptls->sig_exception = excpt; + jl_jmp_buf *saferestore = jl_get_safe_restore(); + if (saferestore) { // restarting jl_ or profile + if (!jl_simulate_longjmp(*saferestore, ctxThread)) + abort(); + return; + } + assert(ct && excpt); + jl_ptls_t ptls = ct->ptls; + ptls->bt_size = 0; + if (excpt != jl_stackovf_exception) { + ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, ctxThread, + ct->gcstack); + } + else if (have_backtrace_fiber) { + uv_mutex_lock(&backtrace_lock); + stkerror_ctx = ctxThread; + stkerror_ptls = ptls; + jl_swapcontext(&error_return_fiber, &collect_backtrace_fiber); + uv_mutex_unlock(&backtrace_lock); + } + ptls->sig_exception = excpt; + ptls->io_wait = 0; + jl_handler_t *eh = ct->eh; + if (eh != NULL) { + asan_unpoison_task_stack(ct, &eh->eh_ctx); + if (!jl_simulate_longjmp(eh->eh_ctx, ctxThread)) + abort(); + } + else { + jl_no_exc_handler(excpt, ct); } -#if defined(_CPU_X86_64_) - *(DWORD64*)Rsp = 0; - ctxThread->Rsp = Rsp; - ctxThread->Rip = (DWORD64)&jl_sig_throw; -#elif defined(_CPU_X86_) - *(DWORD32*)Esp = 0; - ctxThread->Esp = Esp; - ctxThread->Eip = (DWORD)&jl_sig_throw; -#endif } HANDLE hMainThread = INVALID_HANDLE_VALUE; diff --git a/src/stackwalk.c b/src/stackwalk.c index 15a9fddeac9a4..6aa36fa8b499c 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -919,7 +919,273 @@ _os_ptr_munge(uintptr_t ptr) JL_NOTSAFEPOINT #endif -extern bt_context_t *jl_to_bt_context(void *sigctx); +extern bt_context_t *jl_to_bt_context(void *sigctx) JL_NOTSAFEPOINT; + +// Some notes: this simulates a longjmp call occurring in context `c`, as if the +// user was to set the PC in `c` to call longjmp and the PC in the longjmp to +// return here. This helps work around many cases where siglongjmp out of a +// signal handler is not supported (e.g. missing a _sigunaltstack call). +// Additionally note that this doesn't restore the MXCSR or FP control word +// (which some, but not most longjmp implementations do). It also doesn't +// support shadow stacks, so if those are in use, you might need to use a direct +// jl_longjmp instead to leave the signal frame instead of relying on simulating +// it and attempting to return normally. +int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT +{ +#if (defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_)) + https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/hwasan/hwasan_interceptors.cpp + return 0; +#elif defined(_OS_WINDOWS_) + _JUMP_BUFFER* _ctx = (_JUMP_BUFFER*)mctx; + #if defined(_CPU_X86_64_) + c->Rbx = _ctx->Rbx; + c->Rsp = _ctx->Rsp; + c->Rbp = _ctx->Rbp; + c->Rsi = _ctx->Rsi; + c->Rdi = _ctx->Rdi; + c->R12 = _ctx->R12; + c->R13 = _ctx->R13; + c->R14 = _ctx->R14; + c->R15 = _ctx->R15; + c->Rip = _ctx->Rip; + memcpy(&c->Xmm6, &_ctx->Xmm6, 10 * sizeof(_ctx->Xmm6)); // Xmm6-Xmm15 + // c->MxCsr = _ctx->MxCsr; + // c->FloatSave.ControlWord = _ctx->FpCsr; + // c->SegGS[0] = _ctx->Frame; + c->Rax = 1; + c->Rsp += sizeof(void*); + assert(c->Rsp % 16 == 0); + return 1; + #elif defined(_CPU_X86_) + c->Ebp = _ctx->Ebp; + c->Ebx = _ctx->Ebx; + c->Edi = _ctx->Edi; + c->Esi = _ctx->Esi; + c->Esp = _ctx->Esp; + c->Eip = _ctx->Eip; + // c->SegFS[0] = _ctx->Registration; + // c->FloatSave.ControlWord = _ctx->FpCsr; + c->Eax = 1; + c->Esp += sizeof(void*); + assert(c->Esp % 16 == 0); + return 1; + #else + #error Windows is currently only supported on x86 and x86_64 + #endif +#elif defined(_OS_LINUX_) && defined(__GLIBC__) + __jmp_buf *_ctx = &mctx->__jmpbuf; + mcontext_t *mc = &c->uc_mcontext; + #if defined(_CPU_X86_) + // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/i386/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/i386/longjmp.s + mc->gregs[REG_EBX] = (*_ctx)[0]; + mc->gregs[REG_ESI] = (*_ctx)[1]; + mc->gregs[REG_EDI] = (*_ctx)[2]; + mc->gregs[REG_EBP] = (*_ctx)[3]; + mc->gregs[REG_ESP] = (*_ctx)[4]; + mc->gregs[REG_EIP] = (*_ctx)[5]; + // ifdef PTR_DEMANGLE ? + mc->gregs[REG_ESP] = ptr_demangle(mc->gregs[REG_ESP]); + mc->gregs[REG_EIP] = ptr_demangle(mc->gregs[REG_EIP]); + mc->gregs[REG_EAX] = 1; + assert(mc->gregs[REG_ESP] % 16 == 0); + return 1; + #elif defined(_CPU_X86_64_) + // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/x86_64/setjmp.s + mc->gregs[REG_RBX] = (*_ctx)[0]; + mc->gregs[REG_RBP] = (*_ctx)[1]; + mc->gregs[REG_R12] = (*_ctx)[2]; + mc->gregs[REG_R13] = (*_ctx)[3]; + mc->gregs[REG_R14] = (*_ctx)[4]; + mc->gregs[REG_R15] = (*_ctx)[5]; + mc->gregs[REG_RSP] = (*_ctx)[6]; + mc->gregs[REG_RIP] = (*_ctx)[7]; + // ifdef PTR_DEMANGLE ? + mc->gregs[REG_RBP] = ptr_demangle(mc->gregs[REG_RBP]); + mc->gregs[REG_RSP] = ptr_demangle(mc->gregs[REG_RSP]); + mc->gregs[REG_RIP] = ptr_demangle(mc->gregs[REG_RIP]); + mc->gregs[REG_RAX] = 1; + assert(mc->gregs[REG_RSP] % 16 == 0); + return 1; + #elif defined(_CPU_ARM_) + // https://github.com/bminor/glibc/blame/master/sysdeps/arm/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/arm/include/bits/setjmp.h + // https://github.com/bminor/musl/blame/master/src/setjmp/arm/longjmp.S + mc->arm_sp = (*_ctx)[0]; + mc->arm_lr = (*_ctx)[1]; + mc->arm_r4 = (*_ctx)[2]; // aka v1 + mc->arm_r5 = (*_ctx)[3]; // aka v2 + mc->arm_r6 = (*_ctx)[4]; // aka v3 + mc->arm_r7 = (*_ctx)[5]; // aka v4 + mc->arm_r8 = (*_ctx)[6]; // aka v5 + mc->arm_r9 = (*_ctx)[7]; // aka v6 aka sb + mc->arm_r10 = (*_ctx)[8]; // aka v7 aka sl + mc->arm_fp = (*_ctx)[10]; // aka v8 aka r11 + // ifdef PTR_DEMANGLE ? + mc->arm_sp = ptr_demangle(mc->arm_sp); + mc->arm_lr = ptr_demangle(mc->arm_lr); + mc->arm_pc = mc->arm_lr; + mc->arm_r0 = 1; + assert(mc->arm_sp % 16 == 0); + return 1; + #elif defined(_CPU_AARCH64_) + // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/aarch64/longjmp.s + // https://github.com/libunwind/libunwind/blob/ec171c9ba7ea3abb2a1383cee2988a7abd483a1f/src/aarch64/unwind_i.h#L62 + unw_fpsimd_context_t *mcfp = (unw_fpsimd_context_t*)&mc->__reserved; + mc->regs[19] = (*_ctx)[0]; + mc->regs[20] = (*_ctx)[1]; + mc->regs[21] = (*_ctx)[2]; + mc->regs[22] = (*_ctx)[3]; + mc->regs[23] = (*_ctx)[4]; + mc->regs[24] = (*_ctx)[5]; + mc->regs[25] = (*_ctx)[6]; + mc->regs[26] = (*_ctx)[7]; + mc->regs[27] = (*_ctx)[8]; + mc->regs[28] = (*_ctx)[9]; + mc->regs[29] = (*_ctx)[10]; // aka fp + mc->regs[30] = (*_ctx)[11]; // aka lr + // Yes, they did skip 12 why writing the code originally; and, no, I do not know why. + mc->sp = (*_ctx)[13]; + mcfp->vregs[7] = (*_ctx)[14]; // aka d8 + mcfp->vregs[8] = (*_ctx)[15]; // aka d9 + mcfp->vregs[9] = (*_ctx)[16]; // aka d10 + mcfp->vregs[10] = (*_ctx)[17]; // aka d11 + mcfp->vregs[11] = (*_ctx)[18]; // aka d12 + mcfp->vregs[12] = (*_ctx)[19]; // aka d13 + mcfp->vregs[13] = (*_ctx)[20]; // aka d14 + mcfp->vregs[14] = (*_ctx)[21]; // aka d15 + // ifdef PTR_DEMANGLE ? + mc->sp = ptr_demangle(mc->sp); + mc->regs[30] = ptr_demangle(mc->regs[30]); + mc->pc = mc->regs[30]; + mc->regs[0] = 1; + assert(mc->sp % 16 == 0); + return 1; + #else + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown linux") + (void)mc; + (void)mctx; + return 0; + #endif +#elif defined(_OS_DARWIN_) + #if defined(_CPU_X86_64_) + // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/x86_64/_setjmp.s + x86_thread_state64_t *mc = (x86_thread_state64_t*)c; + mc->__rbx = ((uint64_t*)mctx)[0]; + mc->__rbp = ((uint64_t*)mctx)[1]; + mc->__rsp = ((uint64_t*)mctx)[2]; + mc->__r12 = ((uint64_t*)mctx)[3]; + mc->__r13 = ((uint64_t*)mctx)[4]; + mc->__r14 = ((uint64_t*)mctx)[5]; + mc->__r15 = ((uint64_t*)mctx)[6]; + mc->__rip = ((uint64_t*)mctx)[7]; + // added in libsystem_platform 177.200.16 (macOS Mojave 10.14.3) + // prior to that _os_ptr_munge_token was (hopefully) typically 0, + // so x ^ 0 == x and this is a no-op + mc->__rbp = _OS_PTR_UNMUNGE(mc->__rbp); + mc->__rsp = _OS_PTR_UNMUNGE(mc->__rsp); + mc->__rip = _OS_PTR_UNMUNGE(mc->__rip); + mc->__rax = 1; + assert(mc->__rsp % 16 == 0); + return 1; + #elif defined(_CPU_AARCH64_) + // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/arm64/setjmp.s + // https://github.com/apple/darwin-xnu/blob/main/osfmk/mach/arm/_structs.h + // https://github.com/llvm/llvm-project/blob/7714e0317520207572168388f22012dd9e152e9e/libunwind/src/Registers.hpp -> Registers_arm64 + arm_thread_state64_t *mc = (arm_thread_state64_t*)c; + mc->__x[19] = ((uint64_t*)mctx)[0]; + mc->__x[20] = ((uint64_t*)mctx)[1]; + mc->__x[21] = ((uint64_t*)mctx)[2]; + mc->__x[22] = ((uint64_t*)mctx)[3]; + mc->__x[23] = ((uint64_t*)mctx)[4]; + mc->__x[24] = ((uint64_t*)mctx)[5]; + mc->__x[25] = ((uint64_t*)mctx)[6]; + mc->__x[26] = ((uint64_t*)mctx)[7]; + mc->__x[27] = ((uint64_t*)mctx)[8]; + mc->__x[28] = ((uint64_t*)mctx)[9]; + mc->__x[10] = ((uint64_t*)mctx)[10]; + mc->__x[11] = ((uint64_t*)mctx)[11]; + mc->__x[12] = ((uint64_t*)mctx)[12]; + // 13 is reserved/unused + double *mcfp = (double*)&mc[1]; + mcfp[7] = ((uint64_t*)mctx)[14]; // aka d8 + mcfp[8] = ((uint64_t*)mctx)[15]; // aka d9 + mcfp[9] = ((uint64_t*)mctx)[16]; // aka d10 + mcfp[10] = ((uint64_t*)mctx)[17]; // aka d11 + mcfp[11] = ((uint64_t*)mctx)[18]; // aka d12 + mcfp[12] = ((uint64_t*)mctx)[19]; // aka d13 + mcfp[13] = ((uint64_t*)mctx)[20]; // aka d14 + mcfp[14] = ((uint64_t*)mctx)[21]; // aka d15 + mc->__fp = _OS_PTR_UNMUNGE(mc->__x[10]); + mc->__lr = _OS_PTR_UNMUNGE(mc->__x[11]); + mc->__x[12] = _OS_PTR_UNMUNGE(mc->__x[12]); + mc->__sp = mc->__x[12]; + // libunwind is broken for signed-pointers, but perhaps best not to leave the signed pointer lying around either + mc->__pc = ptrauth_strip(mc->__lr, 0); + mc->__pad = 0; // aka __ra_sign_state = not signed + mc->__x[0] = 1; + assert(mc->__sp % 16 == 0); + return 1; + #else + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown darwin") + (void)mctx; + return 0; +#endif +#elif defined(_OS_FREEBSD_) + mcontext_t *mc = &c->uc_mcontext; + #if defined(_CPU_X86_64_) + // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S + mc->mc_rip = ((long*)mctx)[0]; + mc->mc_rbx = ((long*)mctx)[1]; + mc->mc_rsp = ((long*)mctx)[2]; + mc->mc_rbp = ((long*)mctx)[3]; + mc->mc_r12 = ((long*)mctx)[4]; + mc->mc_r13 = ((long*)mctx)[5]; + mc->mc_r14 = ((long*)mctx)[6]; + mc->mc_r15 = ((long*)mctx)[7]; + mc->mc_rax = 1; + mc->mc_rsp += sizeof(void*); + assert(mc->mc_rsp % 16 == 0); + return 1; + #elif defined(_CPU_AARCH64_) + mc->mc_gpregs.gp_x[19] = ((long*)mctx)[0]; + mc->mc_gpregs.gp_x[20] = ((long*)mctx)[1]; + mc->mc_gpregs.gp_x[21] = ((long*)mctx)[2]; + mc->mc_gpregs.gp_x[22] = ((long*)mctx)[3]; + mc->mc_gpregs.gp_x[23] = ((long*)mctx)[4]; + mc->mc_gpregs.gp_x[24] = ((long*)mctx)[5]; + mc->mc_gpregs.gp_x[25] = ((long*)mctx)[6]; + mc->mc_gpregs.gp_x[26] = ((long*)mctx)[7]; + mc->mc_gpregs.gp_x[27] = ((long*)mctx)[8]; + mc->mc_gpregs.gp_x[28] = ((long*)mctx)[9]; + mc->mc_gpregs.gp_x[29] = ((long*)mctx)[10]; + mc->mc_gpregs.gp_lr = ((long*)mctx)[11]; + mc->mc_gpregs.gp_sp = ((long*)mctx)[12]; + mc->mc_fpregs.fp_q[7] = ((long*)mctx)[13]; + mc->mc_fpregs.fp_q[8] = ((long*)mctx)[14]; + mc->mc_fpregs.fp_q[9] = ((long*)mctx)[15]; + mc->mc_fpregs.fp_q[10] = ((long*)mctx)[16]; + mc->mc_fpregs.fp_q[11] = ((long*)mctx)[17]; + mc->mc_fpregs.fp_q[12] = ((long*)mctx)[18]; + mc->mc_fpregs.fp_q[13] = ((long*)mctx)[19]; + mc->mc_fpregs.fp_q[14] = ((long*)mctx)[20]; + mc->mc_gpregs.gp_x[0] = 1; + assert(mc->mc_gpregs.gp_sp % 16 == 0); + return 1; + #else + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown freebsd") + (void)mctx; + return 0; + #endif +#else +return 0; +#endif +} JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, size_t max_bt_size) JL_NOTSAFEPOINT { @@ -955,234 +1221,19 @@ JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, } if (context == NULL && (!t->ctx.copy_stack && t->ctx.started && t->ctx.ctx != NULL)) { // need to read the context from the task stored state + jl_jmp_buf *mctx = &t->ctx.ctx->uc_mcontext; #if defined(_OS_WINDOWS_) memset(&c, 0, sizeof(c)); - _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx->uc_mcontext; -#if defined(_CPU_X86_64_) - c.Rbx = mctx->Rbx; - c.Rsp = mctx->Rsp; - c.Rbp = mctx->Rbp; - c.Rsi = mctx->Rsi; - c.Rdi = mctx->Rdi; - c.R12 = mctx->R12; - c.R13 = mctx->R13; - c.R14 = mctx->R14; - c.R15 = mctx->R15; - c.Rip = mctx->Rip; - memcpy(&c.Xmm6, &mctx->Xmm6, 10 * sizeof(mctx->Xmm6)); // Xmm6-Xmm15 -#elif defined(_CPU_X86_) - c.Eip = mctx->Eip; - c.Esp = mctx->Esp; - c.Ebp = mctx->Ebp; -#else - #error Windows is currently only supported on x86 and x86_64 -#endif - context = &c; + if (jl_simulate_longjmp(*mctx, &c)) + context = &c; #elif defined(JL_HAVE_UNW_CONTEXT) context = t->ctx.ctx; #elif defined(JL_HAVE_UCONTEXT) context = jl_to_bt_context(t->ctx.ctx); #elif defined(JL_HAVE_ASM) memset(&c, 0, sizeof(c)); - #if defined(_OS_LINUX_) && defined(__GLIBC__) - __jmp_buf *mctx = &t->ctx.ctx->uc_mcontext->__jmpbuf; - mcontext_t *mc = &c.uc_mcontext; - #if defined(_CPU_X86_) - // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/i386/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/i386/longjmp.s - mc->gregs[REG_EBX] = (*mctx)[0]; - mc->gregs[REG_ESI] = (*mctx)[1]; - mc->gregs[REG_EDI] = (*mctx)[2]; - mc->gregs[REG_EBP] = (*mctx)[3]; - mc->gregs[REG_ESP] = (*mctx)[4]; - mc->gregs[REG_EIP] = (*mctx)[5]; - // ifdef PTR_DEMANGLE ? - mc->gregs[REG_ESP] = ptr_demangle(mc->gregs[REG_ESP]); - mc->gregs[REG_EIP] = ptr_demangle(mc->gregs[REG_EIP]); - context = &c; - #elif defined(_CPU_X86_64_) - // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/x86_64/setjmp.s - mc->gregs[REG_RBX] = (*mctx)[0]; - mc->gregs[REG_RBP] = (*mctx)[1]; - mc->gregs[REG_R12] = (*mctx)[2]; - mc->gregs[REG_R13] = (*mctx)[3]; - mc->gregs[REG_R14] = (*mctx)[4]; - mc->gregs[REG_R15] = (*mctx)[5]; - mc->gregs[REG_RSP] = (*mctx)[6]; - mc->gregs[REG_RIP] = (*mctx)[7]; - // ifdef PTR_DEMANGLE ? - mc->gregs[REG_RBP] = ptr_demangle(mc->gregs[REG_RBP]); - mc->gregs[REG_RSP] = ptr_demangle(mc->gregs[REG_RSP]); - mc->gregs[REG_RIP] = ptr_demangle(mc->gregs[REG_RIP]); - context = &c; - #elif defined(_CPU_ARM_) - // https://github.com/bminor/glibc/blame/master/sysdeps/arm/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/arm/include/bits/setjmp.h - // https://github.com/bminor/musl/blame/master/src/setjmp/arm/longjmp.S - mc->arm_sp = (*mctx)[0]; - mc->arm_lr = (*mctx)[1]; - mc->arm_r4 = (*mctx)[2]; // aka v1 - mc->arm_r5 = (*mctx)[3]; // aka v2 - mc->arm_r6 = (*mctx)[4]; // aka v3 - mc->arm_r7 = (*mctx)[5]; // aka v4 - mc->arm_r8 = (*mctx)[6]; // aka v5 - mc->arm_r9 = (*mctx)[7]; // aka v6 aka sb - mc->arm_r10 = (*mctx)[8]; // aka v7 aka sl - mc->arm_fp = (*mctx)[10]; // aka v8 aka r11 - // ifdef PTR_DEMANGLE ? - mc->arm_sp = ptr_demangle(mc->arm_sp); - mc->arm_lr = ptr_demangle(mc->arm_lr); - mc->arm_pc = mc->arm_lr; - context = &c; - #elif defined(_CPU_AARCH64_) - // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/aarch64/longjmp.s - // https://github.com/libunwind/libunwind/blob/ec171c9ba7ea3abb2a1383cee2988a7abd483a1f/src/aarch64/unwind_i.h#L62 - unw_fpsimd_context_t *mcfp = (unw_fpsimd_context_t*)&mc->__reserved; - mc->regs[19] = (*mctx)[0]; - mc->regs[20] = (*mctx)[1]; - mc->regs[21] = (*mctx)[2]; - mc->regs[22] = (*mctx)[3]; - mc->regs[23] = (*mctx)[4]; - mc->regs[24] = (*mctx)[5]; - mc->regs[25] = (*mctx)[6]; - mc->regs[26] = (*mctx)[7]; - mc->regs[27] = (*mctx)[8]; - mc->regs[28] = (*mctx)[9]; - mc->regs[29] = (*mctx)[10]; // aka fp - mc->regs[30] = (*mctx)[11]; // aka lr - // Yes, they did skip 12 why writing the code originally; and, no, I do not know why. - mc->sp = (*mctx)[13]; - mcfp->vregs[7] = (*mctx)[14]; // aka d8 - mcfp->vregs[8] = (*mctx)[15]; // aka d9 - mcfp->vregs[9] = (*mctx)[16]; // aka d10 - mcfp->vregs[10] = (*mctx)[17]; // aka d11 - mcfp->vregs[11] = (*mctx)[18]; // aka d12 - mcfp->vregs[12] = (*mctx)[19]; // aka d13 - mcfp->vregs[13] = (*mctx)[20]; // aka d14 - mcfp->vregs[14] = (*mctx)[21]; // aka d15 - // ifdef PTR_DEMANGLE ? - mc->sp = ptr_demangle(mc->sp); - mc->regs[30] = ptr_demangle(mc->regs[30]); - mc->pc = mc->regs[30]; - context = &c; - #else - #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown linux") - (void)mc; - (void)c; - (void)mctx; - #endif - #elif defined(_OS_DARWIN_) - sigjmp_buf *mctx = &t->ctx.ctx->uc_mcontext; - #if defined(_CPU_X86_64_) - // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/x86_64/_setjmp.s - x86_thread_state64_t *mc = (x86_thread_state64_t*)&c; - mc->__rbx = ((uint64_t*)mctx)[0]; - mc->__rbp = ((uint64_t*)mctx)[1]; - mc->__rsp = ((uint64_t*)mctx)[2]; - mc->__r12 = ((uint64_t*)mctx)[3]; - mc->__r13 = ((uint64_t*)mctx)[4]; - mc->__r14 = ((uint64_t*)mctx)[5]; - mc->__r15 = ((uint64_t*)mctx)[6]; - mc->__rip = ((uint64_t*)mctx)[7]; - // added in libsystem_platform 177.200.16 (macOS Mojave 10.14.3) - // prior to that _os_ptr_munge_token was (hopefully) typically 0, - // so x ^ 0 == x and this is a no-op - mc->__rbp = _OS_PTR_UNMUNGE(mc->__rbp); - mc->__rsp = _OS_PTR_UNMUNGE(mc->__rsp); - mc->__rip = _OS_PTR_UNMUNGE(mc->__rip); - context = &c; - #elif defined(_CPU_AARCH64_) - // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/arm64/setjmp.s - // https://github.com/apple/darwin-xnu/blob/main/osfmk/mach/arm/_structs.h - // https://github.com/llvm/llvm-project/blob/7714e0317520207572168388f22012dd9e152e9e/libunwind/src/Registers.hpp -> Registers_arm64 - arm_thread_state64_t *mc = (arm_thread_state64_t*)&c; - mc->__x[19] = ((uint64_t*)mctx)[0]; - mc->__x[20] = ((uint64_t*)mctx)[1]; - mc->__x[21] = ((uint64_t*)mctx)[2]; - mc->__x[22] = ((uint64_t*)mctx)[3]; - mc->__x[23] = ((uint64_t*)mctx)[4]; - mc->__x[24] = ((uint64_t*)mctx)[5]; - mc->__x[25] = ((uint64_t*)mctx)[6]; - mc->__x[26] = ((uint64_t*)mctx)[7]; - mc->__x[27] = ((uint64_t*)mctx)[8]; - mc->__x[28] = ((uint64_t*)mctx)[9]; - mc->__x[10] = ((uint64_t*)mctx)[10]; - mc->__x[11] = ((uint64_t*)mctx)[11]; - mc->__x[12] = ((uint64_t*)mctx)[12]; - // 13 is reserved/unused - double *mcfp = (double*)&mc[1]; - mcfp[7] = ((uint64_t*)mctx)[14]; // aka d8 - mcfp[8] = ((uint64_t*)mctx)[15]; // aka d9 - mcfp[9] = ((uint64_t*)mctx)[16]; // aka d10 - mcfp[10] = ((uint64_t*)mctx)[17]; // aka d11 - mcfp[11] = ((uint64_t*)mctx)[18]; // aka d12 - mcfp[12] = ((uint64_t*)mctx)[19]; // aka d13 - mcfp[13] = ((uint64_t*)mctx)[20]; // aka d14 - mcfp[14] = ((uint64_t*)mctx)[21]; // aka d15 - mc->__fp = _OS_PTR_UNMUNGE(mc->__x[10]); - mc->__lr = _OS_PTR_UNMUNGE(mc->__x[11]); - mc->__x[12] = _OS_PTR_UNMUNGE(mc->__x[12]); - mc->__sp = mc->__x[12]; - // libunwind is broken for signed-pointers, but perhaps best not to leave the signed pointer lying around either - mc->__pc = ptrauth_strip(mc->__lr, 0); - mc->__pad = 0; // aka __ra_sign_state = not signed - context = &c; - #else - #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown darwin") - (void)mctx; - (void)c; - #endif - #elif defined(_OS_FREEBSD_) - sigjmp_buf *mctx = &t->ctx.ctx->uc_mcontext; - mcontext_t *mc = &c.uc_mcontext; - #if defined(_CPU_X86_64_) - // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S - mc->mc_rip = ((long*)mctx)[0]; - mc->mc_rbx = ((long*)mctx)[1]; - mc->mc_rsp = ((long*)mctx)[2]; - mc->mc_rbp = ((long*)mctx)[3]; - mc->mc_r12 = ((long*)mctx)[4]; - mc->mc_r13 = ((long*)mctx)[5]; - mc->mc_r14 = ((long*)mctx)[6]; - mc->mc_r15 = ((long*)mctx)[7]; - context = &c; - #elif defined(_CPU_AARCH64_) - mc->mc_gpregs.gp_x[19] = ((long*)mctx)[0]; - mc->mc_gpregs.gp_x[20] = ((long*)mctx)[1]; - mc->mc_gpregs.gp_x[21] = ((long*)mctx)[2]; - mc->mc_gpregs.gp_x[22] = ((long*)mctx)[3]; - mc->mc_gpregs.gp_x[23] = ((long*)mctx)[4]; - mc->mc_gpregs.gp_x[24] = ((long*)mctx)[5]; - mc->mc_gpregs.gp_x[25] = ((long*)mctx)[6]; - mc->mc_gpregs.gp_x[26] = ((long*)mctx)[7]; - mc->mc_gpregs.gp_x[27] = ((long*)mctx)[8]; - mc->mc_gpregs.gp_x[28] = ((long*)mctx)[9]; - mc->mc_gpregs.gp_x[29] = ((long*)mctx)[10]; - mc->mc_gpregs.gp_lr = ((long*)mctx)[11]; - mc->mc_gpregs.gp_sp = ((long*)mctx)[12]; - mc->mc_fpregs.fp_q[7] = ((long*)mctx)[13]; - mc->mc_fpregs.fp_q[8] = ((long*)mctx)[14]; - mc->mc_fpregs.fp_q[9] = ((long*)mctx)[15]; - mc->mc_fpregs.fp_q[10] = ((long*)mctx)[16]; - mc->mc_fpregs.fp_q[11] = ((long*)mctx)[17]; - mc->mc_fpregs.fp_q[12] = ((long*)mctx)[18]; - mc->mc_fpregs.fp_q[13] = ((long*)mctx)[19]; - mc->mc_fpregs.fp_q[14] = ((long*)mctx)[20]; - context = &c; - #else - #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown freebsd") - (void)mctx; - (void)c; - #endif - #else - #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown system") - (void)c; - #endif + if (jl_simulate_longjmp(*mctx, &c)) + context = &c; #else #pragma message("jl_record_backtrace not defined for unknown task system") #endif diff --git a/src/task.c b/src/task.c index 86acac23a186a..f86e0ab3a880d 100644 --- a/src/task.c +++ b/src/task.c @@ -771,48 +771,31 @@ JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e, jl_task_t *ct) #define pop_timings_stack() /* Nothing */ #endif -#define throw_internal_body(altstack) \ - assert(!jl_get_safe_restore()); \ - jl_ptls_t ptls = ct->ptls; \ - ptls->io_wait = 0; \ - jl_gc_unsafe_enter(ptls); \ - if (exception) { \ - /* The temporary ptls->bt_data is rooted by special purpose code in the\ - GC. This exists only for the purpose of preserving bt_data until we \ - set ptls->bt_size=0 below. */ \ - jl_push_excstack(ct, &ct->excstack, exception, \ - ptls->bt_data, ptls->bt_size); \ - ptls->bt_size = 0; \ - } \ - assert(ct->excstack && ct->excstack->top); \ - jl_handler_t *eh = ct->eh; \ - if (eh != NULL) { \ - if (altstack) ptls->sig_exception = NULL; \ - pop_timings_stack() \ - asan_unpoison_task_stack(ct, &eh->eh_ctx); \ - jl_longjmp(eh->eh_ctx, 1); \ - } \ - else { \ - jl_no_exc_handler(exception, ct); \ - } \ - assert(0); - static void JL_NORETURN throw_internal(jl_task_t *ct, jl_value_t *exception JL_MAYBE_UNROOTED) { -CFI_NORETURN JL_GC_PUSH1(&exception); - throw_internal_body(0); - jl_unreachable(); -} - -/* On the signal stack, we don't want to create any asan frames, but we do on the - normal, stack, so we split this function in two, depending on which context - we're calling it in. This also lets us avoid making a GC frame on the altstack, - which might end up getting corrupted if we recur here through another signal. */ -JL_NO_ASAN static void JL_NORETURN throw_internal_altstack(jl_task_t *ct, jl_value_t *exception) -{ -CFI_NORETURN - throw_internal_body(1); + jl_ptls_t ptls = ct->ptls; + ptls->io_wait = 0; + jl_gc_unsafe_enter(ptls); + if (exception) { + /* The temporary ptls->bt_data is rooted by special purpose code in the\ + GC. This exists only for the purpose of preserving bt_data until we + set ptls->bt_size=0 below. */ + jl_push_excstack(ct, &ct->excstack, exception, + ptls->bt_data, ptls->bt_size); + ptls->bt_size = 0; + } + assert(ct->excstack && ct->excstack->top); + jl_handler_t *eh = ct->eh; + if (eh != NULL) { + pop_timings_stack() + asan_unpoison_task_stack(ct, &eh->eh_ctx); + jl_longjmp(eh->eh_ctx, 1); + } + else { + jl_no_exc_handler(exception, ct); + } + assert(0); jl_unreachable(); } @@ -842,24 +825,6 @@ JL_DLLEXPORT void jl_rethrow(void) throw_internal(ct, NULL); } -// Special case throw for errors detected inside signal handlers. This is not -// (cannot be) called directly in the signal handler itself, but is returned to -// after the signal handler exits. -JL_DLLEXPORT JL_NO_ASAN void JL_NORETURN jl_sig_throw(void) -{ -CFI_NORETURN - jl_jmp_buf *safe_restore = jl_get_safe_restore(); - jl_task_t *ct = jl_current_task; - if (safe_restore) { - asan_unpoison_task_stack(ct, safe_restore); - jl_longjmp(*safe_restore, 1); - } - jl_ptls_t ptls = ct->ptls; - jl_value_t *e = ptls->sig_exception; - JL_GC_PROMISE_ROOTED(e); - throw_internal_altstack(ct, e); -} - JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED) { // TODO: Should uses of `rethrow(exc)` be replaced with a normal throw, now diff --git a/src/threading.c b/src/threading.c index a6050ace01833..2f3719b89fac3 100644 --- a/src/threading.c +++ b/src/threading.c @@ -74,6 +74,16 @@ JL_DLLEXPORT jl_jmp_buf *jl_get_safe_restore(void) JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr) { +#ifdef _OS_DARWIN_ + jl_task_t *ct = jl_get_current_task(); + if (ct != NULL && ct->ptls) { + if (sr == NULL) + pthread_setspecific(jl_safe_restore_key, (void*)sr); + ct->ptls->safe_restore = sr; + if (sr == NULL) + return; + } +#endif pthread_setspecific(jl_safe_restore_key, (void*)sr); } #endif From 7ce90a399669976899bf4a940864a54bc9ec8ac9 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Tue, 3 Sep 2024 16:25:33 +0900 Subject: [PATCH 164/548] fix `exct` for mismatched opaque closure call --- base/compiler/abstractinterpretation.jl | 2 +- test/compiler/inference.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f2d4327668137..5141a0e3c19b6 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2339,7 +2339,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, ocargsig′ = unwrap_unionall(ocargsig) ocargsig′ isa DataType || return CallMeta(Any, Any, Effects(), NoCallInfo()) ocsig = rewrap_unionall(Tuple{Tuple, ocargsig′.parameters...}, ocargsig) - hasintersect(sig, ocsig) || return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + hasintersect(sig, ocsig) || return CallMeta(Union{}, Union{MethodError,TypeError}, EFFECTS_THROWS, NoCallInfo()) ocmethod = closure.source::Method result = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) (; rt, edge, effects, volatile_inf_result) = result diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7e1fea54830c9..d96e6351aa437 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6089,3 +6089,11 @@ end == Union{} f = issue55627_make_oc() return f(1), f(xs...) end == Tuple{Int,Int} +@test Base.infer_exception_type() do + f = issue55627_make_oc() + return f(1), f() +end >: MethodError +@test Base.infer_exception_type() do + f = issue55627_make_oc() + return f(1), f('1') +end >: TypeError From 8f6a3ef4cb65692b0e6e0d07c9c909d1428187da Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Tue, 3 Sep 2024 16:32:25 +0900 Subject: [PATCH 165/548] improve `exct` modeling for opaque closure calls --- base/compiler/abstractinterpretation.jl | 23 +++++++++++++---------- test/compiler/inference.jl | 12 ++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 5141a0e3c19b6..5d3208d40ece3 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -42,7 +42,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), sv::AbsIntState, max_methods::Int) 𝕃ₚ, 𝕃ᵢ = ipo_lattice(interp), typeinf_lattice(interp) - ⊑ₚ, ⊔ₚ, ⊔ᵢ = partialorder(𝕃ₚ), join(𝕃ₚ), join(𝕃ᵢ) + ⊑ₚ, ⋤ₚ, ⊔ₚ, ⊔ᵢ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ), join(𝕃ᵢ) argtypes = arginfo.argtypes matches = find_method_matches(interp, argtypes, atype; max_methods) if isa(matches, FailedMethodMatch) @@ -97,7 +97,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), else add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") end - if !(exct ⊑ₚ const_call_result.exct) + if const_call_result.exct ⋤ exct exct = const_call_result.exct (; const_result, edge) = const_call_result else @@ -154,7 +154,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end # Treat the exception type separately. Currently, constprop often cannot determine the exception type # because consistent-cy does not apply to exceptions. - if !(this_exct ⊑ₚ const_call_result.exct) + if const_call_result.exct ⋤ this_exct this_exct = const_call_result.exct (; const_result, edge) = const_call_result else @@ -2342,10 +2342,10 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, hasintersect(sig, ocsig) || return CallMeta(Union{}, Union{MethodError,TypeError}, EFFECTS_THROWS, NoCallInfo()) ocmethod = closure.source::Method result = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) - (; rt, edge, effects, volatile_inf_result) = result + (; rt, exct, edge, effects, volatile_inf_result) = result match = MethodMatch(sig, Core.svec(), ocmethod, sig <: ocsig) 𝕃ₚ = ipo_lattice(interp) - ⊑ₚ = ⊑(𝕃ₚ) + ⊑ₚ, ⋤ₚ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ) const_result = volatile_inf_result if !result.edgecycle const_call_result = abstract_call_method_with_const_args(interp, result, @@ -2354,20 +2354,23 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, if const_call_result.rt ⊑ₚ rt (; rt, effects, const_result, edge) = const_call_result end + if const_call_result.exct ⋤ₚ exct + (; exct, const_result, edge) = const_call_result + end end end if check # analyze implicit type asserts on argument and return type - ftt = closure.typ - (aty, rty) = (unwrap_unionall(ftt)::DataType).parameters - rty = rewrap_unionall(rty isa TypeVar ? rty.lb : rty, ftt) - if !(rt ⊑ₚ rty && tuple_tfunc(𝕃ₚ, arginfo.argtypes[2:end]) ⊑ₚ rewrap_unionall(aty, ftt)) + rty = (unwrap_unionall(tt)::DataType).parameters[2] + rty = rewrap_unionall(rty isa TypeVar ? rty.ub : rty, tt) + if !(rt ⊑ₚ rty && sig ⊑ₚ ocsig) effects = Effects(effects; nothrow=false) + exct = tmerge(𝕃ₚ, exct, TypeError) end end rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) info = OpaqueClosureCallInfo(match, const_result) edge !== nothing && add_backedge!(sv, edge) - return CallMeta(rt, Any, effects, info) + return CallMeta(rt, exct, effects, info) end function most_general_argtypes(closure::PartialOpaque) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d96e6351aa437..abd9b7b4e4d1b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6097,3 +6097,15 @@ end >: MethodError f = issue55627_make_oc() return f(1), f('1') end >: TypeError + +# `exct` modeling for opaque closure +oc_exct_1() = Base.Experimental.@opaque function (x) + return x < 0 ? throw(x) : x + end +@test Base.infer_exception_type((Int,)) do x + oc_exct_1()(x) +end == Int +oc_exct_2() = Base.Experimental.@opaque Tuple{Number}->Number (x) -> '1' +@test Base.infer_exception_type((Int,)) do x + oc_exct_2()(x) +end == TypeError From f87d16414dc5cdfd14224322ccd354bb05d41954 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Tue, 3 Sep 2024 17:00:21 +0900 Subject: [PATCH 166/548] fix `nothrow` modeling for `invoke` calls --- base/compiler/abstractinterpretation.jl | 3 +++ test/compiler/inference.jl | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 5d3208d40ece3..4136ce02f6b2e 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2181,6 +2181,9 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt rt = from_interprocedural!(interp, rt, sv, arginfo, sig) info = InvokeCallInfo(match, const_result) edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) + if !match.fully_covers + effects = Effects(effects; nothrow=false) + end return CallMeta(rt, Any, effects, info) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index abd9b7b4e4d1b..a282a8911ce23 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6109,3 +6109,13 @@ oc_exct_2() = Base.Experimental.@opaque Tuple{Number}->Number (x) -> '1' @test Base.infer_exception_type((Int,)) do x oc_exct_2()(x) end == TypeError + +# nothrow modeling for `invoke` calls +f_invoke_nothrow(::Number) = :number +f_invoke_nothrow(::Int) = :int +@test Base.infer_effects((Int,)) do x + @invoke f_invoke_nothrow(x::Number) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Union{Nothing,Int},)) do x + @invoke f_invoke_nothrow(x::Number) +end |> !Core.Compiler.is_nothrow From 94829611fe2fa6779aff90c54ef06f13def8bc14 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Tue, 3 Sep 2024 17:06:21 +0900 Subject: [PATCH 167/548] improve `exct` modeling for `invoke` calls --- base/compiler/abstractinterpretation.jl | 37 ++++++++++++++----------- test/compiler/inference.jl | 30 ++++++++++++++++---- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4136ce02f6b2e..8623a32ddbb2b 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -98,8 +98,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") end if const_call_result.exct ⋤ exct - exct = const_call_result.exct - (; const_result, edge) = const_call_result + (; exct, const_result, edge) = const_call_result else add_remark!(interp, sv, "[constprop] Discarded exception type because result was wider than inference") end @@ -2135,12 +2134,13 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) isexact || return CallMeta(Any, Any, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) - if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name - return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + types === Bottom && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + if !(unwrapped isa DataType && unwrapped.name === Tuple.name) + return CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo()) end argtype = argtypes_to_type(argtype_tail(argtypes, 4)) nargtype = typeintersect(types, argtype) - nargtype === Bottom && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + nargtype === Bottom && return CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo()) nargtype isa DataType || return CallMeta(Any, Any, Effects(), NoCallInfo()) # other cases are not implemented below isdispatchelem(ft) || return CallMeta(Any, Any, Effects(), NoCallInfo()) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below ft = ft::DataType @@ -2154,7 +2154,7 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt tienv = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector ti = tienv[1]; env = tienv[2]::SimpleVector result = abstract_call_method(interp, method, ti, env, false, si, sv) - (; rt, edge, effects, volatile_inf_result) = result + (; rt, exct, edge, effects, volatile_inf_result) = result match = MethodMatch(ti, env, method, argtype <: method.sig) res = nothing sig = match.spec_types @@ -2168,23 +2168,28 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt # argtypes′[i] = t ⊑ a ? t : a # end 𝕃ₚ = ipo_lattice(interp) + ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) f = singleton_type(ft′) invokecall = InvokeCall(types, lookupsig) const_call_result = abstract_call_method_with_const_args(interp, result, f, arginfo, si, match, sv, invokecall) const_result = volatile_inf_result if const_call_result !== nothing - if ⊑(𝕃ₚ, const_call_result.rt, rt) + if const_call_result.rt ⊑ rt (; rt, effects, const_result, edge) = const_call_result end + if const_call_result.exct ⋤ exct + (; exct, const_result, edge) = const_call_result + end end rt = from_interprocedural!(interp, rt, sv, arginfo, sig) info = InvokeCallInfo(match, const_result) edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) if !match.fully_covers effects = Effects(effects; nothrow=false) + exct = exct ⊔ TypeError end - return CallMeta(rt, Any, effects, info) + return CallMeta(rt, exct, effects, info) end function invoke_rewrite(xs::Vector{Any}) @@ -2205,16 +2210,16 @@ end function abstract_throw(interp::AbstractInterpreter, argtypes::Vector{Any}, ::AbsIntState) na = length(argtypes) - 𝕃ᵢ = typeinf_lattice(interp) + ⊔ = join(typeinf_lattice(interp)) if na == 2 argtype2 = argtypes[2] if isvarargtype(argtype2) - exct = tmerge(𝕃ᵢ, unwrapva(argtype2), ArgumentError) + exct = unwrapva(argtype2) ⊔ ArgumentError else exct = argtype2 end elseif na == 3 && isvarargtype(argtypes[3]) - exct = tmerge(𝕃ᵢ, argtypes[2], ArgumentError) + exct = argtypes[2] ⊔ ArgumentError else exct = ArgumentError end @@ -2348,16 +2353,16 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, (; rt, exct, edge, effects, volatile_inf_result) = result match = MethodMatch(sig, Core.svec(), ocmethod, sig <: ocsig) 𝕃ₚ = ipo_lattice(interp) - ⊑ₚ, ⋤ₚ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ) + ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) const_result = volatile_inf_result if !result.edgecycle const_call_result = abstract_call_method_with_const_args(interp, result, nothing, arginfo, si, match, sv) if const_call_result !== nothing - if const_call_result.rt ⊑ₚ rt + if const_call_result.rt ⊑ rt (; rt, effects, const_result, edge) = const_call_result end - if const_call_result.exct ⋤ₚ exct + if const_call_result.exct ⋤ exct (; exct, const_result, edge) = const_call_result end end @@ -2365,9 +2370,9 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, if check # analyze implicit type asserts on argument and return type rty = (unwrap_unionall(tt)::DataType).parameters[2] rty = rewrap_unionall(rty isa TypeVar ? rty.ub : rty, tt) - if !(rt ⊑ₚ rty && sig ⊑ₚ ocsig) + if !(rt ⊑ rty && sig ⊑ ocsig) effects = Effects(effects; nothrow=false) - exct = tmerge(𝕃ₚ, exct, TypeError) + exct = exct ⊔ TypeError end end rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index a282a8911ce23..f15df49d75745 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6078,9 +6078,7 @@ gcondvarargs(a, x...) = return fcondvarargs(a, x...) ? isa(a, Int64) : !isa(a, I @test Core.Compiler.return_type(gcondvarargs, Tuple{Vararg{Any}}) === Bool # JuliaLang/julia#55627: argtypes check in `abstract_call_opaque_closure` -issue55627_some_method(x) = 2x -issue55627_make_oc() = Base.Experimental.@opaque (x::Int)->issue55627_some_method(x) - +issue55627_make_oc() = Base.Experimental.@opaque (x::Int) -> 2x @test Base.infer_return_type() do f = issue55627_make_oc() return f(1), f() @@ -6099,9 +6097,7 @@ end >: MethodError end >: TypeError # `exct` modeling for opaque closure -oc_exct_1() = Base.Experimental.@opaque function (x) - return x < 0 ? throw(x) : x - end +oc_exct_1() = Base.Experimental.@opaque (x) -> x < 0 ? throw(x) : x @test Base.infer_exception_type((Int,)) do x oc_exct_1()(x) end == Int @@ -6116,6 +6112,28 @@ f_invoke_nothrow(::Int) = :int @test Base.infer_effects((Int,)) do x @invoke f_invoke_nothrow(x::Number) end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Char,)) do x + @invoke f_invoke_nothrow(x::Number) +end |> !Core.Compiler.is_nothrow @test Base.infer_effects((Union{Nothing,Int},)) do x @invoke f_invoke_nothrow(x::Number) end |> !Core.Compiler.is_nothrow + +# `exct` modeling for `invoke` calls +f_invoke_exct(x::Number) = x < 0 ? throw(x) : x +f_invoke_exct(x::Int) = x +@test Base.infer_exception_type((Int,)) do x + @invoke f_invoke_exct(x::Number) +end == Int +@test Base.infer_exception_type() do + @invoke f_invoke_exct(42::Number) +end == Union{} +@test Base.infer_exception_type((Union{Nothing,Int},)) do x + @invoke f_invoke_exct(x::Number) +end == Union{Int,TypeError} +@test Base.infer_exception_type((Int,)) do x + invoke(f_invoke_exct, Number, x) +end == TypeError +@test Base.infer_exception_type((Char,)) do x + invoke(f_invoke_exct, Tuple{Number}, x) +end == TypeError From 2f0607f419efe0eec405250f3df94878c5928f62 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 4 Sep 2024 07:26:41 -0400 Subject: [PATCH 168/548] show a bit more detail when finished precompiling (#55660) --- base/precompilation.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 32fdb86bbdb07..d3f076633f386 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -919,8 +919,12 @@ function precompilepkgs(pkgs::Vector{String}=String[]; seconds_elapsed = round(Int, (time_ns() - time_start) / 1e9) ndeps = count(values(was_recompiled)) if ndeps > 0 || !isempty(failed_deps) || (quick_exit && !isempty(std_outputs)) - str = sprint() do iostr + str = sprint(context=io) do iostr if !quick_exit + if fancyprint # replace the progress bar + what = isempty(requested_pkgs) ? "packages finished." : "$(join(requested_pkgs, ", ", " and ")) finished." + printpkgstyle(iostr, :Precompiling, what) + end plural = length(configs) > 1 ? "dependency configurations" : ndeps == 1 ? "dependency" : "dependencies" print(iostr, " $(ndeps) $(plural) successfully precompiled in $(seconds_elapsed) seconds") if n_already_precomp > 0 || !isempty(circular_deps) From 53d3ca9855db0308ddf2044a3a0f21f3de492cf3 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 4 Sep 2024 21:08:14 +0800 Subject: [PATCH 169/548] subtype: minor clean up for fast path for lhs union and rhs typevar (#55645) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up #55413. The error pattern mentioned in https://github.com/JuliaLang/julia/pull/55413#issuecomment-2288384468 care's `∃y`'s ub in env rather than its original ub. So it seems more robust to check the bounds in env directly. The equivalent typevar propagation is lifted from `subtype_var` for the same reason. --- src/subtype.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 4118bbeab649b..2011ca8b1c705 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1304,6 +1304,7 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in } static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e); +static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT; // `param` means we are currently looking at a parameter of a type constructor // (as opposed to being outside any type constructor, or comparing variable bounds). @@ -1314,7 +1315,7 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) if (jl_is_uniontype(x)) { if (obviously_egal(x, y)) return 1; - if (e->Runions.depth == 0 && jl_is_typevar(y) && !jl_has_free_typevars(x) && !jl_has_free_typevars(((jl_tvar_t*)y)->ub)) { + if (e->Runions.depth == 0 && jl_is_typevar(y) && !jl_has_free_typevars(x)) { // Similar to fast path for repeated elements: if there have been no outer // unions on the right, and the right side is a typevar, then we can handle the // typevar first before picking a union element, under the theory that it may @@ -1325,7 +1326,17 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) // free typevars, since the typevars presence might lead to those elements // getting eliminated (omit_bad_union) or degenerate (Union{Ptr{T}, Ptr}) or // combined (Union{T, S} where {T, S <: T}). - return subtype_var((jl_tvar_t*)y, x, e, 1, param); + jl_tvar_t *yvar = (jl_tvar_t *)y; + jl_varbinding_t *yb = lookup(e, yvar); + while (e->intersection && yb != NULL && yb->lb == yb->ub && jl_is_typevar(yb->lb)) { + yvar = (jl_tvar_t *)yb->lb; + yb = lookup(e, yvar); + } + // Note: `x <: ∃y` performs a local ∀-∃ check between `x` and `yb->ub`. + // We need to ensure that there's no ∃ typevar as otherwise that check + // might cause false alarm due to the accumulated env change. + if (yb == NULL || yb->right == 0 || !has_exists_typevar(yb->ub, e)) + return subtype_var(yvar, x, e, 1, param); } x = pick_union_element(x, e, 0); } From e8188631cd019d86b4899b202b4312041febff73 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Thu, 5 Sep 2024 00:05:49 +1000 Subject: [PATCH 170/548] Adding `JL_DATA_TYPE` annotation to `_jl_globalref_t` (#55684) `_jl_globalref_t` seems to be allocated in the heap, and there is an object `jl_globalref_type` which indicates that it is in fact, a data type, thus it should be annotated with `JL_DATA_TYPE`?? --- src/julia.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/julia.h b/src/julia.h index 589a5745ff59f..efdd6a1b08bf7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -718,6 +718,7 @@ typedef struct _jl_module_t { } jl_module_t; struct _jl_globalref_t { + JL_DATA_TYPE jl_module_t *mod; jl_sym_t *name; jl_binding_t *binding; From 351727f44651d995ef061c76a7ee058b37661111 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 4 Sep 2024 11:13:05 -0300 Subject: [PATCH 171/548] Make GEP when loading the PTLS an inbounds one. (#55682) Non inbounds GEPs should only be used when doing pointer arithmethic i.e Ptr or MemoryRef boundscheck. Found when auditing non inbounds GEPs for https://github.com/JuliaLang/julia/pull/55681 --- src/llvm-ptls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 9e49aa5ba2f39..736c1acd9525a 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -128,7 +128,7 @@ Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefor offset = ConstantInt::getSigned(T_size, jl_tls_offset); auto tp = InlineAsm::get(FunctionType::get(PointerType::get(builder.getContext(), 0), false), asm_str, "=r", false); tls = builder.CreateCall(tp, {}, "thread_ptr"); - tls = builder.CreateGEP(Type::getInt8Ty(builder.getContext()), tls, {offset}, "tls_ppgcstack"); + tls = builder.CreateInBoundsGEP(Type::getInt8Ty(builder.getContext()), tls, {offset}, "tls_ppgcstack"); } return builder.CreateLoad(T_pppjlvalue, tls, "tls_pgcstack"); } From 68d04bad49515ea6f246f826be0fec7445e3e7ba Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 4 Sep 2024 11:16:00 -0300 Subject: [PATCH 172/548] codegen: make boundscheck GEP not be inbounds while the load GEP is inbounds (#55681) Avoids undefined behavior on the boundschecking arithmetic, which is correct only assuming overflow follows unsigned arithmetic wrap around rules. Also add names to the Memory related LLVM instructions to aid debugging Closes: https://github.com/JuliaLang/julia/pull/55674 --- src/cgutils.cpp | 36 ++++++++++++++++++++++++++++++------ src/codegen.cpp | 8 ++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 2d2d2aed22069..bec84d9901279 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3026,12 +3026,15 @@ static Value *emit_genericmemoryelsize(jl_codectx_t &ctx, Value *v, jl_value_t * size_t sz = sty->layout->size; if (sty->layout->flags.arrayelem_isunion) sz++; - return ConstantInt::get(ctx.types().T_size, sz); + auto elsize = ConstantInt::get(ctx.types().T_size, sz); + return elsize; } else { Value *t = emit_typeof(ctx, v, false, false, true); Value *elsize = emit_datatype_size(ctx, t, add_isunion); - return ctx.builder.CreateZExt(elsize, ctx.types().T_size); + elsize = ctx.builder.CreateZExt(elsize, ctx.types().T_size); + setName(ctx.emission_context, elsize, "elsize"); + return elsize; } } @@ -3066,6 +3069,7 @@ static Value *emit_genericmemorylen(jl_codectx_t &ctx, Value *addr, jl_value_t * MDBuilder MDB(ctx.builder.getContext()); auto rng = MDB.createRange(Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, genericmemoryype_maxsize(typ))); LI->setMetadata(LLVMContext::MD_range, rng); + setName(ctx.emission_context, LI, "memory_len"); return LI; } @@ -3075,7 +3079,7 @@ static Value *emit_genericmemoryptr(jl_codectx_t &ctx, Value *mem, const jl_data Value *addr = mem; addr = decay_derived(ctx, addr); addr = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, addr, 1); - setName(ctx.emission_context, addr, ".data_ptr"); + setName(ctx.emission_context, addr, "memory_data_ptr"); PointerType *PPT = cast(ctx.types().T_jlgenericmemory->getElementType(1)); LoadInst *LI = ctx.builder.CreateAlignedLoad(PPT, addr, Align(sizeof(char*))); LI->setOrdering(AtomicOrdering::NotAtomic); @@ -3087,6 +3091,7 @@ static Value *emit_genericmemoryptr(jl_codectx_t &ctx, Value *mem, const jl_data assert(AS == AddressSpace::Loaded); ptr = ctx.builder.CreateCall(prepare_call(gc_loaded_func), { mem, ptr }); } + setName(ctx.emission_context, ptr, "memory_data"); return ptr; } @@ -4195,6 +4200,7 @@ static jl_cgval_t _emit_memoryref(jl_codectx_t &ctx, Value *mem, Value *data, co Value *ref = Constant::getNullValue(get_memoryref_type(ctx.builder.getContext(), ctx.types().T_size, layout, 0)); ref = ctx.builder.CreateInsertValue(ref, data, 0); ref = ctx.builder.CreateInsertValue(ref, mem, 1); + setName(ctx.emission_context, ref, "memory_ref"); return mark_julia_type(ctx, ref, false, typ); } @@ -4215,6 +4221,7 @@ static Value *emit_memoryref_FCA(jl_codectx_t &ctx, const jl_cgval_t &ref, const LoadInst *load = ctx.builder.CreateLoad(type, data_pointer(ctx, ref)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ref.tbaa); ai.decorateInst(load); + setName(ctx.emission_context, load, "memory_ref_FCA"); return load; } else { @@ -4231,9 +4238,12 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg return jl_cgval_t(); Value *V = emit_memoryref_FCA(ctx, ref, layout); Value *data = CreateSimplifiedExtractValue(ctx, V, 0); + maybeSetName(ctx.emission_context, data, "memoryref_data"); Value *mem = CreateSimplifiedExtractValue(ctx, V, 1); + maybeSetName(ctx.emission_context, mem, "memoryref_mem"); Value *i = emit_unbox(ctx, ctx.types().T_size, idx, (jl_value_t*)jl_long_type); Value *offset = ctx.builder.CreateSub(i, ConstantInt::get(ctx.types().T_size, 1)); + setName(ctx.emission_context, offset, "memoryref_offset"); Value *elsz = emit_genericmemoryelsize(ctx, mem, ref.typ, false); bool bc = bounds_check_enabled(ctx, inbounds); #if 1 @@ -4245,12 +4255,14 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg bool isghost = layout->size == 0; if ((!isboxed && isunion) || isghost) { newdata = ctx.builder.CreateAdd(data, offset); + setName(ctx.emission_context, newdata, "memoryref_data+offset"); if (bc) { BasicBlock *failBB, *endBB; failBB = BasicBlock::Create(ctx.builder.getContext(), "oob"); endBB = BasicBlock::Create(ctx.builder.getContext(), "idxend"); Value *mlen = emit_genericmemorylen(ctx, mem, ref.typ); Value *inbound = ctx.builder.CreateICmpULT(newdata, mlen); + setName(ctx.emission_context, offset, "memoryref_isinbounds"); ctx.builder.CreateCondBr(inbound, endBB, failBB); failBB->insertInto(ctx.f); ctx.builder.SetInsertPoint(failBB); @@ -4278,10 +4290,13 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg // and we can further rearrange that as ovflw = !( offset+len < len+len ) as unsigned math Value *mlen = emit_genericmemorylen(ctx, mem, ref.typ); ovflw = ctx.builder.CreateICmpUGE(ctx.builder.CreateAdd(offset, mlen), ctx.builder.CreateNUWAdd(mlen, mlen)); + setName(ctx.emission_context, ovflw, "memoryref_ovflw"); } #endif boffset = ctx.builder.CreateMul(offset, elsz); - newdata = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), data, boffset); + setName(ctx.emission_context, boffset, "memoryref_byteoffset"); + newdata = ctx.builder.CreateGEP(getInt8Ty(ctx.builder.getContext()), data, boffset); + setName(ctx.emission_context, newdata, "memoryref_data_byteoffset"); (void)boffset; // LLVM is very bad at handling GEP with types different from the load if (bc) { BasicBlock *failBB, *endBB; @@ -4304,8 +4319,11 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg ctx.builder.CreatePtrToInt(newdata, ctx.types().T_size), ctx.builder.CreatePtrToInt(mptr, ctx.types().T_size)); Value *blen = ctx.builder.CreateMul(mlen, elsz, "", true, true); + setName(ctx.emission_context, blen, "memoryref_bytelen"); Value *inbound = ctx.builder.CreateICmpULT(bidx0, blen); + setName(ctx.emission_context, inbound, "memoryref_isinbounds"); inbound = ctx.builder.CreateAnd(ctx.builder.CreateNot(ovflw), inbound); + setName(ctx.emission_context, inbound, "memoryref_isinbounds¬ovflw"); #else Value *idx0; // (newdata - mptr) / elsz idx0 = ctx.builder.CreateSub( @@ -4342,8 +4360,10 @@ static jl_cgval_t emit_memoryref_offset(jl_codectx_t &ctx, const jl_cgval_t &ref offset = ctx.builder.CreateSub( ctx.builder.CreatePtrToInt(data, ctx.types().T_size), ctx.builder.CreatePtrToInt(mptr, ctx.types().T_size)); + setName(ctx.emission_context, offset, "memoryref_offset"); Value *elsz = emit_genericmemoryelsize(ctx, mem, ref.typ, false); offset = ctx.builder.CreateExactUDiv(offset, elsz); + setName(ctx.emission_context, offset, "memoryref_offsetidx"); } offset = ctx.builder.CreateAdd(offset, ConstantInt::get(ctx.types().T_size, 1)); return mark_julia_type(ctx, offset, false, jl_long_type); @@ -4352,7 +4372,9 @@ static jl_cgval_t emit_memoryref_offset(jl_codectx_t &ctx, const jl_cgval_t &ref static Value *emit_memoryref_mem(jl_codectx_t &ctx, const jl_cgval_t &ref, const jl_datatype_layout_t *layout) { Value *V = emit_memoryref_FCA(ctx, ref, layout); - return CreateSimplifiedExtractValue(ctx, V, 1); + V = CreateSimplifiedExtractValue(ctx, V, 1); + maybeSetName(ctx.emission_context, V, "memoryref_mem"); + return V; } static Value *emit_memoryref_ptr(jl_codectx_t &ctx, const jl_cgval_t &ref, const jl_datatype_layout_t *layout) @@ -4374,13 +4396,15 @@ static Value *emit_memoryref_ptr(jl_codectx_t &ctx, const jl_cgval_t &ref, const data = ctx.builder.CreateCall(prepare_call(gc_loaded_func), { mem, data }); if (!GEPlist.empty()) { for (auto &GEP : make_range(GEPlist.rbegin(), GEPlist.rend())) { - Instruction *GEP2 = GEP->clone(); + GetElementPtrInst *GEP2 = cast(GEP->clone()); GEP2->mutateType(PointerType::get(GEP->getResultElementType(), AS)); GEP2->setOperand(GetElementPtrInst::getPointerOperandIndex(), data); + GEP2->setIsInBounds(true); ctx.builder.Insert(GEP2); data = GEP2; } } + setName(ctx.emission_context, data, "memoryref_data"); return data; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 4091ec6c03db0..9f80791f2882d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -171,6 +171,14 @@ void setName(jl_codegen_params_t ¶ms, Value *V, const Twine &Name) } } +void maybeSetName(jl_codegen_params_t ¶ms, Value *V, const Twine &Name) +{ + // To be used when we may get an Instruction or something that is not an instruction i.e Constants/Arguments + if (params.debug_level >= 2 && isa(V)) { + V->setName(Name); + } +} + void setName(jl_codegen_params_t ¶ms, Value *V, std::function GetName) { assert((isa(V) || isa(V)) && "Should only set names on instructions!"); From e217f938d291854caaca4e5b81bbeb27c3e985ba Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:18:02 -0400 Subject: [PATCH 173/548] Make `rename` public (#55652) Fixes #41584. Follow up of #55503 I think `rename` is a very useful low-level file system operation. Many other programming languages have this function, so it is useful when porting IO code to Julia. One use case is to improve the Zarr.jl package to be more compatible with zarr-python. https://github.com/zarr-developers/zarr-python/blob/0b5483a7958e2ae5512a14eb424a84b2a75dd727/src/zarr/v2/storage.py#L994 uses the `os.replace` function. It would be nice to be able to directly use `Base.rename` as a replacement for `os.replace` to ensure compatibility. Another use case is writing a safe zip file extractor in pure Julia. https://github.com/madler/sunzip/blob/34107fa9e2a2e36e7e72725dc4c58c9ad6179898/sunzip.c#L365 uses the `rename` function to do this in C. Lastly in https://github.com/medyan-dev/MEDYANSimRunner.jl/blob/67d5b42cc599670486d5d640260a95e951091f7a/src/file-saving.jl#L83 I am using `ccall(:jl_fs_rename` to save files, because I have large numbers of Julia processes creating and reading these files at the same time on a distributed file system on a cluster, so I don't want data to become corrupted if one of the nodes crashes (which happens fairly regularly). However `jl_fs_rename` is not public, and might break in a future release. This PR also adds a note to `mv` comparing it to the `mv` command, similar to the note on the `cp` function. --- base/file.jl | 17 ++++++++++++++++- base/public.jl | 3 +++ doc/src/base/file.md | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index d45f34fb55dc6..567783c4b1e5b 100644 --- a/base/file.jl +++ b/base/file.jl @@ -385,7 +385,7 @@ of the file or directory `src` refers to. Return `dst`. !!! note - The `cp` function is different from the `cp` command. The `cp` function always operates on + The `cp` function is different from the `cp` Unix command. The `cp` function always operates on the assumption that `dst` is a file, while the command does different things depending on whether `dst` is a directory or a file. Using `force=true` when `dst` is a directory will result in loss of all the contents present @@ -438,6 +438,16 @@ julia> mv("hello.txt", "goodbye.txt", force=true) julia> rm("goodbye.txt"); ``` + +!!! note + The `mv` function is different from the `mv` Unix command. The `mv` function by + default will error if `dst` exists, while the command will delete + an existing `dst` file by default. + Also the `mv` function always operates on + the assumption that `dst` is a file, while the command does different things depending + on whether `dst` is a directory or a file. + Using `force=true` when `dst` is a directory will result in loss of all the contents present + in the `dst` directory, and `dst` will become a file that has the contents of `src` instead. """ function mv(src::AbstractString, dst::AbstractString; force::Bool=false) if force @@ -1192,6 +1202,8 @@ If a path contains a "\\0" throw an `ArgumentError`. On other failures throw an `IOError`. Return `newpath`. +This is a lower level filesystem operation used to implement [`mv`](@ref). + OS-specific restrictions may apply when `oldpath` and `newpath` are in different directories. Currently there are a few differences in behavior on Windows which may be resolved in a future release. @@ -1202,6 +1214,9 @@ Specifically, currently on Windows: 4. `rename` may remove `oldpath` if it is a hardlink to `newpath`. See also: [`mv`](@ref). + +!!! compat "Julia 1.12" + This method was made public in Julia 1.12. """ function rename(oldpath::AbstractString, newpath::AbstractString) err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), oldpath, newpath) diff --git a/base/public.jl b/base/public.jl index 862aff48da63e..803766a0cec1b 100644 --- a/base/public.jl +++ b/base/public.jl @@ -110,6 +110,9 @@ public reseteof, link_pipe!, +# filesystem operations + rename, + # misc notnothing, runtests, diff --git a/doc/src/base/file.md b/doc/src/base/file.md index 22799f882bb26..300738a39322d 100644 --- a/doc/src/base/file.md +++ b/doc/src/base/file.md @@ -29,6 +29,7 @@ Base.Filesystem.operm Base.Filesystem.cp Base.download Base.Filesystem.mv +Base.Filesystem.rename Base.Filesystem.rm Base.Filesystem.touch Base.Filesystem.tempname From 4aac5ae31e12721c60916ac79e0ca9f974fdbec0 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:29:16 -0400 Subject: [PATCH 174/548] contrib: include private libdir in `ldflags` on macOS (#55687) The private libdir is used on macOS, so it needs to be included in our `ldflags` --- contrib/julia-config.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/julia-config.jl b/contrib/julia-config.jl index df17b967c1ed7..c692b3f522fb2 100755 --- a/contrib/julia-config.jl +++ b/contrib/julia-config.jl @@ -67,9 +67,7 @@ function ldlibs(doframework) "julia" end if Sys.isunix() - return "-Wl,-rpath,$(shell_escape(libDir())) " * - (Sys.isapple() ? string() : "-Wl,-rpath,$(shell_escape(private_libDir())) ") * - "-l$libname" + return "-Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname" else return "-l$libname -lopenlibm" end From bada969c14fd8f58804b662f5cca44808bb52433 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 4 Sep 2024 18:35:33 -0400 Subject: [PATCH 175/548] Profile.print: Shorten C paths too (#55683) --- base/sysinfo.jl | 2 ++ stdlib/Profile/src/Profile.jl | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/base/sysinfo.jl b/base/sysinfo.jl index d0dcac8c6d416..7dab313cf4f57 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -56,6 +56,8 @@ global STDLIB::String = "$BINDIR/../share/julia/stdlib/v$(VERSION.major).$(VERSI # In case STDLIB change after julia is built, the variable below can be used # to update cached method locations to updated ones. const BUILD_STDLIB_PATH = STDLIB +# Similarly, this is the root of the julia repo directory that julia was built from +const BUILD_ROOT_PATH = "$BINDIR/../.." # helper to avoid triggering precompile warnings diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index a80e6c71e5aef..c7ef1efb35945 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -503,13 +503,23 @@ function flatten(data::Vector, lidict::LineInfoDict) return (newdata, newdict) end +const SRC_DIR = normpath(joinpath(Sys.BUILD_ROOT_PATH, "src")) + # Take a file-system path and try to form a concise representation of it # based on the package ecosystem function short_path(spath::Symbol, filenamecache::Dict{Symbol, Tuple{String,String,String}}) return get!(filenamecache, spath) do path = Base.fixup_stdlib_path(string(spath)) + path_norm = normpath(path) possible_base_path = normpath(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base", path)) - if isabspath(path) + lib_dir = abspath(Sys.BINDIR, Base.LIBDIR) + if startswith(path_norm, SRC_DIR) + remainder = only(split(path_norm, SRC_DIR, keepempty=false)) + return (isfile(path_norm) ? path_norm : ""), "@juliasrc", remainder + elseif startswith(path_norm, lib_dir) + remainder = only(split(path_norm, lib_dir, keepempty=false)) + return (isfile(path_norm) ? path_norm : ""), "@julialib", remainder + elseif isabspath(path) if ispath(path) # try to replace the file-system prefix with a short "@Module" one, # assuming that profile came from the current machine From 6f04ee0ff34f727dfd44199d6e1132d2235ebedd Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 5 Sep 2024 21:54:11 -0300 Subject: [PATCH 176/548] [LLVMLibUnwindJLL] Update llvmlibunwind to 14.0.6 (#48140) --- deps/checksums/llvmunwind | 32 ++++ deps/llvmunwind.version | 2 +- ...ibunwind-revert-monorepo-requirement.patch | 156 ------------------ deps/unwind.mk | 49 +++--- stdlib/LLVMLibUnwind_jll/Project.toml | 2 +- 5 files changed, 59 insertions(+), 182 deletions(-) delete mode 100644 deps/patches/llvm-libunwind-revert-monorepo-requirement.patch diff --git a/deps/checksums/llvmunwind b/deps/checksums/llvmunwind index e69de29bb2d1d..a90d28717dd85 100644 --- a/deps/checksums/llvmunwind +++ b/deps/checksums/llvmunwind @@ -0,0 +1,32 @@ +LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/md5/d8584e0e3dc26ea7404d3719cea9e233 +LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/sha512/7a0396eaace91b9b4d013c209605d468a7ff9b99ede9fdd57602539a6fa6f3ea84a440f32840056a1234df3ef1896739ea0820fee72b4f208096c553fc54adb9 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/md5/d6edea561b61173d05aa79936e49f6b7 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/sha512/9fbe29ec6a33c719bc9a4dd19911ceded9622269c042192d339a6cf45aa8209ad64c424167c094ca01293438af5930f091acba0538b3fe640a746297f5cc8cb3 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/md5/3ec68c87e4bddd024ee0ca6adc2b3b96 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/sha512/be3cd9d5510c2693dee1494c36c479d32311ff83f5b2d31c08508a3dd370788961ce46e9025afe148a0febd05942fd294370a357dd717bee353d8a108617f6de +LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/md5/8ca5a926d69124225d485d679232a54f +LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/sha512/353f540b342bc54877e7a41fe65c9eeac525fd91bf4cddbe1b3ec2ed93c3751beaf8316a4d31530502b067100b160301262e10cbe4407db3abf1ceb5d9a74eb2 +LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/md5/4e5b576958f2a2e708eb5918ceef0de0 +LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/sha512/2e98c472d3ee25c2e062efa4eb21ac9cfc49b26ea9d99ad4a8e7660c4c09f121d31193bd161f54ea332ce94785d601897311e9e6668adb1e25e2b666e0d5bb3f +LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/md5/1c81a886e799663ce8d04400c5b516a9 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/sha512/236b78b9a17eaae74ab07349ac8dde16c3abbd48e0d075abd1c195d60efff48e2fbf799554df114ea3d3dba937e0369430a2788bde2a1201126e026ef6cdac42 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/md5/0371f43ebcb571d0a635739252b88986 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/sha512/605318ae3737e26ff89d6291311a7db3bc3ec7c8d1f2e72ae40fd3d9df0754ee2ebfb77687122605f26d76d62effb85157bc39982814920d5af46c124e71a5ff +LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/md5/cd3f1cdf404b6102754ced4bd3a890f6 +LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/sha512/65fe2c5b1e04da1e1d8111a0b0083fa0fa9447eaea7af7a018c09fe6d5506566c491bbad296a7be8c488ca3495016ae16a6879d69f057f8866d94910147dee03 +LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/md5/abac9b416d2ba5abcf5ce849f43ffa96 +LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/sha512/fed677ed6f103c56eb9dd4578fa37a56ed2a4bc803aa1997c5af19762a623d2f82db1f72f429448d66fcef3b37af2104e6cb782f023aaabef086a921a862b042 +LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/md5/4c71ffd7c8cabb1c0ed6290b193883c5 +LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/sha512/6b1421a3268170467225112167cdb33fec962181993a2dad5594d4ee0623ac88ee0588cdc7d0656dc1cb9129ef96f621a97a224731cd161134d7d63c8fd32c16 +LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/md5/06faf505f0dc354afcd01113cfc57af2 +LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/sha512/1f9dfbd403e2ce121e126c217baede178cb1323012bb5e3cd1f778ff51e4216aed9dd69036e2baffbd60a6f5ae438ddaba6c13809459e94bb00be3f7bfc8c30e +LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/md5/516a11d99306e3f214968a7951b07a06 +LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/sha512/885738599bbd96f20083f9b9368ce3f243bd5868d3ac9a45189de6cb40b6664a6dcdaece159989e504670231db8c2addfa8d544003eb0cdabba960e4ab6a4470 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/md5/d851b90ea3f9664774316169fc494e21 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/sha512/a1f529454f0881baaa508481ba97ecffb040fa92141b4cbc72278adcf8b84f0766fa918aea7fb99ce690c4fd80c36fec365987625db42f4e7bb36ad24ce177d0 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/md5/dc4e86eb2effe1f6cb0d0ceda635f226 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/sha512/c52de384853890f9df81aa9e422c1ba3fde12b2ae9c7b60b9ecdc6d0c88eab495dd336af2b6cd2c31d6eddcd0a213954eadbc7884bc39ce2039cec672eac32fe +LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/md5/8477e3624c73a820d8ab82a53e1e10fa +LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/sha512/32ce031245a5b59a779cd77fa3c9bf05ee59e48c913b75d4964bea49f37da232c59a42ad993f7b5edc88322148c1d7394984349682bfce3b69d33a51756ac8e3 +LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/md5/7be93eccbdb0aff427c43af651073d66 +LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/sha512/89a61a81ec664c72107ac09e717200b00434350bf77064267180bc0c101a59e0ee8c8af4dd6fe75eacdeb14e82743c138b2fc558ca08550d8796b8db93f89da4 diff --git a/deps/llvmunwind.version b/deps/llvmunwind.version index 7d13af9a158f7..9c2a91c566ba2 100644 --- a/deps/llvmunwind.version +++ b/deps/llvmunwind.version @@ -2,4 +2,4 @@ LLVMUNWIND_JLL_NAME := LLVMLibUnwind ## source build -LLVMUNWIND_VER := 12.0.1 +LLVMUNWIND_VER := 14.0.6 diff --git a/deps/patches/llvm-libunwind-revert-monorepo-requirement.patch b/deps/patches/llvm-libunwind-revert-monorepo-requirement.patch deleted file mode 100644 index 4e3897dfb9801..0000000000000 --- a/deps/patches/llvm-libunwind-revert-monorepo-requirement.patch +++ /dev/null @@ -1,156 +0,0 @@ -Upstream commit 8c03fdf34a659925a3f09c8f54016e47ea1c7519 changed the build such -that it requires living inside the monorepo with libcxx available, only so that -it can reuse a CMake file to simplify some build steps. This patch is a revert -of that commit applied only to libunwind. - ---- -diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt -index 570b8db90653..a383d7d77d6f 100644 ---- a/libunwind/CMakeLists.txt -+++ b/libunwind/CMakeLists.txt -@@ -1,7 +1,3 @@ --if (NOT IS_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/../libcxx") -- message(FATAL_ERROR "libunwind requires being built in a monorepo layout with libcxx available") --endif() -- - #=============================================================================== - # Setup Project - #=============================================================================== -@@ -15,31 +11,103 @@ set(CMAKE_MODULE_PATH - ${CMAKE_MODULE_PATH} - ) - --set(LIBUNWIND_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) --set(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) --set(LIBUNWIND_LIBCXX_PATH "${CMAKE_CURRENT_LIST_DIR}/../libcxx" CACHE PATH -- "Specify path to libc++ source.") -- - if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR LIBUNWIND_STANDALONE_BUILD) - project(libunwind LANGUAGES C CXX ASM) - -+ # Rely on llvm-config. -+ set(CONFIG_OUTPUT) -+ if(NOT LLVM_CONFIG_PATH) -+ find_program(LLVM_CONFIG_PATH "llvm-config") -+ endif() -+ if (DEFINED LLVM_PATH) -+ set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} CACHE PATH "Path to llvm/include") -+ set(LLVM_PATH ${LLVM_PATH} CACHE PATH "Path to LLVM source tree") -+ set(LLVM_MAIN_SRC_DIR ${LLVM_PATH}) -+ set(LLVM_CMAKE_PATH "${LLVM_PATH}/cmake/modules") -+ elseif(LLVM_CONFIG_PATH) -+ message(STATUS "Found LLVM_CONFIG_PATH as ${LLVM_CONFIG_PATH}") -+ set(CONFIG_COMMAND ${LLVM_CONFIG_PATH} "--includedir" "--prefix" "--src-root") -+ execute_process(COMMAND ${CONFIG_COMMAND} -+ RESULT_VARIABLE HAD_ERROR -+ OUTPUT_VARIABLE CONFIG_OUTPUT) -+ if (NOT HAD_ERROR) -+ string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" -+ CONFIG_OUTPUT ${CONFIG_OUTPUT}) -+ else() -+ string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") -+ message(STATUS "${CONFIG_COMMAND_STR}") -+ message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") -+ endif() -+ -+ list(GET CONFIG_OUTPUT 0 INCLUDE_DIR) -+ list(GET CONFIG_OUTPUT 1 LLVM_OBJ_ROOT) -+ list(GET CONFIG_OUTPUT 2 MAIN_SRC_DIR) -+ -+ set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") -+ set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") -+ set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") -+ set(LLVM_LIT_PATH "${LLVM_PATH}/utils/lit/lit.py") -+ -+ # --cmakedir is supported since llvm r291218 (4.0 release) -+ execute_process( -+ COMMAND ${LLVM_CONFIG_PATH} --cmakedir -+ RESULT_VARIABLE HAD_ERROR -+ OUTPUT_VARIABLE CONFIG_OUTPUT -+ ERROR_QUIET) -+ if(NOT HAD_ERROR) -+ string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH_FROM_LLVM_CONFIG) -+ file(TO_CMAKE_PATH "${LLVM_CMAKE_PATH_FROM_LLVM_CONFIG}" LLVM_CMAKE_PATH) -+ else() -+ file(TO_CMAKE_PATH "${LLVM_BINARY_DIR}" LLVM_BINARY_DIR_CMAKE_STYLE) -+ set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") -+ endif() -+ else() -+ message(WARNING "UNSUPPORTED LIBUNWIND CONFIGURATION DETECTED: " -+ "llvm-config not found and LLVM_MAIN_SRC_DIR not defined. " -+ "Reconfigure with -DLLVM_CONFIG=path/to/llvm-config " -+ "or -DLLVM_PATH=path/to/llvm-source-root.") -+ endif() -+ -+ if (EXISTS ${LLVM_CMAKE_PATH}) -+ list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") -+ include("${LLVM_CMAKE_PATH}/AddLLVM.cmake") -+ include("${LLVM_CMAKE_PATH}/HandleLLVMOptions.cmake") -+ else() -+ message(WARNING "Not found: ${LLVM_CMAKE_PATH}") -+ endif() -+ - set(PACKAGE_NAME libunwind) - set(PACKAGE_VERSION 12.0.1) - set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") - set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org") - -- # Add the CMake module path of libcxx so we can reuse HandleOutOfTreeLLVM.cmake -- set(LIBUNWIND_LIBCXX_CMAKE_PATH "${LIBUNWIND_LIBCXX_PATH}/cmake/Modules") -- list(APPEND CMAKE_MODULE_PATH "${LIBUNWIND_LIBCXX_CMAKE_PATH}") -+ if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) -+ set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) -+ else() -+ # Seek installed Lit. -+ find_program(LLVM_LIT "lit.py" ${LLVM_MAIN_SRC_DIR}/utils/lit -+ DOC "Path to lit.py") -+ endif() - -- # In a standalone build, we don't have llvm to automatically generate the -- # llvm-lit script for us. So we need to provide an explicit directory that -- # the configurator should write the script into. -- set(LIBUNWIND_STANDALONE_BUILD 1) -- set(LLVM_LIT_OUTPUT_DIR "${LIBUNWIND_BINARY_DIR}/bin") -+ if (LLVM_LIT) -+ # Define the default arguments to use with 'lit', and an option for the user -+ # to override. -+ set(LIT_ARGS_DEFAULT "-sv") -+ if (MSVC OR XCODE) -+ set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") -+ endif() -+ set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") -+ -+ # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. -+ if (WIN32 AND NOT CYGWIN) -+ set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") -+ endif() -+ else() -+ set(LLVM_INCLUDE_TESTS OFF) -+ endif() - -- # Find the LLVM sources and simulate LLVM CMake options. -- include(HandleOutOfTreeLLVM) -+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) -+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) - else() - set(LLVM_LIT "${CMAKE_SOURCE_DIR}/utils/lit/lit.py") - endif() -@@ -85,8 +153,6 @@ set(LIBUNWIND_TEST_COMPILER_FLAGS "" CACHE STRING - "Additional compiler flags for test programs.") - set(LIBUNWIND_TEST_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/test/lit.site.cfg.in" CACHE STRING - "The Lit testing configuration to use when running the tests.") --set(LIBUNWIND_TEST_PARAMS "" CACHE STRING -- "A list of parameters to run the Lit test suite with.") - - if (NOT LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_STATIC) - message(FATAL_ERROR "libunwind must be built as either a shared or static library.") -@@ -113,6 +179,9 @@ set(CMAKE_MODULE_PATH - "${CMAKE_CURRENT_SOURCE_DIR}/cmake" - ${CMAKE_MODULE_PATH}) - -+set(LIBUNWIND_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -+set(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -+ - if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) - set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++) - set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++) diff --git a/deps/unwind.mk b/deps/unwind.mk index 079e4d69b04a3..3951bbf36e22f 100644 --- a/deps/unwind.mk +++ b/deps/unwind.mk @@ -88,40 +88,41 @@ check-unwind: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-checked LLVMUNWIND_OPTS := $(CMAKE_COMMON) \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DLIBUNWIND_ENABLE_PEDANTIC=OFF \ - -DLLVM_PATH=$(SRCCACHE)/$(LLVM_SRC_DIR)/llvm + -DLIBUNWIND_INCLUDE_DOCS=OFF \ + -DLIBUNWIND_INCLUDE_TESTS=OFF \ + -DLIBUNWIND_INSTALL_HEADERS=ON \ + -DLIBUNWIND_ENABLE_ASSERTIONS=OFF \ + -DLLVM_CONFIG_PATH=$(build_depsbindir)/llvm-config \ + -DLLVM_PATH=$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/llvm -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER).tar.xz: | $(SRCCACHE) - $(JLDOWNLOAD) $@ https://github.com/llvm/llvm-project/releases/download/llvmorg-$(LLVMUNWIND_VER)/libunwind-$(LLVMUNWIND_VER).src.tar.xz +$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/llvm/llvm-project/releases/download/llvmorg-$(LLVMUNWIND_VER)/llvm-project-$(LLVMUNWIND_VER).src.tar.xz -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/source-extracted: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER).tar.xz +$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/source-extracted: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz $(JLCHECKSUM) $< cd $(dir $<) && $(TAR) xf $< - mv $(SRCCACHE)/libunwind-$(LLVMUNWIND_VER).src $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) + mv $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).src $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER) echo 1 > $@ -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-prologue-epilogue.patch-applied: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/source-extracted - cd $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-prologue-epilogue.patch +$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-prologue-epilogue.patch-applied: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/source-extracted + cd $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-prologue-epilogue.patch echo 1 > $@ -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-force-dwarf.patch-applied: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-prologue-epilogue.patch-applied - cd $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-force-dwarf.patch +$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-force-dwarf.patch-applied: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-prologue-epilogue.patch-applied + cd $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-force-dwarf.patch echo 1 > $@ -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-revert-monorepo-requirement.patch-applied: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-force-dwarf.patch-applied - cd $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-revert-monorepo-requirement.patch +$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-freebsd-libgcc-api-compat.patch-applied: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-force-dwarf.patch-applied + cd $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-freebsd-libgcc-api-compat.patch echo 1 > $@ -$(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-freebsd-libgcc-api-compat.patch-applied: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-revert-monorepo-requirement.patch-applied - cd $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) && patch -p2 -f < $(SRCDIR)/patches/llvm-libunwind-freebsd-libgcc-api-compat.patch - echo 1 > $@ - -checksum-llvmunwind: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER).tar.xz +checksum-llvmunwind: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz $(JLCHECKSUM) $< -$(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/source-extracted $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/llvm-libunwind-freebsd-libgcc-api-compat.patch-applied +$(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/source-extracted $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-freebsd-libgcc-api-compat.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) $(dir $<) $(LLVMUNWIND_OPTS) + $(CMAKE) $(dir $<)/libunwind $(LLVMUNWIND_OPTS) echo 1 > $@ $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-compiled: $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured @@ -131,7 +132,7 @@ $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-compiled: $(BUILDDIR)/llvmunwind- $(eval $(call staged-install, \ llvmunwind,llvmunwind-$(LLVMUNWIND_VER), \ MAKE_INSTALL,,, \ - cp -fR $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/include/* $(build_includedir))) + cp -fR $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/* $(build_includedir))) clean-llvmunwind: -rm -f $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-compiled @@ -139,14 +140,14 @@ clean-llvmunwind: -$(MAKE) -C $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER) clean distclean-llvmunwind: - rm -rf $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER).tar.xz \ + rm -rf $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz \ $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER) \ $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER) -get-llvmunwind: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER).tar.xz -extract-llvmunwind: $(SRCCACHE)/llvmunwind-$(LLVMUNWIND_VER)/source-extracted -configure-llvmunwind: $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured -compile-llvmunwind: $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-compiled +get-llvmunwind: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz +extract-llvmunwind: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/source-extracted +configure-llvmunwind: $(BUILDDIR)/llvm-project-$(LLVMUNWIND_VER)/build-configured +compile-llvmunwind: $(BUILDDIR)/llvm-project-$(LLVMUNWIND_VER)/build-compiled fastcheck-llvmunwind: check-llvmunwind check-llvmunwind: # no test/check provided by Makefile diff --git a/stdlib/LLVMLibUnwind_jll/Project.toml b/stdlib/LLVMLibUnwind_jll/Project.toml index 36c24111d4d31..0cb0fe5440066 100644 --- a/stdlib/LLVMLibUnwind_jll/Project.toml +++ b/stdlib/LLVMLibUnwind_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLVMLibUnwind_jll" uuid = "47c5dbc3-30ba-59ef-96a6-123e260183d9" -version = "12.0.1+0" +version = "14.0.6+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 8547b0250046cf20f762bfd910bf180b757c69a6 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Sat, 7 Sep 2024 06:06:49 +1200 Subject: [PATCH 177/548] Add `JL_DATA_TYPE` for `jl_line_info_node_t` and `jl_code_info_t` (#55698) --- src/julia.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/julia.h b/src/julia.h index efdd6a1b08bf7..abb8a57ff13b0 100644 --- a/src/julia.h +++ b/src/julia.h @@ -234,6 +234,7 @@ JL_DLLEXPORT extern const jl_callptr_t jl_f_opaque_closure_call_addr; JL_DLLEXPORT extern const jl_callptr_t jl_fptr_wait_for_compiled_addr; typedef struct _jl_line_info_node_t { + JL_DATA_TYPE struct _jl_module_t *module; jl_value_t *method; // may contain a jl_symbol, jl_method_t, or jl_method_instance_t jl_sym_t *file; @@ -281,6 +282,7 @@ typedef union __jl_purity_overrides_t { // This type describes a single function body typedef struct _jl_code_info_t { + JL_DATA_TYPE // ssavalue-indexed arrays of properties: jl_array_t *code; // Any array of statements jl_debuginfo_t *debuginfo; // Table of edge data for each statement From 0c9c1e25b7d017250fd5b64255c683b74a168a9b Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:10:50 -0300 Subject: [PATCH 178/548] Canonicalize names of nested functions by keeping a more fine grained counter -- per (module, method name) pair (#53719) As mentioned in https://github.com/JuliaLang/julia/pull/53716, we've been noticing that `precompile` statements lists from one version of our codebase often don't apply cleanly in a slightly different version. That's because a lot of nested and anonymous function names have a global numeric suffix which is incremented every time a new name is generated, and these numeric suffixes are not very stable across codebase changes. To solve this, this PR makes the numeric suffixes a bit more fine grained: every pair of (module, top-level/outermost function name) will have its own counter, which should make nested function names a bit more stable across different versions. This PR applies @JeffBezanson's idea of making the symbol name changes directly in `current-julia-module-counter`. Here is an example: ```Julia julia> function foo(x) function bar(y) return x + y end end foo (generic function with 1 method) julia> f = foo(42) (::var"#bar#foo##0"{Int64}) (generic function with 1 method) ``` --- doc/src/manual/functions.md | 4 +- src/ast.c | 44 ++++++++++++++++-- src/datatype.c | 14 +++--- src/flisp/flisp.h | 2 +- src/julia-syntax.scm | 92 ++++++++++++++++++++++++------------- src/julia_internal.h | 22 ++++++++- test/show.jl | 63 +++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 48 deletions(-) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 9a91ea7467750..be81fe529ef7d 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -292,12 +292,12 @@ syntaxes: ```jldoctest julia> x -> x^2 + 2x - 1 -#1 (generic function with 1 method) +#2 (generic function with 1 method) julia> function (x) x^2 + 2x - 1 end -#3 (generic function with 1 method) +#5 (generic function with 1 method) ``` Each statement creates a function taking one argument `x` and returning the value of the polynomial `x^2 + diff --git a/src/ast.c b/src/ast.c index 26b95225fbf1c..ea1de429a946c 100644 --- a/src/ast.c +++ b/src/ast.c @@ -7,6 +7,7 @@ #include #include #include + #ifdef _OS_WINDOWS_ #include #endif @@ -215,11 +216,46 @@ static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F; } -static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT +// Used to generate a unique suffix for a given symbol (e.g. variable or type name) +// first argument contains a stack of method definitions seen so far by `closure-convert` in flisp. +// if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes +// of the form $top_level_method_name##$counter, where `counter` is the smallest integer +// such that the resulting name is not already defined in the current module's bindings. +// If the top of the stack is NIL, we simply return the current module's counter. +// This ensures that precompile statements are a bit more stable across different versions +// of a codebase. see #53719 +static value_t fl_module_unique_name(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) { + argcount(fl_ctx, "julia-module-unique-name", nargs, 1); jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - assert(ctx->module); - return fixnum(jl_module_next_counter(ctx->module)); + jl_module_t *m = ctx->module; + assert(m != NULL); + // Get the outermost function name from the `parsed_method_stack` top + char *funcname = NULL; + value_t parsed_method_stack = args[0]; + if (parsed_method_stack != fl_ctx->NIL) { + value_t bottom_stack_symbol = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "last")), parsed_method_stack); + funcname = tosymbol(fl_ctx, bottom_stack_symbol, "julia-module-unique-name")->name; + } + size_t sz = funcname != NULL ? strlen(funcname) + 32 : 32; // 32 is enough for the suffix + char *buf = (char*)alloca(sz); + if (funcname != NULL && strchr(funcname, '#') == NULL) { + for (int i = 0; ; i++) { + snprintf(buf, sz, "%s##%d", funcname, i); + jl_sym_t *sym = jl_symbol(buf); + JL_LOCK(&m->lock); + if (jl_get_module_binding(m, sym, 0) == NULL) { // make sure this name is not already taken + jl_get_module_binding(m, sym, 1); // create the binding + JL_UNLOCK(&m->lock); + return symbol(fl_ctx, buf); + } + JL_UNLOCK(&m->lock); + } + } + else { + snprintf(buf, sz, "%d", jl_module_next_counter(m)); + } + return symbol(fl_ctx, buf); } static int jl_is_number(jl_value_t *v) @@ -252,7 +288,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m static const builtinspec_t julia_flisp_ast_ext[] = { { "defined-julia-global", fl_defined_julia_global }, // TODO: can we kill this safepoint { "nothrow-julia-global", fl_nothrow_julia_global }, - { "current-julia-module-counter", fl_current_module_counter }, + { "current-julia-module-counter", fl_module_unique_name }, { "julia-scalar?", fl_julia_scalar }, { NULL, NULL } }; diff --git a/src/datatype.c b/src/datatype.c index 1157c1d425cb2..c78b00fdd2245 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -20,23 +20,21 @@ extern "C" { // allocating TypeNames ----------------------------------------------------------- -static int is10digit(char c) JL_NOTSAFEPOINT -{ - return (c >= '0' && c <= '9'); -} - static jl_sym_t *jl_demangle_typename(jl_sym_t *s) JL_NOTSAFEPOINT { char *n = jl_symbol_name(s); if (n[0] != '#') return s; - char *end = strrchr(n, '#'); + char *end = strchr(&n[1], '#'); + // handle `#f...##...#...` + if (end != NULL && end[1] == '#') + end = strchr(&end[2], '#'); int32_t len; - if (end == n || end == n+1) + if (end == NULL || end == n+1) len = strlen(n) - 1; else len = (end-n) - 1; // extract `f` from `#f#...` - if (is10digit(n[1])) + if (isdigit(n[1]) || is_canonicalized_anonfn_typename(n)) return _jl_symbol(n, len+1); return _jl_symbol(&n[1], len); } diff --git a/src/flisp/flisp.h b/src/flisp/flisp.h index 669753a9f5302..f8dd1cfd81ed0 100644 --- a/src/flisp/flisp.h +++ b/src/flisp/flisp.h @@ -158,7 +158,7 @@ value_t fl_cons(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT; value_t fl_list2(fl_context_t *fl_ctx, value_t a, value_t b) JL_NOTSAFEPOINT; value_t fl_listn(fl_context_t *fl_ctx, size_t n, ...) JL_NOTSAFEPOINT; value_t symbol(fl_context_t *fl_ctx, const char *str) JL_NOTSAFEPOINT; -char *symbol_name(fl_context_t *fl_ctx, value_t v); +char *symbol_name(fl_context_t *fl_ctx, value_t v) JL_NOTSAFEPOINT; int fl_is_keyword_name(const char *str, size_t len); value_t alloc_vector(fl_context_t *fl_ctx, size_t n, int init); size_t llength(value_t v); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index a2d3ffdd66f67..6815921375184 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -225,6 +225,19 @@ (if lb (list lb ub) (list ub)) (if lb (list lb '(core Any)) '()))))) +(define (is-method? x) + (if (and (pair? x) (eq? (car x) 'method)) + (let ((name (cadr x))) + (if (and (pair? name) (eq? (car name) 'globalref)) + (let ((name (caddr name))) + (if (symbol? name) + #t + #f)) + (if (symbol? name) + #t + #f))) + #f)) + (define (method-expr-name m) (let ((name (cadr m))) (cond ((globalref? name) (caddr name)) @@ -372,7 +385,7 @@ (generator (if (expr-contains-p if-generated? body (lambda (x) (not (function-def? x)))) (let* ((gen (generated-version body)) (nongen (non-generated-version body)) - (gname (symbol (string (gensy) "#" (current-julia-module-counter)))) + (gname (symbol (string (gensy) "#" (current-julia-module-counter '())))) (gf (make-generator-function gname names anames gen))) (set! body (insert-after-meta nongen @@ -512,7 +525,7 @@ "" "#") (or und '_) "#" - (string (current-julia-module-counter))))))) + (string (current-julia-module-counter '()))))))) ;; this is a hack: nest these statements inside a call so they get closure ;; converted together, allowing all needed types to be defined before any methods. `(call (core ifelse) (false) (false) (block @@ -1251,7 +1264,7 @@ (list a))) ;; TODO: always use a specific special name like #anon# or _, then ignore ;; this as a local variable name. - (name (symbol (string "#" (current-julia-module-counter))))) + (name (symbol (string "#" (current-julia-module-counter '()))))) (expand-forms `(block (local ,name) (function @@ -3537,9 +3550,9 @@ f(x) = yt(x) (define (clear-capture-bits vinfos) (map vinfo:not-capt vinfos)) -(define (convert-lambda lam fname interp capt-sp opaq) +(define (convert-lambda lam fname interp capt-sp opaq parsed-method-stack) (let ((body (add-box-inits-to-body - lam (cl-convert (cadddr lam) fname lam (table) (table) #f interp opaq (table) (vinfo-to-table (car (lam:vinfo lam))))))) + lam (cl-convert (cadddr lam) fname lam (table) (table) #f interp opaq parsed-method-stack (table) (vinfo-to-table (car (lam:vinfo lam))))))) `(lambda ,(lam:args lam) (,(clear-capture-bits (car (lam:vinfo lam))) () @@ -3614,7 +3627,7 @@ f(x) = yt(x) ;; declared types. ;; when doing this, the original value needs to be preserved, to ;; ensure the expression `a=b` always returns exactly `b`. -(define (convert-assignment var rhs0 fname lam interp opaq globals locals) +(define (convert-assignment var rhs0 fname lam interp opaq parsed-method-stack globals locals) (cond ((symbol? var) (let* ((vi (get locals var #f)) @@ -3632,7 +3645,7 @@ f(x) = yt(x) (equal? rhs0 '(the_exception))) rhs0 (make-ssavalue))) - (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq (table) locals) #t lam)) + (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq parsed-method-stack (table) locals) #t lam)) (ex (cond (closed `(call (core setfield!) ,(if interp `($ ,var) @@ -3916,17 +3929,17 @@ f(x) = yt(x) (define (toplevel-preserving? e) (and (pair? e) (memq (car e) '(if elseif block trycatch tryfinally trycatchelse)))) -(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) +(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq parsed-method-stack (globals (table)) (locals (table))) (if toplevel (map (lambda (x) (let ((tl (lift-toplevel (cl-convert x fname lam namemap defined (and toplevel (toplevel-preserving? x)) - interp opaq globals locals)))) + interp opaq parsed-method-stack globals locals)))) (if (null? (cdr tl)) (car tl) `(block ,@(cdr tl) ,(car tl))))) exprs) - (map (lambda (x) (cl-convert x fname lam namemap defined #f interp opaq globals locals)) exprs))) + (map (lambda (x) (cl-convert x fname lam namemap defined #f interp opaq parsed-method-stack globals locals)) exprs))) (define (prepare-lambda! lam) ;; mark all non-arguments as assigned, since locals that are never assigned @@ -3935,11 +3948,17 @@ f(x) = yt(x) (list-tail (car (lam:vinfo lam)) (length (lam:args lam)))) (lambda-optimize-vars! lam)) -(define (cl-convert e fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) +;; must start with a hash and second character must be numeric +(define (anon-function-name? str) + (and (>= (string-length str) 2) + (char=? (string.char str 0) #\#) + (char-numeric? (string.char str 1)))) + +(define (cl-convert- e fname lam namemap defined toplevel interp opaq parsed-method-stack (globals (table)) (locals (table))) (if (and (not lam) (not (and (pair? e) (memq (car e) '(lambda method macro opaque_closure))))) (if (atom? e) e - (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals))) + (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (cond ((symbol? e) (define (new-undef-var name) @@ -3958,7 +3977,7 @@ f(x) = yt(x) (val (if (equal? typ '(core Any)) val `(call (core typeassert) ,val - ,(cl-convert typ fname lam namemap defined toplevel interp opaq globals locals))))) + ,(cl-convert typ fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))))) `(block ,@(if (eq? box access) '() `((= ,access ,box))) ,undefcheck @@ -3990,8 +4009,8 @@ f(x) = yt(x) e) ((=) (let ((var (cadr e)) - (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp opaq globals locals))) - (convert-assignment var rhs fname lam interp opaq globals locals))) + (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) + (convert-assignment var rhs fname lam interp opaq parsed-method-stack globals locals))) ((local-def) ;; make new Box for local declaration of defined variable (let ((vi (get locals (cadr e) #f))) (if (and vi (vinfo:asgn vi) (vinfo:capt vi)) @@ -4043,7 +4062,7 @@ f(x) = yt(x) cvs))) `(new_opaque_closure ,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) ,allow-partial - (opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs))) + (opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs) parsed-method-stack)) ,@var-exprs)))) ((method) (let* ((name (method-expr-name e)) @@ -4057,7 +4076,7 @@ f(x) = yt(x) (sp-inits (if (or short (not (eq? (car sig) 'block))) '() (map-cl-convert (butlast (cdr sig)) - fname lam namemap defined toplevel interp opaq globals locals))) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (sig (and sig (if (eq? (car sig) 'block) (last sig) sig)))) @@ -4084,22 +4103,22 @@ f(x) = yt(x) ;; anonymous functions with keyword args generate global ;; functions that refer to the type of a local function (rename-sig-types sig namemap) - fname lam namemap defined toplevel interp opaq globals locals) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(let ((body (add-box-inits-to-body lam2 - (cl-convert (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq (table) + (cl-convert (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq parsed-method-stack (table) (vinfo-to-table (car (lam:vinfo lam2))))))) `(lambda ,(cadr lam2) (,(clear-capture-bits (car vis)) ,@(cdr vis)) ,body))))) (else - (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '() #f))) + (let* ((exprs (lift-toplevel (convert-lambda lam2 '|#anon| #t '() #f parsed-method-stack))) (top-stmts (cdr exprs)) (newlam (compact-and-renumber (linearize (car exprs)) 'none 0))) `(toplevel-butfirst (block ,@sp-inits - (method ,(cadr e) ,(cl-convert sig fname lam namemap defined toplevel interp opaq globals locals) + (method ,(cadr e) ,(cl-convert sig fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(julia-bq-macro newlam))) ,@top-stmts)))) @@ -4108,9 +4127,11 @@ f(x) = yt(x) (type-name (or (get namemap name #f) (and name (symbol (string (if (= (string.char (string name) 0) #\#) - "" - "#") - name "#" (current-julia-module-counter)))))) + (if (anon-function-name? (string name)) + (string "#" (current-julia-module-counter parsed-method-stack)) + name) + (string "#" name)) + "#" (current-julia-module-counter parsed-method-stack)))))) (alldefs (expr-find-all (lambda (ex) (and (length> ex 2) (eq? (car ex) 'method) (not (eq? ex e)) @@ -4202,12 +4223,12 @@ f(x) = yt(x) (append (map (lambda (gs tvar) (make-assignment gs `(call (core TypeVar) ',tvar (core Any)))) closure-param-syms closure-param-names) - `((method #f ,(cl-convert arg-defs fname lam namemap defined toplevel interp opaq globals locals) + `((method #f ,(cl-convert arg-defs fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals) ,(convert-lambda lam2 (if iskw (caddr (lam:args lam2)) (car (lam:args lam2))) - #f closure-param-names #f))))))) + #f closure-param-names #f parsed-method-stack))))))) (mk-closure ;; expression to make the closure (let* ((var-exprs (map (lambda (v) (let ((cv (assq v (cadr (lam:vinfo lam))))) @@ -4241,7 +4262,7 @@ f(x) = yt(x) (begin (put! defined name #t) `(toplevel-butfirst - ,(convert-assignment name mk-closure fname lam interp opaq globals locals) + ,(convert-assignment name mk-closure fname lam interp opaq parsed-method-stack globals locals) ,@typedef ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits @@ -4255,14 +4276,14 @@ f(x) = yt(x) (table) (table) (null? (cadr e)) ;; only toplevel thunks have 0 args - interp opaq globals (vinfo-to-table (car (lam:vinfo e)))))) + interp opaq parsed-method-stack globals (vinfo-to-table (car (lam:vinfo e)))))) `(lambda ,(cadr e) (,(clear-capture-bits (car (lam:vinfo e))) () ,@(cddr (lam:vinfo e))) (block ,@body)))) ;; remaining `::` expressions are type assertions ((|::|) - (cl-convert `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq globals locals)) + (cl-convert `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)) ;; remaining `decl` expressions are only type assertions if the ;; argument is global or a non-symbol. ((decl) @@ -4280,13 +4301,20 @@ f(x) = yt(x) (globaldecl ,ref ,(caddr e)) (null))) `(call (core typeassert) ,@(cdr e)))) - fname lam namemap defined toplevel interp opaq globals locals)))) + fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)))) ;; `with-static-parameters` expressions can be removed now; used only by analyze-vars ((with-static-parameters) - (cl-convert (cadr e) fname lam namemap defined toplevel interp opaq globals locals)) + (cl-convert (cadr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)) (else (cons (car e) - (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals)))))))) + (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals)))))))) + +;; wrapper for `cl-convert-` +(define (cl-convert e fname lam namemap defined toplevel interp opaq (parsed-method-stack '()) (globals (table)) (locals (table))) + (if (is-method? e) + (let ((name (method-expr-name e))) + (cl-convert- e fname lam namemap defined toplevel interp opaq (cons name parsed-method-stack) globals locals)) + (cl-convert- e fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) (define (closure-convert e) (cl-convert e #f #f (table) (table) #f #f #f)) diff --git a/src/julia_internal.h b/src/julia_internal.h index d9e1a078c8a03..ac955e2bcb3a6 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -13,6 +13,7 @@ #include "support/strtod.h" #include "gc-alloc-profiler.h" #include "support/rle.h" +#include #include #include #include @@ -947,12 +948,31 @@ STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl } #endif +STATIC_INLINE int is10digit(char c) JL_NOTSAFEPOINT +{ + return (c >= '0' && c <= '9'); +} + STATIC_INLINE int is_anonfn_typename(char *name) { if (name[0] != '#' || name[1] == '#') return 0; char *other = strrchr(name, '#'); - return other > &name[1] && other[1] > '0' && other[1] <= '9'; + return other > &name[1] && is10digit(other[1]); +} + +// Returns true for typenames of anounymous functions that have been canonicalized (i.e. +// we mangled the name of the outermost enclosing function in their name). +STATIC_INLINE int is_canonicalized_anonfn_typename(char *name) JL_NOTSAFEPOINT +{ + char *delim = strchr(&name[1], '#'); + if (delim == NULL) + return 0; + if (delim[1] != '#') + return 0; + if (!is10digit(delim[2])) + return 0; + return 1; } // Each tuple can exist in one of 4 Vararg states: diff --git a/test/show.jl b/test/show.jl index 63663152d9d91..65c65111606c5 100644 --- a/test/show.jl +++ b/test/show.jl @@ -755,6 +755,69 @@ end @test startswith(sprint(show, typeof(x->x), context = :module=>@__MODULE__), "var\"") +# PR 53719 +module M53719 + f = x -> x + 1 + function foo(x) + function bar(y) + function baz(z) + return x + y + z + end + return baz + end + return bar + end + function foo2(x) + function bar2(y) + return z -> x + y + z + end + return bar2 + end + lambda1 = (x)->begin + function foo(y) + return x + y + end + return foo + end + lambda2 = (x)->begin + y -> x + y + end +end + +@testset "PR 53719 function names" begin + # M53719.f should be printed as var"#[0-9]+" + @test occursin(r"var\"#[0-9]+", sprint(show, M53719.f, context = :module=>M53719)) + # M53719.foo(1) should be printed as var"#bar" + @test occursin(r"var\"#bar", sprint(show, M53719.foo(1), context = :module=>M53719)) + # M53719.foo(1)(2) should be printed as var"#baz" + @test occursin(r"var\"#baz", sprint(show, M53719.foo(1)(2), context = :module=>M53719)) + # M53719.foo2(1) should be printed as var"#bar2" + @test occursin(r"var\"#bar2", sprint(show, M53719.foo2(1), context = :module=>M53719)) + # M53719.foo2(1)(2) should be printed as var"#foo2##[0-9]+" + @test occursin(r"var\"#foo2##[0-9]+", sprint(show, M53719.foo2(1)(2), context = :module=>M53719)) + # M53719.lambda1(1) should be printed as var"#foo" + @test occursin(r"var\"#foo", sprint(show, M53719.lambda1(1), context = :module=>M53719)) + # M53719.lambda2(1) should be printed as var"#[0-9]+" + @test occursin(r"var\"#[0-9]+", sprint(show, M53719.lambda2(1), context = :module=>M53719)) +end + +@testset "PR 53719 function types" begin + # typeof(M53719.f) should be printed as var"#[0-9]+#[0-9]+" + @test occursin(r"var\"#[0-9]+#[0-9]+", sprint(show, typeof(M53719.f), context = :module=>M53719)) + #typeof(M53719.foo(1)) should be printed as var"#bar#foo##[0-9]+" + @test occursin(r"var\"#bar#foo##[0-9]+", sprint(show, typeof(M53719.foo(1)), context = :module=>M53719)) + #typeof(M53719.foo(1)(2)) should be printed as var"#baz#foo##[0-9]+" + @test occursin(r"var\"#baz#foo##[0-9]+", sprint(show, typeof(M53719.foo(1)(2)), context = :module=>M53719)) + #typeof(M53719.foo2(1)) should be printed as var"#bar2#foo2##[0-9]+" + @test occursin(r"var\"#bar2#foo2##[0-9]+", sprint(show, typeof(M53719.foo2(1)), context = :module=>M53719)) + #typeof(M53719.foo2(1)(2)) should be printed as var"#foo2##[0-9]+#foo2##[0-9]+" + @test occursin(r"var\"#foo2##[0-9]+#foo2##[0-9]+", sprint(show, typeof(M53719.foo2(1)(2)), context = :module=>M53719)) + #typeof(M53719.lambda1(1)) should be printed as var"#foo#[0-9]+" + @test occursin(r"var\"#foo#[0-9]+", sprint(show, typeof(M53719.lambda1(1)), context = :module=>M53719)) + #typeof(M53719.lambda2(1)) should be printed as var"#[0-9]+#[0-9]+" + @test occursin(r"var\"#[0-9]+#[0-9]+", sprint(show, typeof(M53719.lambda2(1)), context = :module=>M53719)) +end + #test methodshow.jl functions @test Base.inbase(Base) @test !Base.inbase(LinearAlgebra) From ac5479946115253f87de1daee467fbc9e3d8e5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:17:22 +0100 Subject: [PATCH 179/548] Use `uv_available_parallelism` inside `jl_effective_threads` (#55592) --- deps/checksums/libuv | 68 +++++++++++++++++------------------ deps/libuv.version | 2 +- src/sys.c | 23 +++--------- stdlib/LibUV_jll/Project.toml | 2 +- 4 files changed, 40 insertions(+), 55 deletions(-) diff --git a/deps/checksums/libuv b/deps/checksums/libuv index c2c86a9767463..6887c3fe62f41 100644 --- a/deps/checksums/libuv +++ b/deps/checksums/libuv @@ -1,34 +1,34 @@ -LibUV.v2.0.1+17.aarch64-apple-darwin.tar.gz/md5/f176c76e5e2096dea8443302cf9550b8 -LibUV.v2.0.1+17.aarch64-apple-darwin.tar.gz/sha512/4301b13953a08a758b86e30af3261fd9a291ce3829b4d98e71e2a2c040e322e284c5a6eb4bc7189cc352f4b1cf7041e2cfd3380d511d88c151df3101ad74594e -LibUV.v2.0.1+17.aarch64-linux-gnu.tar.gz/md5/c81515783363702a1bd4b65fd6d7f36b -LibUV.v2.0.1+17.aarch64-linux-gnu.tar.gz/sha512/011429365337f5a45e56ca7a42709866bb994c747a1170d870f5f3ddfff2d36138866ee9278ac01172bc71bde8dee404bcb9cae9c7b44222bf1cc883659df269 -LibUV.v2.0.1+17.aarch64-linux-musl.tar.gz/md5/e74d5ea4912dd326b2705638faa7b805 -LibUV.v2.0.1+17.aarch64-linux-musl.tar.gz/sha512/a26a9f2c9051816230324071c502321f7af3885d581a400615858a93a4cd457226048d15b0e7f6a73d12659763c705b5ab519e9f5b35c6d886b9fd5babbfe352 -LibUV.v2.0.1+17.armv6l-linux-gnueabihf.tar.gz/md5/6df38bcf5d0a61dee63d16b73d0c9a24 -LibUV.v2.0.1+17.armv6l-linux-gnueabihf.tar.gz/sha512/d5354a6532061de0a58965ce0e427bde52f9ae0ee39a98e1a33de4c414fddcba9ba139ddf91be7321a4ccc97bbf7a8a8357ff10cf60f83c0a6bff7d839d6d7a8 -LibUV.v2.0.1+17.armv6l-linux-musleabihf.tar.gz/md5/6f02a24cfbfae3032fadceaea1faed39 -LibUV.v2.0.1+17.armv6l-linux-musleabihf.tar.gz/sha512/7fd107eb9a5ea84b488ea02e4fbedc9fe13bb11be859986a47af38f40ad775dd9f738c790878a3503437bcac1eb26ad9fe26f4aa0d3cb45c980b4c5abc9aec99 -LibUV.v2.0.1+17.armv7l-linux-gnueabihf.tar.gz/md5/96b09dec72f7e9b7409fa2920e67c866 -LibUV.v2.0.1+17.armv7l-linux-gnueabihf.tar.gz/sha512/6a0f79fc15c944fabba5c65180b665bc9769c6ff25863e330049f48b3a4394b448492f5a9a76bb7f8dbd3ce44dfc6f9ccdc2c71c42e1c749e88070fe99b1db69 -LibUV.v2.0.1+17.armv7l-linux-musleabihf.tar.gz/md5/f44e4b2521a813181f943895bdb0dd3c -LibUV.v2.0.1+17.armv7l-linux-musleabihf.tar.gz/sha512/cda1413dca817f772e8b343db0c6de0ef6b8f269e9a6a2ef3403c2582aeab554f46281bbb1eb4659c259198ef47fe26aab648a281e66f80aaf2f2cda0a23ac05 -LibUV.v2.0.1+17.i686-linux-gnu.tar.gz/md5/1f231d89cf9c04515d2d107a5d786cc8 -LibUV.v2.0.1+17.i686-linux-gnu.tar.gz/sha512/089cb8a372cdee0cbc0e78fc201611bb9bafd99af9a78e09d6097a6b70e7c4aa001ebd86f944b0a885c072093c529bf86bcaa32bde4fc1934407a858c1a5a764 -LibUV.v2.0.1+17.i686-linux-musl.tar.gz/md5/01cfc2a9e2536dbd330267917abb19ce -LibUV.v2.0.1+17.i686-linux-musl.tar.gz/sha512/72f3588cb464a60e61f8998242aaa2abdf93df920a2feba5e1d66ef0f2498488df0ec415cbb499d7f75c47bdfc7e3a2fdda6a94383492e0ad13e464eb1314ff8 -LibUV.v2.0.1+17.i686-w64-mingw32.tar.gz/md5/8ba829adad6a45dd71d5f25a7f958e59 -LibUV.v2.0.1+17.i686-w64-mingw32.tar.gz/sha512/dff3b1cfe54e583f8e9536ed02e56180b2cdb391bd108559ed97b2cafa743ebade9ddf04580912436e2efab747e09c79b99e33187ed67a27c5d38113373e1cec -LibUV.v2.0.1+17.powerpc64le-linux-gnu.tar.gz/md5/af0e43d9d0aa91dd82b63220d96991ef -LibUV.v2.0.1+17.powerpc64le-linux-gnu.tar.gz/sha512/9fabe3089e4fc60e910770c32d36300ce8ef36c28e8cc9c72fbecba6eb80285ee8174e84e4452fb4ce90ee7c7f94e99b03fce47d8c579bd614bfffd553f93666 -LibUV.v2.0.1+17.x86_64-apple-darwin.tar.gz/md5/871040e874eedae54553d8f1c91b9133 -LibUV.v2.0.1+17.x86_64-apple-darwin.tar.gz/sha512/d5eee08b65e4bb8b444c61ac277bec9ef944b9279dd7f0732b3cd91d47788c77938e5db71e019e01bbe7785a75df95faf14368764f700c6b7a6b9e4d96d6b4c2 -LibUV.v2.0.1+17.x86_64-linux-gnu.tar.gz/md5/d2d186952c6d017fe33f6a6bea63a3ea -LibUV.v2.0.1+17.x86_64-linux-gnu.tar.gz/sha512/15501534bf5721e6bb668aabe6dc6375349f7a284e28df0609d00982e7e456908bd6868722391afa7f44a5c82faedc8cf544f69a0e4fb9fb0d529b3ae3d44d78 -LibUV.v2.0.1+17.x86_64-linux-musl.tar.gz/md5/271d4d40a1ae53ed5b2376e5936cfcf9 -LibUV.v2.0.1+17.x86_64-linux-musl.tar.gz/sha512/1956f059ed01f66b72349d6561b04e6a89b7257c0f838d7fbdd2cee79bd126bb46b93bf944a042b5a6a235762a7a0cdd117207342dd55a0c58653a70b4a38d48 -LibUV.v2.0.1+17.x86_64-unknown-freebsd.tar.gz/md5/62fe8523948914fbe7e28bf0b8d73594 -LibUV.v2.0.1+17.x86_64-unknown-freebsd.tar.gz/sha512/e6486888028c96975f74bc9313cba9706f6bf2be085aa776c44cbb2886753b2eee62469a0be92eb0542df1d0f51db3b34c7ba5e46842e16c6ff1d20e11b75322 -LibUV.v2.0.1+17.x86_64-w64-mingw32.tar.gz/md5/541601cf27a1d28d25b9ffb00f5aed16 -LibUV.v2.0.1+17.x86_64-w64-mingw32.tar.gz/sha512/58fd5b54694dbddc2abaccfa7291955c75818cdedbd5d9057025bc3e3e6511812e7b03f71cd839c70cd39e77c5ba3e19a07dccd579e2148a2d212fa3829a9a76 -libuv-c57e7f06cbe697ca8ea9215ce054a608c451b193.tar.gz/md5/2b81dbf80d7a9fd10806d1705dbccb7f -libuv-c57e7f06cbe697ca8ea9215ce054a608c451b193.tar.gz/sha512/6796960a58031fa2bc9d72c8f35dff9f213e8d36bbc21ddb65f59bb4dfb074fc18414aece5a046a882dd08b42d5bd32277560c07a1298f68ab8bcd8aadcbf50b +LibUV.v2.0.1+18.aarch64-apple-darwin.tar.gz/md5/f176c76e5e2096dea8443302cf9550b8 +LibUV.v2.0.1+18.aarch64-apple-darwin.tar.gz/sha512/4301b13953a08a758b86e30af3261fd9a291ce3829b4d98e71e2a2c040e322e284c5a6eb4bc7189cc352f4b1cf7041e2cfd3380d511d88c151df3101ad74594e +LibUV.v2.0.1+18.aarch64-linux-gnu.tar.gz/md5/c81515783363702a1bd4b65fd6d7f36b +LibUV.v2.0.1+18.aarch64-linux-gnu.tar.gz/sha512/011429365337f5a45e56ca7a42709866bb994c747a1170d870f5f3ddfff2d36138866ee9278ac01172bc71bde8dee404bcb9cae9c7b44222bf1cc883659df269 +LibUV.v2.0.1+18.aarch64-linux-musl.tar.gz/md5/e74d5ea4912dd326b2705638faa7b805 +LibUV.v2.0.1+18.aarch64-linux-musl.tar.gz/sha512/a26a9f2c9051816230324071c502321f7af3885d581a400615858a93a4cd457226048d15b0e7f6a73d12659763c705b5ab519e9f5b35c6d886b9fd5babbfe352 +LibUV.v2.0.1+18.armv6l-linux-gnueabihf.tar.gz/md5/6df38bcf5d0a61dee63d16b73d0c9a24 +LibUV.v2.0.1+18.armv6l-linux-gnueabihf.tar.gz/sha512/d5354a6532061de0a58965ce0e427bde52f9ae0ee39a98e1a33de4c414fddcba9ba139ddf91be7321a4ccc97bbf7a8a8357ff10cf60f83c0a6bff7d839d6d7a8 +LibUV.v2.0.1+18.armv6l-linux-musleabihf.tar.gz/md5/6f02a24cfbfae3032fadceaea1faed39 +LibUV.v2.0.1+18.armv6l-linux-musleabihf.tar.gz/sha512/7fd107eb9a5ea84b488ea02e4fbedc9fe13bb11be859986a47af38f40ad775dd9f738c790878a3503437bcac1eb26ad9fe26f4aa0d3cb45c980b4c5abc9aec99 +LibUV.v2.0.1+18.armv7l-linux-gnueabihf.tar.gz/md5/96b09dec72f7e9b7409fa2920e67c866 +LibUV.v2.0.1+18.armv7l-linux-gnueabihf.tar.gz/sha512/6a0f79fc15c944fabba5c65180b665bc9769c6ff25863e330049f48b3a4394b448492f5a9a76bb7f8dbd3ce44dfc6f9ccdc2c71c42e1c749e88070fe99b1db69 +LibUV.v2.0.1+18.armv7l-linux-musleabihf.tar.gz/md5/f44e4b2521a813181f943895bdb0dd3c +LibUV.v2.0.1+18.armv7l-linux-musleabihf.tar.gz/sha512/cda1413dca817f772e8b343db0c6de0ef6b8f269e9a6a2ef3403c2582aeab554f46281bbb1eb4659c259198ef47fe26aab648a281e66f80aaf2f2cda0a23ac05 +LibUV.v2.0.1+18.i686-linux-gnu.tar.gz/md5/1f231d89cf9c04515d2d107a5d786cc8 +LibUV.v2.0.1+18.i686-linux-gnu.tar.gz/sha512/089cb8a372cdee0cbc0e78fc201611bb9bafd99af9a78e09d6097a6b70e7c4aa001ebd86f944b0a885c072093c529bf86bcaa32bde4fc1934407a858c1a5a764 +LibUV.v2.0.1+18.i686-linux-musl.tar.gz/md5/01cfc2a9e2536dbd330267917abb19ce +LibUV.v2.0.1+18.i686-linux-musl.tar.gz/sha512/72f3588cb464a60e61f8998242aaa2abdf93df920a2feba5e1d66ef0f2498488df0ec415cbb499d7f75c47bdfc7e3a2fdda6a94383492e0ad13e464eb1314ff8 +LibUV.v2.0.1+18.i686-w64-mingw32.tar.gz/md5/8c6599aab9ed4c46e52f03683aac664e +LibUV.v2.0.1+18.i686-w64-mingw32.tar.gz/sha512/13f0565f7244a8bcf1ab43fac91a856dc86d214877033a3cefee8c2179c1a275dfd7dda32e9017763acac2ba42ab6799934a58f5feaa38fb6cf2253dd713f57a +LibUV.v2.0.1+18.powerpc64le-linux-gnu.tar.gz/md5/af0e43d9d0aa91dd82b63220d96991ef +LibUV.v2.0.1+18.powerpc64le-linux-gnu.tar.gz/sha512/9fabe3089e4fc60e910770c32d36300ce8ef36c28e8cc9c72fbecba6eb80285ee8174e84e4452fb4ce90ee7c7f94e99b03fce47d8c579bd614bfffd553f93666 +LibUV.v2.0.1+18.x86_64-apple-darwin.tar.gz/md5/871040e874eedae54553d8f1c91b9133 +LibUV.v2.0.1+18.x86_64-apple-darwin.tar.gz/sha512/d5eee08b65e4bb8b444c61ac277bec9ef944b9279dd7f0732b3cd91d47788c77938e5db71e019e01bbe7785a75df95faf14368764f700c6b7a6b9e4d96d6b4c2 +LibUV.v2.0.1+18.x86_64-linux-gnu.tar.gz/md5/d2d186952c6d017fe33f6a6bea63a3ea +LibUV.v2.0.1+18.x86_64-linux-gnu.tar.gz/sha512/15501534bf5721e6bb668aabe6dc6375349f7a284e28df0609d00982e7e456908bd6868722391afa7f44a5c82faedc8cf544f69a0e4fb9fb0d529b3ae3d44d78 +LibUV.v2.0.1+18.x86_64-linux-musl.tar.gz/md5/271d4d40a1ae53ed5b2376e5936cfcf9 +LibUV.v2.0.1+18.x86_64-linux-musl.tar.gz/sha512/1956f059ed01f66b72349d6561b04e6a89b7257c0f838d7fbdd2cee79bd126bb46b93bf944a042b5a6a235762a7a0cdd117207342dd55a0c58653a70b4a38d48 +LibUV.v2.0.1+18.x86_64-unknown-freebsd.tar.gz/md5/62fe8523948914fbe7e28bf0b8d73594 +LibUV.v2.0.1+18.x86_64-unknown-freebsd.tar.gz/sha512/e6486888028c96975f74bc9313cba9706f6bf2be085aa776c44cbb2886753b2eee62469a0be92eb0542df1d0f51db3b34c7ba5e46842e16c6ff1d20e11b75322 +LibUV.v2.0.1+18.x86_64-w64-mingw32.tar.gz/md5/ae103f24b6e1830cdbe02143826fe551 +LibUV.v2.0.1+18.x86_64-w64-mingw32.tar.gz/sha512/f814085c135815947f342ff24fa0e1015e283ccece84a5b8dd5ccec0f5928a129e5fd79100a33b131376ad696f70b5acadcc5a02a7e6544635ecf7e18003ba1c +libuv-af4172ec713ee986ba1a989b9e33993a07c60c9e.tar.gz/md5/c1a7d3c74ef3999052f3bfe426264353 +libuv-af4172ec713ee986ba1a989b9e33993a07c60c9e.tar.gz/sha512/a3f16863b711ddeeb5ab8d135d7df7a4be19cc2b9821fc78c8cd3ba421231d39b7d8bd9965321455094fda01584842a58f60612d93082b4fe32210b8aa44d999 diff --git a/deps/libuv.version b/deps/libuv.version index db6afd9f1bb51..ebfc63927d9db 100644 --- a/deps/libuv.version +++ b/deps/libuv.version @@ -6,4 +6,4 @@ LIBUV_JLL_NAME := LibUV ## source build LIBUV_VER := 2 LIBUV_BRANCH=julia-uv2-1.48.0 -LIBUV_SHA1=c57e7f06cbe697ca8ea9215ce054a608c451b193 +LIBUV_SHA1=af4172ec713ee986ba1a989b9e33993a07c60c9e diff --git a/src/sys.c b/src/sys.c index 107a8f7637763..712d232da363a 100644 --- a/src/sys.c +++ b/src/sys.c @@ -478,25 +478,10 @@ JL_DLLEXPORT int jl_cpu_threads(void) JL_NOTSAFEPOINT JL_DLLEXPORT int jl_effective_threads(void) JL_NOTSAFEPOINT { - int cpu = jl_cpu_threads(); - int masksize = uv_cpumask_size(); - if (masksize < 0 || jl_running_under_rr(0)) - return cpu; - uv_thread_t tid = uv_thread_self(); - char *cpumask = (char *)calloc(masksize, sizeof(char)); - int err = uv_thread_getaffinity(&tid, cpumask, masksize); - if (err) { - free(cpumask); - jl_safe_printf("WARNING: failed to get thread affinity (%s %d)\n", uv_err_name(err), - err); - return cpu; - } - int n = 0; - for (size_t i = 0; i < masksize; i++) { - n += cpumask[i]; - } - free(cpumask); - return n < cpu ? n : cpu; + // We want the more conservative estimate of the two. + int cpu_threads = jl_cpu_threads(); + int available_parallelism = uv_available_parallelism(); + return available_parallelism < cpu_threads ? available_parallelism : cpu_threads; } diff --git a/stdlib/LibUV_jll/Project.toml b/stdlib/LibUV_jll/Project.toml index 0c1ad8f25f848..fb03c6b996048 100644 --- a/stdlib/LibUV_jll/Project.toml +++ b/stdlib/LibUV_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUV_jll" uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+17" +version = "2.0.1+18" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From fa1c6b211e2b13dd46221aac0c02791aa8ba34d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 7 Sep 2024 13:04:14 +0100 Subject: [PATCH 180/548] [LinearAlgebra] Initialise number of BLAS threads with `jl_effective_threads` (#55574) This is a safer estimate than `Sys.CPU_THREADS` to avoid oversubscribing the machine when running distributed applications, or when the Julia process is constrained by external controls (`taskset`, `cgroups`, etc.). Fix #55572 --- NEWS.md | 2 ++ stdlib/LinearAlgebra/src/LinearAlgebra.jl | 4 ++-- test/threads.jl | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 95a8a51c67ac8..c12cc3c64300c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -128,6 +128,8 @@ Standard library changes between different eigendecomposition algorithms ([#49355]). * Added a generic version of the (unblocked) pivoted Cholesky decomposition (callable via `cholesky[!](A, RowMaximum())`) ([#54619]). +* The number of default BLAS threads now respects process affinity, instead of + using total number of logical threads available on the system ([#55574]). #### Logging diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index be59516f086ab..27d4255fb656b 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -843,9 +843,9 @@ function __init__() # https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "GOTO_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS") @static if Sys.isapple() && Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "aarch64" - BLAS.set_num_threads(max(1, Sys.CPU_THREADS)) + BLAS.set_num_threads(max(1, @ccall(jl_effective_threads()::Cint))) else - BLAS.set_num_threads(max(1, Sys.CPU_THREADS ÷ 2)) + BLAS.set_num_threads(max(1, @ccall(jl_effective_threads()::Cint) ÷ 2)) end end end diff --git a/test/threads.jl b/test/threads.jl index 2832f2a0e972c..6265368c2ac79 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -359,3 +359,18 @@ end @test jl_setaffinity(0, mask, cpumasksize) == 0 end end + +# Make sure default number of BLAS threads respects CPU affinity: issue #55572. +@testset "LinearAlgebra number of default threads" begin + if AFFINITY_SUPPORTED + allowed_cpus = findall(uv_thread_getaffinity()) + cmd = addenv(`$(Base.julia_cmd()) --startup-file=no -E 'using LinearAlgebra; BLAS.get_num_threads()'`, + # Remove all variables which could affect the default number of threads + "OPENBLAS_NUM_THREADS"=>nothing, + "GOTO_NUM_THREADS"=>nothing, + "OMP_NUM_THREADS"=>nothing) + for n in 1:min(length(allowed_cpus), 8) # Cap to 8 to avoid too many tests on large systems + @test readchomp(setcpuaffinity(cmd, allowed_cpus[1:n])) == string(max(1, n ÷ 2)) + end + end +end From e95eeddb30737962791f51e637dabdfe38c1a9bc Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Sat, 7 Sep 2024 09:43:58 -0400 Subject: [PATCH 181/548] Artifacts: Improve type-stability (#55707) This improves Artifacts.jl to make `artifact"..."` fully type-stable, so that it can be used with `--trim`. This is a requirement for JLL support w/ trimmed executables. Dependent on https://github.com/JuliaLang/julia/pull/55016 --------- Co-authored-by: Gabriel Baraldi --- base/loading.jl | 1 - base/toml_parser.jl | 100 ++++++++++++++++-------------- stdlib/Artifacts/src/Artifacts.jl | 30 +++++---- stdlib/TOML/test/values.jl | 2 +- 4 files changed, 68 insertions(+), 65 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 4dc735f0099d8..e0a2563dd0178 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -269,7 +269,6 @@ struct TOMLCache{Dates} d::Dict{String, CachedTOMLDict} end TOMLCache(p::TOML.Parser) = TOMLCache(p, Dict{String, CachedTOMLDict}()) -# TODO: Delete this converting constructor once Pkg stops using it TOMLCache(p::TOML.Parser, d::Dict{String, Dict{String, Any}}) = TOMLCache(p, convert(Dict{String, CachedTOMLDict}, d)) const TOML_CACHE = TOMLCache(TOML.Parser{nothing}()) diff --git a/base/toml_parser.jl b/base/toml_parser.jl index cc1455f61928b..4d07cfed05d8a 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -84,9 +84,6 @@ mutable struct Parser{Dates} # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} - - # Optionally populate with the Dates stdlib to change the type of Date types returned - Dates::Union{Module, Nothing} # TODO: remove once Pkg is updated end function Parser{Dates}(str::String; filepath=nothing) where {Dates} @@ -106,8 +103,7 @@ function Parser{Dates}(str::String; filepath=nothing) where {Dates} IdSet{Any}(), # static_arrays IdSet{TOMLDict}(), # defined_tables root, - filepath, - nothing + filepath ) startup(l) return l @@ -495,8 +491,10 @@ function recurse_dict!(l::Parser, d::Dict, dotted_keys::AbstractVector{String}, d = d::TOMLDict key = dotted_keys[i] d = get!(TOMLDict, d, key) - if d isa Vector + if d isa Vector{Any} d = d[end] + elseif d isa Vector + return ParserError(ErrKeyAlreadyHasValue) end check && @try check_allowed_add_key(l, d, i == length(dotted_keys)) end @@ -537,7 +535,7 @@ function parse_array_table(l)::Union{Nothing, ParserError} end d = @try recurse_dict!(l, l.root, @view(table_key[1:end-1]), false) k = table_key[end] - old = get!(() -> [], d, k) + old = get!(() -> Any[], d, k) if old isa Vector if old in l.static_arrays return ParserError(ErrAddArrayToStaticArray) @@ -546,7 +544,7 @@ function parse_array_table(l)::Union{Nothing, ParserError} return ParserError(ErrArrayTreatedAsDictionary) end d_new = TOMLDict() - push!(old, d_new) + push!(old::Vector{Any}, d_new) push!(l.defined_tables, d_new) l.active_table = d_new @@ -668,41 +666,20 @@ end # Array # ######### -function push!!(v::Vector, el) - # Since these types are typically non-inferable, they are a big invalidation risk, - # and since it's used by the package-loading infrastructure the cost of invalidation - # is high. Therefore, this is written to reduce the "exposed surface area": e.g., rather - # than writing `T[el]` we write it as `push!(Vector{T}(undef, 1), el)` so that there - # is no ambiguity about what types of objects will be created. - T = eltype(v) - t = typeof(el) - if el isa T || t === T - push!(v, el::T) - return v - elseif T === Union{} - out = Vector{t}(undef, 1) - out[1] = el - return out - else - if T isa Union - newT = Any - else - newT = Union{T, typeof(el)} - end - new = Array{newT}(undef, length(v)) - copy!(new, v) - return push!(new, el) +function copyto_typed!(a::Vector{T}, b::Vector) where T + for i in 1:length(b) + a[i] = b[i]::T end + return nothing end -function parse_array(l::Parser)::Err{Vector} +function parse_array(l::Parser{Dates})::Err{Vector} where Dates skip_ws_nl(l) - array = Vector{Union{}}() + array = Vector{Any}() empty_array = accept(l, ']') while !empty_array v = @try parse_value(l) - # TODO: Worth to function barrier this? - array = push!!(array, v) + array = push!(array, v) # There can be an arbitrary number of newlines and comments before a value and before the closing bracket. skip_ws_nl(l) comma = accept(l, ',') @@ -712,8 +689,40 @@ function parse_array(l::Parser)::Err{Vector} return ParserError(ErrExpectedCommaBetweenItemsArray) end end - push!(l.static_arrays, array) - return array + # check for static type throughout array + T = !isempty(array) ? typeof(array[1]) : Union{} + for el in array + if typeof(el) != T + T = Any + break + end + end + if T === Any + new = array + elseif T === String + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Bool + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Int64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === UInt64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Float64 + new = Array{T}(undef, length(array)) + copyto_typed!(new, array) + elseif T === Union{} + new = Any[] + elseif (T === TOMLDict) || (T == BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) || + (T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime) + # do nothing, leave as Vector{Any} + new = array + else @assert false end + push!(l.static_arrays, new) + return new end @@ -1025,10 +1034,9 @@ function parse_datetime(l) end function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.DateTime(year, month, day, h, m, s, ms) + return Dates.DateTime(year, month, day, h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1039,10 +1047,9 @@ function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) wh end function try_return_date(p::Parser{Dates}, year, month, day) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.Date(year, month, day) + return Dates.Date(year, month, day) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() @@ -1062,10 +1069,9 @@ function parse_local_time(l::Parser) end function try_return_time(p::Parser{Dates}, h, m, s, ms) where Dates - if Dates !== nothing || p.Dates !== nothing - mod = Dates !== nothing ? Dates : p.Dates + if Dates !== nothing try - return mod.Time(h, m, s, ms) + return Dates.Time(h, m, s, ms) catch ex ex isa ArgumentError && return ParserError(ErrParsingDateTime) rethrow() diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index bfc884cc30634..9bca72f6c7a14 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -175,13 +175,11 @@ function load_overrides(;force::Bool = false)::Dict{Symbol, Any} end end - overrides = Dict{Symbol,Any}( - # Overrides by UUID - :UUID => overrides_uuid, - - # Overrides by hash - :hash => overrides_hash - ) + overrides = Dict{Symbol,Any}() + # Overrides by UUID + overrides[:UUID] = overrides_uuid + # Overrides by hash + overrides[:hash] = overrides_hash ARTIFACT_OVERRIDES[] = overrides return overrides @@ -351,7 +349,7 @@ function process_overrides(artifact_dict::Dict, pkg_uuid::Base.UUID) # If we've got a platform-specific friend, override all hashes: artifact_dict_name = artifact_dict[name] - if isa(artifact_dict_name, Array) + if isa(artifact_dict_name, Vector{Any}) for entry in artifact_dict_name entry = entry::Dict{String,Any} hash = SHA1(entry["git-tree-sha1"]::String) @@ -544,7 +542,7 @@ function jointail(dir, tail) end end -function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, @nospecialize(lazyartifacts)) +function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, ::Val{LazyArtifacts}) where LazyArtifacts pkg = Base.PkgId(__module__) if pkg.uuid !== nothing # Process overrides for this UUID, if we know what it is @@ -563,11 +561,11 @@ function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dic # If not, try determining what went wrong: meta = artifact_meta(name, artifact_dict, artifacts_toml; platform) if meta !== nothing && get(meta, "lazy", false) - if lazyartifacts isa Module && isdefined(lazyartifacts, :ensure_artifact_installed) - if nameof(lazyartifacts) in (:Pkg, :Artifacts) + if LazyArtifacts isa Module && isdefined(LazyArtifacts, :ensure_artifact_installed) + if nameof(LazyArtifacts) in (:Pkg, :Artifacts) Base.depwarn("using Pkg instead of using LazyArtifacts is deprecated", :var"@artifact_str", force=true) end - return jointail(lazyartifacts.ensure_artifact_installed(string(name), meta, artifacts_toml; platform), path_tail) + return jointail(LazyArtifacts.ensure_artifact_installed(string(name), meta, artifacts_toml; platform), path_tail) end error("Artifact $(repr(name)) is a lazy artifact; package developers must call `using LazyArtifacts` in $(__module__) before using lazy artifacts.") end @@ -699,10 +697,10 @@ macro artifact_str(name, platform=nothing) # Check if the user has provided `LazyArtifacts`, and thus supports lazy artifacts # If not, check to see if `Pkg` or `Pkg.Artifacts` has been imported. - lazyartifacts = nothing + LazyArtifacts = nothing for module_name in (:LazyArtifacts, :Pkg, :Artifacts) if isdefined(__module__, module_name) - lazyartifacts = GlobalRef(__module__, module_name) + LazyArtifacts = GlobalRef(__module__, module_name) break end end @@ -714,7 +712,7 @@ macro artifact_str(name, platform=nothing) platform = HostPlatform() artifact_name, artifact_path_tail, hash = artifact_slash_lookup(name, artifact_dict, artifacts_toml, platform) return quote - Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), $(artifact_name), $(artifact_path_tail), $(artifact_dict), $(hash), $(platform), $(lazyartifacts))::String + Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), $(artifact_name), $(artifact_path_tail), $(artifact_dict), $(hash), $(platform), Val($(LazyArtifacts)))::String end else if platform === nothing @@ -723,7 +721,7 @@ macro artifact_str(name, platform=nothing) return quote local platform = $(esc(platform)) local artifact_name, artifact_path_tail, hash = artifact_slash_lookup($(esc(name)), $(artifact_dict), $(artifacts_toml), platform) - Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), artifact_name, artifact_path_tail, $(artifact_dict), hash, platform, $(lazyartifacts))::String + Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), artifact_name, artifact_path_tail, $(artifact_dict), hash, platform, Val($(LazyArtifacts)))::String end end end diff --git a/stdlib/TOML/test/values.jl b/stdlib/TOML/test/values.jl index 4fc49d47fc98d..53be1b04708b3 100644 --- a/stdlib/TOML/test/values.jl +++ b/stdlib/TOML/test/values.jl @@ -172,6 +172,6 @@ end @testset "Array" begin @test testval("[1,2,3]", Int64[1,2,3]) @test testval("[1.0, 2.0, 3.0]", Float64[1.0, 2.0, 3.0]) - @test testval("[1.0, 2.0, 3]", Union{Int64, Float64}[1.0, 2.0, Int64(3)]) + @test testval("[1.0, 2.0, 3]", Any[1.0, 2.0, Int64(3)]) @test testval("[1.0, 2, \"foo\"]", Any[1.0, Int64(2), "foo"]) end From 8cae8d138b8105e5aa3b96cbe40713a97690e7d4 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 8 Sep 2024 00:09:16 +0530 Subject: [PATCH 182/548] Remove redundant conversion in structured matrix broadcasting (#55695) The additional construction is unnecessary, as we are already constructing a `Matrix`. Performance: ```julia julia> using LinearAlgebra julia> U = UpperTriangular(rand(1000,1000)); julia> L = LowerTriangular(rand(1000,1000)); julia> @btime $U .+ $L; 1.956 ms (6 allocations: 15.26 MiB) # nightly 1.421 ms (3 allocations: 7.63 MiB) # This PR ``` --- stdlib/LinearAlgebra/src/structuredbroadcast.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index 3c60b37959f91..0c06f84116fc7 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -96,7 +96,7 @@ structured_broadcast_alloc(bc, ::Type{UnitLowerTriangular}, ::Type{ElType}, n) w structured_broadcast_alloc(bc, ::Type{UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = UnitUpperTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{Matrix}, ::Type{ElType}, n) where {ElType} = - Matrix(Array{ElType}(undef, n, n)) + Array{ElType}(undef, n, n) # A _very_ limited list of structure-preserving functions known at compile-time. This list is # derived from the formerly-implemented `broadcast` methods in 0.6. Note that this must From 4f0a333d9d76df76a6383ed2113e66c789d5ecee Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Sep 2024 18:16:22 -0400 Subject: [PATCH 183/548] [Profile] fix threading issue (#55704) I forgot about the existence of threads, so had hard-coded this to only support one thread. Clearly that is not sufficient though, so use the semaphore here as it is intended to be used. Fixes #55703 --------- Co-authored-by: Ian Butterworth --- src/signals-unix.c | 24 ++++++++++-------------- stdlib/Profile/test/runtests.jl | 3 ++- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index d0885b6bdee3f..f99eca31730b6 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -426,6 +426,7 @@ pthread_mutex_t in_signal_lock; // shared with jl_delete_thread static bt_context_t *signal_context; // protected by in_signal_lock static int exit_signal_cond = -1; static int signal_caught_cond = -1; +static int signals_inflight = 0; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { @@ -438,7 +439,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - if (jl_atomic_load(&ptls2->signal_request) != 0) { + while (signals_inflight) { // something is wrong, or there is already a usr2 in flight elsewhere // try to wait for it to finish or wait for timeout struct pollfd event = {signal_caught_cond, POLLIN, 0}; @@ -450,25 +451,16 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - } - // check for any stale signal_caught_cond events - struct pollfd event = {signal_caught_cond, POLLIN, 0}; - do { - err = poll(&event, 1, 0); - } while (err == -1 && errno == EINTR); - if (err == -1) { - pthread_mutex_unlock(&in_signal_lock); - return 0; - } - if ((event.revents & POLLIN) != 0) { // consume it before continuing eventfd_t got; do { err = read(signal_caught_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); - assert(got == 1); (void) got; + assert(signals_inflight >= got); + signals_inflight -= got; } + signals_inflight++; sig_atomic_t request = jl_atomic_exchange(&ptls2->signal_request, 1); assert(request == 0 || request == -1); request = 1; @@ -485,6 +477,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) if (err == -1) { // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { + signals_inflight--; pthread_mutex_unlock(&in_signal_lock); return 0; } @@ -494,7 +487,9 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) err = read(signal_caught_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); - assert(got == 1); (void) got; + assert(signals_inflight >= got); + signals_inflight -= got; + signals_inflight++; // Now the other thread is waiting on exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) request = jl_atomic_load_acquire(&ptls2->signal_request); @@ -521,6 +516,7 @@ static void jl_try_deliver_sigint(void) jl_safepoint_enable_sigint(); jl_wake_libuv(); pthread_mutex_lock(&in_signal_lock); + signals_inflight++; jl_atomic_store_release(&ptls2->signal_request, 2); // This also makes sure `sleep` is aborted. pthread_kill(ptls2->system_id, SIGUSR2); diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 32d628130c4ac..1769cbd12da3e 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -168,7 +168,8 @@ let cmd = Base.julia_cmd() println("done") print(Profile.len_data()) """ - p = open(`$cmd -e $script`) + # use multiple threads here to ensure that profiling works with threading + p = open(`$cmd -t2 -e $script`) t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING debuginfo registration test BY PROFILE TEST WATCHDOG\n") From 8173750700d21a6c35b47e76075eb84a9cb8a393 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sun, 8 Sep 2024 12:12:38 +0200 Subject: [PATCH 184/548] delete flaky ranges/`TwicePrecision` test (#55712) Fixes #55710 --- test/ranges.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/ranges.jl b/test/ranges.jl index 16b2c6bf7b77b..86cd1c3f2345c 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -292,15 +292,10 @@ end rand_twiceprecision(::Type{T}) where {T<:Number} = Base.TwicePrecision{T}(rand(widen(T))) - rand_twiceprecision_is_ok(::Type{T}) where {T<:Number} = @test !iszero(rand_twiceprecision(T).lo) - # For this test the `BigFloat` mantissa needs to be just a bit # larger than the `Float64` mantissa setprecision(BigFloat, 70) do n = 10 - @testset "rand twiceprecision is ok" for T ∈ (Float32, Float64), i ∈ 1:n - rand_twiceprecision_is_ok(T) - end @testset "twiceprecision roundtrip is not lossy 1" for i ∈ 1:n twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) end From 95c643a689293eb91a47cc83c41533a94c3677cc Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 8 Sep 2024 18:12:38 +0530 Subject: [PATCH 185/548] Avoid stack overflow in triangular eigvecs (#55497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a stack overflow in ```julia julia> using LinearAlgebra, StaticArrays julia> U = UpperTriangular(SMatrix{2,2}(1:4)) 2×2 UpperTriangular{Int64, SMatrix{2, 2, Int64, 4}} with indices SOneTo(2)×SOneTo(2): 1 3 ⋅ 4 julia> eigvecs(U) Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable. ERROR: StackOverflowError: Stacktrace: [1] eigvecs(A::UpperTriangular{Float32, SMatrix{2, 2, Float32, 4}}) (repeats 79984 times) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/triangular.jl:2749 ``` After this, ```julia julia> eigvecs(U) 2×2 Matrix{Float32}: 1.0 1.0 0.0 1.0 ``` --- stdlib/LinearAlgebra/src/triangular.jl | 8 ++++++++ stdlib/LinearAlgebra/test/triangular.jl | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 71473e0dc1174..df0d0d4fd0d8b 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -2782,6 +2782,14 @@ end # Generic eigensystems eigvals(A::AbstractTriangular) = diag(A) +# fallback for unknown types +function eigvecs(A::AbstractTriangular{<:BlasFloat}) + if istriu(A) + eigvecs(UpperTriangular(Matrix(A))) + else # istril(A) + eigvecs(LowerTriangular(Matrix(A))) + end +end function eigvecs(A::AbstractTriangular{T}) where T TT = promote_type(T, Float32) if TT <: BlasFloat diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 8748d11bd1da4..a09d0092e9f39 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1198,6 +1198,22 @@ end end end +@testset "eigvecs for AbstractTriangular" begin + S = SizedArrays.SizedArray{(3,3)}(reshape(1:9,3,3)) + for T in (UpperTriangular, UnitUpperTriangular, + LowerTriangular, UnitLowerTriangular) + U = T(S) + V = eigvecs(U) + λ = eigvals(U) + @test U * V ≈ V * Diagonal(λ) + + MU = MyTriangular(U) + V = eigvecs(U) + λ = eigvals(U) + @test MU * V ≈ V * Diagonal(λ) + end +end + @testset "(l/r)mul! and (l/r)div! for generic triangular" begin @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) M = MyTriangular(T(rand(4,4))) From 527fa1c1cd4335074e775bde686dbcbdf921c177 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 9 Sep 2024 01:17:29 -0400 Subject: [PATCH 186/548] builtins: add `Core.throw_methoderror` (#55705) This allows us to simulate/mark calls that are known-to-fail. Required for https://github.com/JuliaLang/julia/pull/54972/ --- base/compiler/abstractinterpretation.jl | 13 +++++++++++++ .../compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl | 1 + base/compiler/tfuncs.jl | 4 ++++ src/builtin_proto.h | 1 + src/builtins.c | 9 +++++++++ src/gf.c | 4 ++-- src/julia_internal.h | 2 +- src/staticdata.c | 2 +- 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 8623a32ddbb2b..83a39ce10d891 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2226,6 +2226,17 @@ function abstract_throw(interp::AbstractInterpreter, argtypes::Vector{Any}, ::Ab return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) end +function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vector{Any}, ::AbsIntState) + exct = if length(argtypes) == 1 + ArgumentError + elseif !isvarargtype(argtypes[2]) + MethodError + else + tmerge(𝕃ᵢ, MethodError, ArgumentError) + end + return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) +end + # call where the function is known exactly function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, @@ -2246,6 +2257,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_applicable(interp, argtypes, sv, max_methods) elseif f === throw return abstract_throw(interp, argtypes, sv) + elseif f === Core.throw_methoderror + return abstract_throw_methoderror(interp, argtypes, sv) end rt = abstract_call_builtin(interp, f, arginfo, sv) ft = popfirst!(argtypes) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index f74cb90e6ab51..6967efe495be1 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -1213,6 +1213,7 @@ escape_builtin!(::typeof(Core.donotdelete), _...) = false # not really safe, but `ThrownEscape` will be imposed later escape_builtin!(::typeof(isdefined), _...) = false escape_builtin!(::typeof(throw), _...) = false +escape_builtin!(::typeof(Core.throw_methoderror), _...) = false function escape_builtin!(::typeof(ifelse), astate::AnalysisState, pc::Int, args::Vector{Any}) length(args) == 4 || return false diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 0c57c04a6ddea..64a93bd07c2fa 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -89,6 +89,7 @@ function add_tfunc(@nospecialize(f::Builtin), minarg::Int, maxarg::Int, @nospeci end add_tfunc(throw, 1, 1, @nospecs((𝕃::AbstractLattice, x)->Bottom), 0) +add_tfunc(Core.throw_methoderror, 1, INT_INF, @nospecs((𝕃::AbstractLattice, x)->Bottom), 0) # the inverse of typeof_tfunc # returns (type, isexact, isconcrete, istype) @@ -2313,6 +2314,7 @@ const _CONSISTENT_BUILTINS = Any[ (<:), typeassert, throw, + Core.throw_methoderror, setfield!, donotdelete ] @@ -2335,6 +2337,7 @@ const _EFFECT_FREE_BUILTINS = [ (<:), typeassert, throw, + Core.throw_methoderror, getglobal, compilerbarrier, ] @@ -2350,6 +2353,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[ isa, nfields, throw, + Core.throw_methoderror, tuple, typeassert, typeof, diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 8b97c46df72da..7fbd555758675 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -69,6 +69,7 @@ DECLARE_BUILTIN(svec); DECLARE_BUILTIN(swapfield); DECLARE_BUILTIN(swapglobal); DECLARE_BUILTIN(throw); +DECLARE_BUILTIN(throw_methoderror); DECLARE_BUILTIN(tuple); DECLARE_BUILTIN(typeassert); DECLARE_BUILTIN(typeof); diff --git a/src/builtins.c b/src/builtins.c index 152836bcab6a9..75c4d02c898b2 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -580,6 +580,14 @@ JL_CALLABLE(jl_f_throw) return jl_nothing; } +JL_CALLABLE(jl_f_throw_methoderror) +{ + JL_NARGSV(throw_methoderror, 1); + size_t world = jl_get_tls_world_age(); + jl_method_error(args[0], &args[1], nargs, world); + return jl_nothing; +} + JL_CALLABLE(jl_f_ifelse) { JL_NARGS(ifelse, 3, 3); @@ -2437,6 +2445,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin_func("_compute_sparams", jl_f__compute_sparams); add_builtin_func("_svec_ref", jl_f__svec_ref); add_builtin_func("current_scope", jl_f_current_scope); + add_builtin_func("throw_methoderror", jl_f_throw_methoderror); // builtin types add_builtin("Any", (jl_value_t*)jl_any_type); diff --git a/src/gf.c b/src/gf.c index 95bab0d0f832e..970cb62b8a862 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2335,7 +2335,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method JL_GC_POP(); } -static void JL_NORETURN jl_method_error_bare(jl_function_t *f, jl_value_t *args, size_t world) +static void JL_NORETURN jl_method_error_bare(jl_value_t *f, jl_value_t *args, size_t world) { if (jl_methoderror_type) { jl_value_t *e = jl_new_struct_uninit(jl_methoderror_type); @@ -2360,7 +2360,7 @@ static void JL_NORETURN jl_method_error_bare(jl_function_t *f, jl_value_t *args, // not reached } -void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world) +void JL_NORETURN jl_method_error(jl_value_t *f, jl_value_t **args, size_t na, size_t world) { jl_value_t *argtup = jl_f_tuple(NULL, args, na - 1); JL_GC_PUSH1(&argtup); diff --git a/src/julia_internal.h b/src/julia_internal.h index ac955e2bcb3a6..f00667d016796 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -705,7 +705,7 @@ int jl_valid_type_param(jl_value_t *v); JL_DLLEXPORT jl_value_t *jl_apply_2va(jl_value_t *f, jl_value_t **args, uint32_t nargs); -void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world); +void JL_NORETURN jl_method_error(jl_value_t *F, jl_value_t **args, size_t na, size_t world); JL_DLLEXPORT jl_value_t *jl_get_exceptionf(jl_datatype_t *exception_type, const char *fmt, ...); JL_DLLEXPORT void jl_typeassert(jl_value_t *x, jl_value_t *t); diff --git a/src/staticdata.c b/src/staticdata.c index 6dfe5e91a9c55..b991dfe8f37f3 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -497,7 +497,7 @@ static htable_t relocatable_ext_cis; // (reverse of fptr_to_id) // This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C. static const jl_fptr_args_t id_to_fptrs[] = { - &jl_f_throw, &jl_f_is, &jl_f_typeof, &jl_f_issubtype, &jl_f_isa, + &jl_f_throw, &jl_f_throw_methoderror, &jl_f_is, &jl_f_typeof, &jl_f_issubtype, &jl_f_isa, &jl_f_typeassert, &jl_f__apply_iterate, &jl_f__apply_pure, &jl_f__call_latest, &jl_f__call_in_world, &jl_f__call_in_world_total, &jl_f_isdefined, &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, From 5272dad1f8f688b24a4cd85ce02b1d57cc7840b7 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 9 Sep 2024 10:01:16 -0400 Subject: [PATCH 187/548] Small missing tests for Irrationals (#55657) Looks like a bunch of methods for `Irrational`s are tested but not picked up by coverage... --- test/numbers.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/numbers.jl b/test/numbers.jl index 34e775f9b2eea..fc3dc2c06bb7c 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -1158,6 +1158,8 @@ end end @testset "Irrationals compared with Rationals and Floats" begin + @test pi != Float64(pi) + @test Float64(pi) != pi @test Float64(pi,RoundDown) < pi @test Float64(pi,RoundUp) > pi @test !(Float64(pi,RoundDown) > pi) @@ -1176,6 +1178,7 @@ end @test nextfloat(big(pi)) > pi @test !(prevfloat(big(pi)) > pi) @test !(nextfloat(big(pi)) < pi) + @test big(typeof(pi)) == BigFloat @test 2646693125139304345//842468587426513207 < pi @test !(2646693125139304345//842468587426513207 > pi) From 169e9e8de1b1429e8efbacfd6274c2a5399989bf Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 9 Sep 2024 12:10:33 -0300 Subject: [PATCH 188/548] Implement faster thread local rng for scheduler (#55501) Implement optimal uniform random number generator using the method proposed in https://github.com/swiftlang/swift/pull/39143 based on OpenSSL's implementation of it in https://github.com/openssl/openssl/blob/1d2cbd9b5a126189d5e9bc78a3bdb9709427d02b/crypto/rand/rand_uniform.c#L13-L99 This PR also fixes some bugs found while developing it. This is a replacement for https://github.com/JuliaLang/julia/pull/50203 and fixes the issues found by @IanButterworth with both rngs C rng image New scheduler rng image ~On my benchmarks the julia implementation seems to be almost 50% faster than the current implementation.~ With oscars suggestion of removing the debiasing this is now almost 5x faster than the original implementation. And almost fully branchless We might want to backport the two previous commits since they technically fix bugs. --------- Co-authored-by: Valentin Churavy --- base/partr.jl | 55 ++++++++++++++++++++++++++++++++++++++- src/ccall.cpp | 32 +++++++++++++++++++++++ src/jl_exported_funcs.inc | 2 ++ src/julia_threads.h | 2 ++ src/scheduler.c | 9 ------- src/threading.c | 12 +++++++++ 6 files changed, 102 insertions(+), 10 deletions(-) diff --git a/base/partr.jl b/base/partr.jl index 8c95e3668ee74..6053a584af5ba 100644 --- a/base/partr.jl +++ b/base/partr.jl @@ -20,7 +20,60 @@ const heaps = [Vector{taskheap}(undef, 0), Vector{taskheap}(undef, 0)] const heaps_lock = [SpinLock(), SpinLock()] -cong(max::UInt32) = iszero(max) ? UInt32(0) : ccall(:jl_rand_ptls, UInt32, (UInt32,), max) + UInt32(1) +""" + cong(max::UInt32) + +Return a random UInt32 in the range `1:max` except if max is 0, in that case return 0. +""" +cong(max::UInt32) = iszero(max) ? UInt32(0) : rand_ptls(max) + UInt32(1) #TODO: make sure users don't use 0 and remove this check + +get_ptls_rng() = ccall(:jl_get_ptls_rng, UInt64, ()) + +set_ptls_rng(seed::UInt64) = ccall(:jl_set_ptls_rng, Cvoid, (UInt64,), seed) + +""" + rand_ptls(max::UInt32) + +Return a random UInt32 in the range `0:max-1` using the thread-local RNG +state. Max must be greater than 0. +""" +Base.@assume_effects :removable :inaccessiblememonly :notaskstate function rand_ptls(max::UInt32) + rngseed = get_ptls_rng() + val, seed = rand_uniform_max_int32(max, rngseed) + set_ptls_rng(seed) + return val % UInt32 +end + +# This implementation is based on OpenSSLs implementation of rand_uniform +# https://github.com/openssl/openssl/blob/1d2cbd9b5a126189d5e9bc78a3bdb9709427d02b/crypto/rand/rand_uniform.c#L13-L99 +# Comments are vendored from their implementation as well. +# For the original developer check the PR to swift https://github.com/apple/swift/pull/39143. + +# Essentially it boils down to incrementally generating a fixed point +# number on the interval [0, 1) and multiplying this number by the upper +# range limit. Once it is certain what the fractional part contributes to +# the integral part of the product, the algorithm has produced a definitive +# result. +""" + rand_uniform_max_int32(max::UInt32, seed::UInt64) + +Return a random UInt32 in the range `0:max-1` using the given seed. +Max must be greater than 0. +""" +Base.@assume_effects :total function rand_uniform_max_int32(max::UInt32, seed::UInt64) + if max == UInt32(1) + return UInt32(0), seed + end + # We are generating a fixed point number on the interval [0, 1). + # Multiplying this by the range gives us a number on [0, upper). + # The high word of the multiplication result represents the integral part + # This is not completely unbiased as it's missing the fractional part of the original implementation but it's good enough for our purposes + seed = UInt64(69069) * seed + UInt64(362437) + prod = (UInt64(max)) * (seed % UInt32) # 64 bit product + i = prod >> 32 % UInt32 # integral part + return i % UInt32, seed +end + function multiq_sift_up(heap::taskheap, idx::Int32) diff --git a/src/ccall.cpp b/src/ccall.cpp index 36808e13fdbf9..7ab8cfa974d6f 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -22,6 +22,8 @@ TRANSFORMED_CCALL_STAT(jl_cpu_wake); TRANSFORMED_CCALL_STAT(jl_gc_safepoint); TRANSFORMED_CCALL_STAT(jl_get_ptls_states); TRANSFORMED_CCALL_STAT(jl_threadid); +TRANSFORMED_CCALL_STAT(jl_get_ptls_rng); +TRANSFORMED_CCALL_STAT(jl_set_ptls_rng); TRANSFORMED_CCALL_STAT(jl_get_tls_world_age); TRANSFORMED_CCALL_STAT(jl_get_world_counter); TRANSFORMED_CCALL_STAT(jl_gc_enable_disable_finalizers_internal); @@ -1692,6 +1694,36 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) ai.decorateInst(tid); return mark_or_box_ccall_result(ctx, tid, retboxed, rt, unionall, static_rt); } + else if (is_libjulia_func(jl_get_ptls_rng)) { + ++CCALL_STAT(jl_get_ptls_rng); + assert(lrt == getInt64Ty(ctx.builder.getContext())); + assert(!isVa && !llvmcall && nccallargs == 0); + JL_GC_POP(); + Value *ptls_p = get_current_ptls(ctx); + const int rng_offset = offsetof(jl_tls_states_t, rngseed); + Value *rng_ptr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), ptls_p, ConstantInt::get(ctx.types().T_size, rng_offset / sizeof(int8_t))); + setName(ctx.emission_context, rng_ptr, "rngseed_ptr"); + LoadInst *rng_value = ctx.builder.CreateAlignedLoad(getInt64Ty(ctx.builder.getContext()), rng_ptr, Align(sizeof(void*))); + setName(ctx.emission_context, rng_value, "rngseed"); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + ai.decorateInst(rng_value); + return mark_or_box_ccall_result(ctx, rng_value, retboxed, rt, unionall, static_rt); + } + else if (is_libjulia_func(jl_set_ptls_rng)) { + ++CCALL_STAT(jl_set_ptls_rng); + assert(lrt == getVoidTy(ctx.builder.getContext())); + assert(!isVa && !llvmcall && nccallargs == 1); + JL_GC_POP(); + Value *ptls_p = get_current_ptls(ctx); + const int rng_offset = offsetof(jl_tls_states_t, rngseed); + Value *rng_ptr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), ptls_p, ConstantInt::get(ctx.types().T_size, rng_offset / sizeof(int8_t))); + setName(ctx.emission_context, rng_ptr, "rngseed_ptr"); + assert(argv[0].V->getType() == getInt64Ty(ctx.builder.getContext())); + auto store = ctx.builder.CreateAlignedStore(argv[0].V, rng_ptr, Align(sizeof(void*))); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + ai.decorateInst(store); + return ghostValue(ctx, jl_nothing_type); + } else if (is_libjulia_func(jl_get_tls_world_age)) { bool toplevel = !(ctx.linfo && jl_is_method(ctx.linfo->def.method)); if (!toplevel) { // top level code does not see a stable world age during execution diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 7f1636ad9ad80..7abf2b055bb8c 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -452,6 +452,8 @@ XX(jl_test_cpu_feature) \ XX(jl_threadid) \ XX(jl_threadpoolid) \ + XX(jl_get_ptls_rng) \ + XX(jl_set_ptls_rng) \ XX(jl_throw) \ XX(jl_throw_out_of_memory_error) \ XX(jl_too_few_args) \ diff --git a/src/julia_threads.h b/src/julia_threads.h index 7c6de1896ca13..b697a0bf030ed 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -18,6 +18,8 @@ extern "C" { JL_DLLEXPORT int16_t jl_threadid(void); JL_DLLEXPORT int8_t jl_threadpoolid(int16_t tid) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint64_t jl_get_ptls_rng(void) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_set_ptls_rng(uint64_t new_seed) JL_NOTSAFEPOINT; // JULIA_ENABLE_THREADING may be controlled by altering JULIA_THREADS in Make.user diff --git a/src/scheduler.c b/src/scheduler.c index bd7da13aa42e3..bb2f85b52283f 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -84,15 +84,6 @@ JL_DLLEXPORT int jl_set_task_threadpoolid(jl_task_t *task, int8_t tpid) JL_NOTSA extern int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, jl_gc_markqueue_t *mq, jl_value_t *obj) JL_NOTSAFEPOINT; -// parallel task runtime -// --- - -JL_DLLEXPORT uint32_t jl_rand_ptls(uint32_t max) // [0, n) -{ - jl_ptls_t ptls = jl_current_task->ptls; - return cong(max, &ptls->rngseed); -} - // initialize the threading infrastructure // (called only by the main thread) void jl_init_threadinginfra(void) diff --git a/src/threading.c b/src/threading.c index 2f3719b89fac3..44b1192528531 100644 --- a/src/threading.c +++ b/src/threading.c @@ -314,6 +314,18 @@ JL_DLLEXPORT int8_t jl_threadpoolid(int16_t tid) JL_NOTSAFEPOINT return -1; // everything else uses threadpool -1 (does not belong to any threadpool) } +// get thread local rng +JL_DLLEXPORT uint64_t jl_get_ptls_rng(void) JL_NOTSAFEPOINT +{ + return jl_current_task->ptls->rngseed; +} + +// get thread local rng +JL_DLLEXPORT void jl_set_ptls_rng(uint64_t new_seed) JL_NOTSAFEPOINT +{ + jl_current_task->ptls->rngseed = new_seed; +} + jl_ptls_t jl_init_threadtls(int16_t tid) { #ifndef _OS_WINDOWS_ From 1463c9991c4a75730e4d6822313180ca323241c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1ll=20Haraldsson?= Date: Mon, 9 Sep 2024 17:57:18 +0000 Subject: [PATCH 189/548] Add precompile signatures to Markdown to reduce latency. (#55715) Fixes #55706 that is seemingly a 4472x regression, not just 16x (was my first guess, based on CondaPkg, also fixes or greatly mitigates https://github.com/JuliaPy/CondaPkg.jl/issues/145), and large part of 3x regression for PythonCall. --------- Co-authored-by: Kristoffer Carlsson --- stdlib/Markdown/src/Markdown.jl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/stdlib/Markdown/src/Markdown.jl b/stdlib/Markdown/src/Markdown.jl index b9ff56297fe51..1832e3a6a6956 100644 --- a/stdlib/Markdown/src/Markdown.jl +++ b/stdlib/Markdown/src/Markdown.jl @@ -122,4 +122,25 @@ import Base.Docs: catdoc catdoc(md::MD...) = MD(md...) +if Base.generating_output() + # workload to reduce latency + md""" + # H1 + ## H2 + ### H3 + **bold text** + *italicized text* + > blockquote + 1. First item + 2. Second item + 3. Third item + - First item + - Second item + - Third item + `code` + Horizontal Rule + --- + """ +end + end From 68feddc7c89b7f328eb72e336c7dbe51ac47b626 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:23:38 -0400 Subject: [PATCH 190/548] Fix invalidations for FileIO (#55593) Fixes https://github.com/JuliaIO/FileIO.jl/issues/396 --- base/strings/io.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index acbd945c8e137..754e058cd2f54 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -365,7 +365,8 @@ function _join_preserve_annotations(iterator, args...) # in nature, we extract an `AnnotatedString`, otherwise we just extract # a plain `String` from `io`. if isconcretetype(et) || !isempty(io.annotations) - read(seekstart(io), AnnotatedString{String}) + seekstart(io) + read(io, AnnotatedString{String}) else String(take!(io.io)) end From 88c90caaaff52963eb640ac2978e7b921e2dabdb Mon Sep 17 00:00:00 2001 From: Zentrik Date: Mon, 9 Sep 2024 21:19:30 +0100 Subject: [PATCH 191/548] Fix various issues with PGO+LTO makefile (#55581) This fixes various issues with the PGO+LTO makefile - `USECCACHE` doesn't work throwing an error at https://github.com/JuliaLang/julia/blob/eb5587dac02d1f6edf486a71b95149139cc5d9f7/Make.inc#L734 This is because setting `CC` and `CCX` by passing them as arguments to `make` prevents `Make.inc` from prepending these variables with `ccache` as `Make.inc` doesn't use override. To workaround this I instead set `USECLANG` and add the toolchain to the `PATH`. - To deal with similar issues for the other make flags, I pass them as environment variables which can be edited in `Make.inc`. - I add a way to build in one go by creating the `all` target, now you can just run `make` and a PGO+LTO build that profiles Julia's build will be generated. - I workaround `PROFRAW_FILES` not being reevaluated after `stage1` builds, this caused the generation of `PROFILE_FILE` to run an outdated command if `stage1` was built and affected the profraw files. This is important when building in one go. - I add a way to run rules like `binary-dist` which are not defined in this makefile with the correct toolchain which for example prevents `make binary-dist` from unnecessarily rebuilding `sys.ji`. - Include `-Wl,--undefined-version` till https://github.com/JuliaLang/julia/issues/54533 gets fixed. These changes need to be copied to the PGO+LTO+BOLT makefile and some to the BOLT makefile in a later pr. --------- Co-authored-by: Zentrik --- contrib/pgo-lto/Makefile | 42 +++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/contrib/pgo-lto/Makefile b/contrib/pgo-lto/Makefile index 896d41ac2e286..ddd86f5d5b39a 100644 --- a/contrib/pgo-lto/Makefile +++ b/contrib/pgo-lto/Makefile @@ -8,7 +8,6 @@ STAGE0_TOOLS:=$(STAGE0_BUILD)/usr/tools/ PROFILE_DIR:=$(CURDIR)/profiles PROFILE_FILE:=$(PROFILE_DIR)/merged.prof -PROFRAW_FILES:=$(wildcard $(PROFILE_DIR)/*.profraw) JULIA_ROOT:=$(CURDIR)/../.. LLVM_CXXFILT:=$(STAGE0_TOOLS)llvm-cxxfilt @@ -26,15 +25,16 @@ AFTER_STAGE1_MESSAGE:='You can now optionally collect more profiling data for us Note that running extensive scripts may result in counter overflows, which can be detected by running $\ `make top`. Afterwards run `make stage2`.' -TOOLCHAIN_FLAGS = $\ - "CC=$(STAGE0_TOOLS)clang" $\ - "CXX=$(STAGE0_TOOLS)clang++" $\ - "LD=$(STAGE0_TOOLS)ld.lld" $\ - "AR=$(STAGE0_TOOLS)llvm-ar" $\ - "RANLIB=$(STAGE0_TOOLS)llvm-ranlib" $\ - "CFLAGS+=$(PGO_CFLAGS)" $\ - "CXXFLAGS+=$(PGO_CXXFLAGS)" $\ - "LDFLAGS+=$(PGO_LDFLAGS)" +STAGE1_FLAGS:=LDFLAGS="-fuse-ld=lld -flto=thin -Wl,--undefined-version -fprofile-generate=$(PROFILE_DIR)" $\ + CFLAGS="-fprofile-generate=$(PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE)" $\ + CXXFLAGS="-fprofile-generate=$(PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE)" +STAGE2_FLAGS:=LDFLAGS="-fuse-ld=lld -flto=thin -Wl,--undefined-version -fprofile-use=$(PROFILE_FILE) -Wl,--icf=safe" $\ + CFLAGS="-fprofile-use=$(PROFILE_FILE)" $\ + CXXFLAGS="-fprofile-use=$(PROFILE_FILE)" + +COMMON_FLAGS:=USECLANG=1 USE_BINARYBUILDER_LLVM=0 + +all: stage2 # Default target as first in file $(STAGE0_BUILD) $(STAGE1_BUILD) $(STAGE2_BUILD): $(MAKE) -C $(JULIA_ROOT) O=$@ configure @@ -48,26 +48,20 @@ stage0: | $(STAGE0_BUILD) touch $@ $(STAGE1_BUILD): stage0 -stage1: PGO_CFLAGS:=-fprofile-generate=$(PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) -stage1: PGO_CXXFLAGS:=-fprofile-generate=$(PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) -stage1: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-generate=$(PROFILE_DIR) -stage1: export USE_BINARYBUILDER_LLVM=0 stage1: | $(STAGE1_BUILD) - $(MAKE) -C $(STAGE1_BUILD) $(TOOLCHAIN_FLAGS) && touch $@ + @echo "--- Build Julia Stage 1 - with instrumentation" + PATH=$(STAGE0_TOOLS):$$PATH $(STAGE1_FLAGS) $(MAKE) -C $(STAGE1_BUILD) $(COMMON_FLAGS) && touch $@ @echo $(AFTER_STAGE1_MESSAGE) -stage2: PGO_CFLAGS:=-fprofile-use=$(PROFILE_FILE) -stage2: PGO_CXXFLAGS:=-fprofile-use=$(PROFILE_FILE) -stage2: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-use=$(PROFILE_FILE) -Wl,--icf=safe -stage2: export USE_BINARYBUILDER_LLVM=0 stage2: $(PROFILE_FILE) | $(STAGE2_BUILD) - $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) && touch $@ + @echo "--- Build Julia Stage 2 - PGO + LTO optimised" + PATH=$(STAGE0_TOOLS):$$PATH $(STAGE2_FLAGS) $(MAKE) -C $(STAGE2_BUILD) $(COMMON_FLAGS) && touch $@ -install: stage2 - $(MAKE) -C $(STAGE2_BUILD) USE_BINARYBUILDER_LLVM=0 install +.DEFAULT: stage2 + PATH=$(STAGE0_TOOLS):$$PATH $(STAGE2_FLAGS) $(MAKE) -C $(STAGE2_BUILD) $(COMMON_FLAGS) $@ -$(PROFILE_FILE): stage1 $(PROFRAW_FILES) - $(LLVM_PROFDATA) merge -output=$@ $(PROFRAW_FILES) +$(PROFILE_FILE): stage1 $(wildcard $(PROFILE_DIR)/*.profraw) + $(LLVM_PROFDATA) merge -output=$@ $(PROFILE_DIR)/*.profraw # show top 50 functions top: $(PROFILE_FILE) From 99b8868ff563eded17376b3f90ffc1637222f2c6 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 9 Sep 2024 21:01:08 -0400 Subject: [PATCH 192/548] Fix `pkgdir` for extensions (#55720) Fixes https://github.com/JuliaLang/julia/issues/55719 --------- Co-authored-by: Max Horn <241512+fingolfin@users.noreply.github.com> --- base/loading.jl | 16 +++++++++++++++- test/loading.jl | 19 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index e0a2563dd0178..fe86a8c198461 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -508,6 +508,8 @@ package root. To get the root directory of the package that implements the current module the form `pkgdir(@__MODULE__)` can be used. +If an extension module is given, the root of the parent package is returned. + ```julia-repl julia> pkgdir(Foo) "/path/to/Foo.jl" @@ -525,7 +527,19 @@ function pkgdir(m::Module, paths::String...) rootmodule = moduleroot(m) path = pathof(rootmodule) path === nothing && return nothing - return joinpath(dirname(dirname(path)), paths...) + original = path + path, base = splitdir(dirname(path)) + if base == "src" + # package source in `../src/Foo.jl` + elseif base == "ext" + # extension source in `../ext/FooExt.jl` + elseif basename(path) == "ext" + # extension source in `../ext/FooExt/FooExt.jl` + path = dirname(path) + else + error("Unexpected path structure for module source: $original") + end + return joinpath(path, paths...) end function get_pkgversion_from_path(path) diff --git a/test/loading.jl b/test/loading.jl index fe6f800276547..8db8405ef2a83 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1034,6 +1034,16 @@ end end @testset "Extensions" begin + test_ext = """ + function test_ext(parent::Module, ext::Symbol) + _ext = Base.get_extension(parent, ext) + _ext isa Module || error("expected extension \$ext to be loaded") + _pkgdir = pkgdir(_ext) + _pkgdir == pkgdir(parent) != nothing || error("unexpected extension \$ext pkgdir path: \$_pkgdir") + _pkgversion = pkgversion(_ext) + _pkgversion == pkgversion(parent) || error("unexpected extension \$ext version: \$_pkgversion") + end + """ depot_path = mktempdir() try proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl") @@ -1044,6 +1054,7 @@ end cmd = """ $load_distr begin + $ew $test_ext $ew push!(empty!(DEPOT_PATH), $(repr(depot_path))) using HasExtensions $ew using HasExtensions @@ -1051,6 +1062,7 @@ end $ew HasExtensions.ext_loaded && error("ext_loaded set") using HasDepWithExtensions $ew using HasDepWithExtensions + $ew test_ext(HasExtensions, :Extension) $ew Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") $ew HasExtensions.ext_loaded || error("ext_loaded not set") $ew HasExtensions.ext_folder_loaded && error("ext_folder_loaded set") @@ -1102,13 +1114,14 @@ end test_ext_proj = """ begin + $test_ext using HasExtensions using ExtDep - Base.get_extension(HasExtensions, :Extension) isa Module || error("expected extension to load") + test_ext(HasExtensions, :Extension) using ExtDep2 - Base.get_extension(HasExtensions, :ExtensionFolder) isa Module || error("expected extension to load") + test_ext(HasExtensions, :ExtensionFolder) using ExtDep3 - Base.get_extension(HasExtensions, :ExtensionDep) isa Module || error("expected extension to load") + test_ext(HasExtensions, :ExtensionDep) end """ for compile in (`--compiled-modules=no`, ``) From 7a645dd23af4491d1d03f4f1cba5d46d8268677d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 10 Sep 2024 12:09:54 +0530 Subject: [PATCH 193/548] Avoid materializing arrays in bidiag matmul (#55450) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, small `Bidiagonal`/`Tridiagonal` matrices are materialized in matrix multiplications, but this is wasteful and unnecessary. This PR changes this to use a naive matrix multiplication for small matrices, and fall back to the banded multiplication for larger ones. Multiplication by a `Bidiagonal` falls back to a banded matrix multiplication for all sizes in the current implementation, and iterates in a cache-friendly manner for the non-`Bidiagonal` matrix. In certain cases, the matrices were being materialized if the non-structured matrix was small, even if the structured matrix was large. This is changed as well in this PR. Some improvements in performance: ```julia julia> B = Bidiagonal(rand(3), rand(2), :U); A = rand(size(B)...); C = similar(A); julia> @btime mul!($C, $A, $B); 193.152 ns (6 allocations: 352 bytes) # nightly v"1.12.0-DEV.1034" 18.826 ns (0 allocations: 0 bytes) # This PR julia> T = Tridiagonal(rand(99), rand(100), rand(99)); A = rand(2, size(T,2)); C = similar(A); julia> @btime mul!($C, $A, $T); 9.398 μs (8 allocations: 79.94 KiB) # nightly 416.407 ns (0 allocations: 0 bytes) # This PR julia> B = Bidiagonal(rand(300), rand(299), :U); A = rand(20000, size(B,2)); C = similar(A); julia> @btime mul!($C, $A, $B); 33.395 ms (0 allocations: 0 bytes) # nightly 6.695 ms (0 allocations: 0 bytes) # This PR (cache-friendly) ``` Closes https://github.com/JuliaLang/julia/pull/55414 --------- Co-authored-by: Daniel Karrasch --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 4 +- stdlib/LinearAlgebra/src/bidiag.jl | 330 +++++++++++++++++++--- stdlib/LinearAlgebra/test/bidiag.jl | 85 ++++-- stdlib/LinearAlgebra/test/tridiag.jl | 71 +++++ 4 files changed, 422 insertions(+), 68 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 27d4255fb656b..17216845b350c 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -673,7 +673,9 @@ matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) - Tridiagonal(similar(A, TS, n-1), similar(A, TS, n), similar(A, TS, n-1)) + ev = similar(A, TS, max(0, n-1)) + dv = similar(A, TS, n) + Tridiagonal(ev, dv, similar(ev)) end # Special handling for adj/trans vec diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index d86bad7e41435..8bc5b1c47f366 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -557,7 +557,8 @@ end # function to get the internally stored vectors for Bidiagonal and [Sym]Tridiagonal # to avoid allocations in _mul! below (#24324, #24578) _diag(A::Tridiagonal, k) = k == -1 ? A.dl : k == 0 ? A.d : A.du -_diag(A::SymTridiagonal, k) = k == 0 ? A.dv : A.ev +_diag(A::SymTridiagonal{<:Number}, k) = k == 0 ? A.dv : A.ev +_diag(A::SymTridiagonal, k) = k == 0 ? view(A, diagind(A, IndexStyle(A))) : view(A, diagind(A, 1, IndexStyle(A))) function _diag(A::Bidiagonal, k) if k == 0 return A.dv @@ -577,12 +578,45 @@ function _bibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + if n <= 3 + # naive multiplication + for I in CartesianIndices(C) + _modify!(_add, sum(A[I[1], k] * B[k, I[2]] for k in axes(A,2)), C, I) + end + return C + end # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the # off-diagonal elements for non-zero beta. _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C + @inbounds begin + # first column of C + C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2,1]) + C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) + C[3,1] += _add(A[3,2]*B[2,1]) + # second column of C + C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) + C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) + C[3,2] += _add(A[3,2]*B[2,2] + A[3,3]*B[3,2]) + C[4,2] += _add(A[4,3]*B[3,2]) + end # inbounds + # middle columns + __bibimul!(C, A, B, _add) + @inbounds begin + C[n-3,n-1] += _add(A[n-3,n-2]*B[n-2,n-1]) + C[n-2,n-1] += _add(A[n-2,n-2]*B[n-2,n-1] + A[n-2,n-1]*B[n-1,n-1]) + C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) + C[n, n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) + # last column of C + C[n-2, n] += _add(A[n-2,n-1]*B[n-1,n]) + C[n-1, n] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1,n]*B[n,n ]) + C[n, n] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) + end # inbounds + C +end +function __bibimul!(C, A, B, _add) + n = size(A,1) Al = _diag(A, -1) Ad = _diag(A, 0) Au = _diag(A, 1) @@ -590,44 +624,198 @@ function _bibimul!(C, A, B, _add) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin - # first row of C - C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2, 1]) - C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) - C[1,3] += _add(A[1,2]*B[2,3]) - # second row of C - C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) - C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) - C[2,3] += _add(A[2,2]*B[2,3] + A[2,3]*B[3,3]) - C[2,4] += _add(A[2,3]*B[3,4]) for j in 3:n-2 - Ajj₋1 = Al[j-1] - Ajj = Ad[j] + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] Ajj₊1 = Au[j] - Bj₋1j₋2 = Bl[j-2] - Bj₋1j₋1 = Bd[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] Bj₋1j = Bu[j-1] - Bjj₋1 = Bl[j-1] Bjj = Bd[j] - Bjj₊1 = Bu[j] Bj₊1j = Bl[j] - Bj₊1j₊1 = Bd[j+1] - Bj₊1j₊2 = Bu[j+1] - C[j,j-2] += _add( Ajj₋1*Bj₋1j₋2) - C[j, j-1] += _add(Ajj₋1*Bj₋1j₋1 + Ajj*Bjj₋1) - C[j, j ] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j, j+1] += _add(Ajj *Bjj₊1 + Ajj₊1*Bj₊1j₊1) - C[j, j+2] += _add(Ajj₊1*Bj₊1j₊2) + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) end - # row before last of C - C[n-1,n-3] += _add(A[n-1,n-2]*B[n-2,n-3]) - C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2] + A[n-1,n-2]*B[n-2,n-2]) - C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) - C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1, n]*B[n ,n ]) - # last row of C - C[n,n-2] += _add(A[n,n-1]*B[n-1,n-2]) - C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) - C[n,n ] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) - end # inbounds + end + C +end +function __bibimul!(C, A, B::Bidiagonal, _add) + n = size(A,1) + Al = _diag(A, -1) + Ad = _diag(A, 0) + Au = _diag(A, 1) + Bd = _diag(B, 0) + if B.uplo == 'U' + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj) + end + end + else # B.uplo == 'L' + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end + C +end +function __bibimul!(C, A::Bidiagonal, B, _add) + n = size(A,1) + Bl = _diag(B, -1) + Bd = _diag(B, 0) + Bu = _diag(B, 1) + Ad = _diag(A, 0) + if A.uplo == 'U' + Au = _diag(A, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) + end + end + else # A.uplo == 'L' + Al = _diag(A, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end + C +end +function __bibimul!(C, A::Bidiagonal, B::Bidiagonal, _add) + n = size(A,1) + Ad = _diag(A, 0) + Bd = _diag(B, 0) + if A.uplo == 'U' && B.uplo == 'U' + Au = _diag(A, 1) + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj) + end + end + elseif A.uplo == 'U' && B.uplo == 'L' + Au = _diag(A, 1) + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) + end + end + elseif A.uplo == 'L' && B.uplo == 'U' + Al = _diag(A, -1) + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj) + end + end + else # A.uplo == 'L' && B.uplo == 'L' + Al = _diag(A, -1) + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j, j] += _add(Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end C end @@ -744,7 +932,52 @@ function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulA nB = size(B,2) (iszero(nA) || iszero(nB)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + if nA <= 3 + # naive multiplication + for I in CartesianIndices(C) + col = Base.tail(Tuple(I)) + _modify!(_add, sum(A[I[1], k] * B[k, col...] for k in axes(A,2)), C, I) + end + return C + end + _mul_bitrisym!(C, A, B, _add) +end +function _mul_bitrisym!(C::AbstractVecOrMat, A::Bidiagonal, B::AbstractVecOrMat, _add::MulAddMul) + nA = size(A,1) + nB = size(B,2) + d = A.dv + if A.uplo == 'U' + u = A.ev + @inbounds begin + for j = 1:nB + b₀, b₊ = B[1, j], B[2, j] + _modify!(_add, d[1]*b₀ + u[1]*b₊, C, (1, j)) + for i = 2:nA - 1 + b₀, b₊ = b₊, B[i + 1, j] + _modify!(_add, d[i]*b₀ + u[i]*b₊, C, (i, j)) + end + _modify!(_add, d[nA]*b₊, C, (nA, j)) + end + end + else + l = A.ev + @inbounds begin + for j = 1:nB + b₀, b₊ = B[1, j], B[2, j] + _modify!(_add, d[1]*b₀, C, (1, j)) + for i = 2:nA - 1 + b₋, b₀, b₊ = b₀, b₊, B[i + 1, j] + _modify!(_add, l[i - 1]*b₋ + d[i]*b₀, C, (i, j)) + end + _modify!(_add, l[nA - 1]*b₀ + d[nA]*b₊, C, (nA, j)) + end + end + end + C +end +function _mul_bitrisym!(C::AbstractVecOrMat, A::TriSym, B::AbstractVecOrMat, _add::MulAddMul) + nA = size(A,1) + nB = size(B,2) l = _diag(A, -1) d = _diag(A, 0) u = _diag(A, 1) @@ -769,8 +1002,9 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) m = size(B,2) (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if n <= 3 || m <= 1 - return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + if m == 1 + B11 = B[1,1] + return mul!(C, A, B11, _add.alpha, _add.beta) end Bl = _diag(B, -1) Bd = _diag(B, 0) @@ -804,21 +1038,18 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAdd m, n = size(A) (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if size(A, 1) <= 3 || size(B, 2) <= 1 - return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) - end @inbounds if B.uplo == 'U' + for j in n:-1:2, i in 1:m + _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) + end for i in 1:m - for j in n:-1:2 - _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) - end _modify!(_add, A[i,1] * B.dv[1], C, (i, 1)) end else # uplo == 'L' + for j in 1:n-1, i in 1:m + _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) + end for i in 1:m - for j in 1:n-1 - _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) - end _modify!(_add, A[i,n] * B.dv[n], C, (i, n)) end end @@ -834,7 +1065,12 @@ function _dibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + if n <= 3 + for I in CartesianIndices(C) + _modify!(_add, A.diag[I[1]] * B[I[1], I[2]], C, I) + end + return C + end _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C Ad = A.diag diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index ef50658a642fb..58c228e39e226 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -1026,26 +1026,71 @@ end @test_throws "cannot set entry" B[1,2] = 4 end -@testset "mul with empty arrays" begin - A = zeros(5,0) - B = Bidiagonal(zeros(0), zeros(0), :U) - BL = Bidiagonal(zeros(5), zeros(4), :U) - @test size(A * B) == size(A) - @test size(BL * A) == size(A) - @test size(B * B) == size(B) - C = similar(A) - @test mul!(C, A, B) == A * B - @test mul!(C, BL, A) == BL * A - @test mul!(similar(B), B, B) == B * B - @test mul!(similar(B, size(B)), B, B) == B * B - - v = zeros(size(B,2)) - @test size(B * v) == size(v) - @test mul!(similar(v), B, v) == B * v - - D = Diagonal(zeros(size(B,2))) - @test size(B * D) == size(D * B) == size(D) - @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D +@testset "mul for small matrices" begin + @testset for n in 0:6 + D = Diagonal(rand(n)) + v = rand(n) + @testset for uplo in (:L, :U) + B = Bidiagonal(rand(n), rand(max(n-1,0)), uplo) + M = Matrix(B) + + @test B * v ≈ M * v + @test mul!(similar(v), B, v) ≈ M * v + @test mul!(ones(size(v)), B, v, 2, 3) ≈ M * v * 2 .+ 3 + + @test B * B ≈ M * M + @test mul!(similar(B, size(B)), B, B) ≈ M * M + @test mul!(ones(size(B)), B, B, 2, 4) ≈ M * M * 2 .+ 4 + + for m in 0:6 + AL = rand(m,n) + AR = rand(n,m) + @test AL * B ≈ AL * M + @test B * AR ≈ M * AR + @test mul!(similar(AL), AL, B) ≈ AL * M + @test mul!(similar(AR), B, AR) ≈ M * AR + @test mul!(ones(size(AL)), AL, B, 2, 4) ≈ AL * M * 2 .+ 4 + @test mul!(ones(size(AR)), B, AR, 2, 4) ≈ M * AR * 2 .+ 4 + end + + @test B * D ≈ M * D + @test D * B ≈ D * M + @test mul!(similar(B), B, D) ≈ M * D + @test mul!(similar(B), B, D) ≈ M * D + @test mul!(similar(B, size(B)), D, B) ≈ D * M + @test mul!(similar(B, size(B)), B, D) ≈ M * D + @test mul!(ones(size(B)), D, B, 2, 4) ≈ D * M * 2 .+ 4 + @test mul!(ones(size(B)), B, D, 2, 4) ≈ M * D * 2 .+ 4 + end + BL = Bidiagonal(rand(n), rand(max(0, n-1)), :L) + ML = Matrix(BL) + BU = Bidiagonal(rand(n), rand(max(0, n-1)), :U) + MU = Matrix(BU) + T = Tridiagonal(zeros(max(0, n-1)), zeros(n), zeros(max(0, n-1))) + @test mul!(T, BL, BU) ≈ ML * MU + @test mul!(T, BU, BL) ≈ MU * ML + T = Tridiagonal(ones(max(0, n-1)), ones(n), ones(max(0, n-1))) + @test mul!(copy(T), BL, BU, 2, 3) ≈ ML * MU * 2 + T * 3 + @test mul!(copy(T), BU, BL, 2, 3) ≈ MU * ML * 2 + T * 3 + end + + n = 4 + arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) + for B in ( + Bidiagonal(fill(arr,n), fill(arr,n-1), :L), + Bidiagonal(fill(arr,n), fill(arr,n-1), :U), + ) + @test B * B ≈ Matrix(B) * Matrix(B) + BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) + BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) + @test BL * B ≈ Matrix(BL) * Matrix(B) + @test BU * B ≈ Matrix(BU) * Matrix(B) + @test B * BL ≈ Matrix(B) * Matrix(BL) + @test B * BU ≈ Matrix(B) * Matrix(BU) + D = Diagonal(fill(arr,n)) + @test D * B ≈ Matrix(D) * Matrix(B) + @test B * D ≈ Matrix(B) * Matrix(D) + end end end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 3330fa682fe5e..15ac7f9f2147f 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -970,4 +970,75 @@ end @test sprint(show, S) == "SymTridiagonal($(repr(diag(S))), $(repr(diag(S,1))))" end +@testset "mul for small matrices" begin + @testset for n in 0:6 + for T in ( + Tridiagonal(rand(max(n-1,0)), rand(n), rand(max(n-1,0))), + SymTridiagonal(rand(n), rand(max(n-1,0))), + ) + M = Matrix(T) + @test T * T ≈ M * M + @test mul!(similar(T, size(T)), T, T) ≈ M * M + @test mul!(ones(size(T)), T, T, 2, 4) ≈ M * M * 2 .+ 4 + + for m in 0:6 + AR = rand(n,m) + AL = rand(m,n) + @test AL * T ≈ AL * M + @test T * AR ≈ M * AR + @test mul!(similar(AL), AL, T) ≈ AL * M + @test mul!(similar(AR), T, AR) ≈ M * AR + @test mul!(ones(size(AL)), AL, T, 2, 4) ≈ AL * M * 2 .+ 4 + @test mul!(ones(size(AR)), T, AR, 2, 4) ≈ M * AR * 2 .+ 4 + end + + v = rand(n) + @test T * v ≈ M * v + @test mul!(similar(v), T, v) ≈ M * v + + D = Diagonal(rand(n)) + @test T * D ≈ M * D + @test D * T ≈ D * M + @test mul!(Tridiagonal(similar(T)), D, T) ≈ D * M + @test mul!(Tridiagonal(similar(T)), T, D) ≈ M * D + @test mul!(similar(T, size(T)), D, T) ≈ D * M + @test mul!(similar(T, size(T)), T, D) ≈ M * D + @test mul!(ones(size(T)), D, T, 2, 4) ≈ D * M * 2 .+ 4 + @test mul!(ones(size(T)), T, D, 2, 4) ≈ M * D * 2 .+ 4 + + for uplo in (:U, :L) + B = Bidiagonal(rand(n), rand(max(0, n-1)), uplo) + @test T * B ≈ M * B + @test B * T ≈ B * M + if n <= 2 + @test mul!(Tridiagonal(similar(T)), B, T) ≈ B * M + @test mul!(Tridiagonal(similar(T)), T, B) ≈ M * B + end + @test mul!(similar(T, size(T)), B, T) ≈ B * M + @test mul!(similar(T, size(T)), T, B) ≈ M * B + @test mul!(ones(size(T)), B, T, 2, 4) ≈ B * M * 2 .+ 4 + @test mul!(ones(size(T)), T, B, 2, 4) ≈ M * B * 2 .+ 4 + end + end + end + + n = 4 + arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) + for T in ( + SymTridiagonal(fill(arr,n), fill(arr,n-1)), + Tridiagonal(fill(arr,n-1), fill(arr,n), fill(arr,n-1)), + ) + @test T * T ≈ Matrix(T) * Matrix(T) + BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) + BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) + @test BL * T ≈ Matrix(BL) * Matrix(T) + @test BU * T ≈ Matrix(BU) * Matrix(T) + @test T * BL ≈ Matrix(T) * Matrix(BL) + @test T * BU ≈ Matrix(T) * Matrix(BU) + D = Diagonal(fill(arr,n)) + @test D * T ≈ Matrix(D) * Matrix(T) + @test T * D ≈ Matrix(T) * Matrix(D) + end +end + end # module TestTridiagonal From d2807927656efe2bfbfe4402f83e2ea417306db2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 10 Sep 2024 07:33:56 -0400 Subject: [PATCH 194/548] Fix `@time_imports` extension recognition (#55718) --- base/loading.jl | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index fe86a8c198461..4e70d2bc257ea 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1203,7 +1203,7 @@ const TIMING_IMPORTS = Threads.Atomic{Int}(0) # these return either the array of modules loaded from the path / content given # or an Exception that describes why it couldn't be loaded # and it reconnects the Base.Docs.META -function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any}, ignore_native::Union{Nothing,Bool}=nothing) +function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any}, ignore_native::Union{Nothing,Bool}=nothing; register::Bool=true) if isnothing(ignore_native) if JLOptions().code_coverage == 0 && JLOptions().malloc_log == 0 ignore_native = false @@ -1252,13 +1252,14 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No for M in restored M = M::Module if parentmodule(M) === M && PkgId(M) == pkg + register && register_root_module(M) if timing_imports elapsed = round((time_ns() - t_before) / 1e6, digits = 1) comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before print(lpad(elapsed, 9), " ms ") - parentid = get(EXT_PRIMED, pkg, nothing) - if parentid !== nothing - print(parentid.name, " → ") + ext_parent = extension_parent_name(M) + if ext_parent !== nothing + print(ext_parent::String, " → ") end print(pkg.name) if comp_time > 0 @@ -1280,6 +1281,27 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No end end +# if M is an extension, return the string name of the parent. Otherwise return nothing +function extension_parent_name(M::Module) + rootmodule = moduleroot(M) + src_path = pathof(rootmodule) + src_path === nothing && return nothing + pkgdir_parts = splitpath(src_path) + ext_pos = findlast(==("ext"), pkgdir_parts) + if ext_pos !== nothing && ext_pos >= length(pkgdir_parts) - 2 + parent_package_root = joinpath(pkgdir_parts[1:ext_pos-1]...) + parent_package_project_file = locate_project_file(parent_package_root) + if parent_package_project_file isa String + d = parsed_toml(parent_package_project_file) + name = get(d, "name", nothing) + if name !== nothing + return name + end + end + end + return nothing +end + function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) # This function is also used by PkgCacheInspector.jl restored = sv[1]::Vector{Any} @@ -1461,7 +1483,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} triggers = triggers::Union{String, Vector{String}} triggers isa String && (triggers = [triggers]) id = PkgId(uuid5(parent.uuid::UUID, ext), ext) - if id in keys(EXT_PRIMED) || haskey(Base.loaded_modules, id) + if haskey(EXT_PRIMED, id) || haskey(Base.loaded_modules, id) continue # extension is already primed or loaded, don't add it again end EXT_PRIMED[id] = parent @@ -1890,8 +1912,7 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union depmods[i] = dep end # then load the file - loaded = _include_from_serialized(pkg, path, ocachepath, depmods, ignore_native) - loaded isa Module && register_root_module(loaded) + loaded = _include_from_serialized(pkg, path, ocachepath, depmods, ignore_native; register = true) return loaded end @@ -1958,8 +1979,7 @@ end if dep === nothing try set_pkgorigin_version_path(modkey, modpath) - dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps) - dep isa Module && stalecheck && register_root_module(dep) + dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) finally end_loading(modkey, dep) end @@ -1975,9 +1995,8 @@ end end restored = get(loaded_precompiles, pkg => newbuild_id, nothing) if !isa(restored, Module) - restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps) + restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps; register = stalecheck) end - isa(restored, Module) && stalecheck && register_root_module(restored) isa(restored, Module) && return restored @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored @label check_next_path From 3653b3898647d4c2528afde1f54bd3e65e3aa8ee Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 10 Sep 2024 12:51:55 -0400 Subject: [PATCH 195/548] drop typed GEP calls (#55708) Now that we use LLVM 18, and almost have LLVM 19 support, do cleanup to remove LLVM 15/16 type pointer support. LLVM now slightly prefers that we rewrite our complex GEP to use a simple emit_ptrgep call instead, which is also much simpler for julia to emit also. --- src/ccall.cpp | 11 +- src/cgutils.cpp | 126 ++++++-------------- src/codegen.cpp | 123 ++++++++----------- src/intrinsics.cpp | 4 +- src/llvm-alloc-opt.cpp | 21 +--- src/llvm-codegen-shared.h | 45 +++---- src/llvm-late-gc-lowering.cpp | 72 +---------- src/llvm-ptls.cpp | 4 +- test/llvmpasses/alloc-opt-gcframe.ll | 2 +- test/llvmpasses/late-lower-gc-addrspaces.ll | 56 ++++----- test/llvmpasses/late-lower-gc.ll | 70 +++++------ test/llvmpasses/names.jl | 3 +- 12 files changed, 187 insertions(+), 350 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 7ab8cfa974d6f..eac130ea43189 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1854,8 +1854,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) ctx.builder.SetInsertPoint(checkBB); auto signal_page_load = ctx.builder.CreateLoad( ctx.types().T_size, - ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, - get_current_signal_page_from_ptls(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const), -1), + emit_ptrgep(ctx, get_current_signal_page_from_ptls(ctx.builder, get_current_ptls(ctx), ctx.tbaa().tbaa_const), + -sizeof(size_t)), true); setName(ctx.emission_context, signal_page_load, "signal_page_load"); ctx.builder.CreateBr(contBB); @@ -1870,8 +1870,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) auto obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); // T_pprjlvalue // The inbounds gep makes it more clear to LLVM that the resulting value is not // a null pointer. - auto strp = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, obj, 1); - setName(ctx.emission_context, strp, "string_ptr"); + auto strp = emit_ptrgep(ctx, obj, ctx.types().sizeof_ptr, "string_ptr"); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } @@ -1882,9 +1881,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) auto obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); // T_pprjlvalue // The inbounds gep makes it more clear to LLVM that the resulting value is not // a null pointer. - auto strp = ctx.builder.CreateConstInBoundsGEP1_32( - ctx.types().T_prjlvalue, obj, (sizeof(jl_sym_t) + sizeof(void*) - 1) / sizeof(void*)); - setName(ctx.emission_context, strp, "symbol_name"); + auto strp = emit_ptrgep(ctx, obj, sizeof(jl_sym_t), "symbol_name"); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index bec84d9901279..2a234f399f5c1 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -130,14 +130,8 @@ static Value *stringConstPtr( } // Doesn't need to be aligned, we shouldn't operate on these like julia objects GlobalVariable *gv = get_pointer_to_constant(emission_context, Data, Align(1), "_j_str_" + StringRef(ctxt.data(), ctxt.size()), *M); - Value *zero = ConstantInt::get(Type::getInt32Ty(irbuilder.getContext()), 0); - Value *Args[] = { zero, zero }; - auto gep = irbuilder.CreateInBoundsGEP(gv->getValueType(), - // AddrSpaceCast in case globals are in non-0 AS - irbuilder.CreateAddrSpaceCast(gv, gv->getValueType()->getPointerTo(0)), - Args); - setName(emission_context, gep, "string_const_ptr"); - return gep; + // AddrSpaceCast in case globals are in non-0 AS + return irbuilder.CreateAddrSpaceCast(gv, gv->getValueType()->getPointerTo(0)); } @@ -621,12 +615,6 @@ static unsigned convert_struct_offset(jl_codectx_t &ctx, Type *lty, unsigned byt return convert_struct_offset(ctx.builder.GetInsertBlock()->getModule()->getDataLayout(), lty, byte_offset); } -static Value *emit_struct_gep(jl_codectx_t &ctx, Type *lty, Value *base, unsigned byte_offset) -{ - unsigned idx = convert_struct_offset(ctx, lty, byte_offset); - return ctx.builder.CreateConstInBoundsGEP2_32(lty, base, 0, idx); -} - static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl_value_t *jt, bool *isboxed, bool llvmcall=false); static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl_value_t *jt, bool *isboxed) @@ -1200,10 +1188,10 @@ static Value *emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull static Value *emit_datatype_types(jl_codectx_t &ctx, Value *dt) { Value *Ptr = decay_derived(ctx, dt); - Value *Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_t, types) / sizeof(void*)); + unsigned Idx = offsetof(jl_datatype_t, types); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); auto types = ai.decorateInst(ctx.builder.CreateAlignedLoad( - ctx.types().T_pjlvalue, ctx.builder.CreateInBoundsGEP(ctx.types().T_pjlvalue, Ptr, Idx), Align(sizeof(void*)))); + ctx.types().T_pjlvalue, emit_ptrgep(ctx, Ptr, Idx), Align(sizeof(void*)))); setName(ctx.emission_context, types, "datatype_types"); return types; } @@ -1222,16 +1210,13 @@ static Value *emit_datatype_size(jl_codectx_t &ctx, Value *dt, bool add_isunion= { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *Ptr = decay_derived(ctx, dt); - Value *Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_t, layout) / sizeof(int32_t*)); - Ptr = ctx.builder.CreateInBoundsGEP(getPointerTy(ctx.builder.getContext()), Ptr, Idx); + Ptr = emit_ptrgep(ctx, Ptr, offsetof(jl_datatype_t, layout)); Ptr = ai.decorateInst(ctx.builder.CreateAlignedLoad(getPointerTy(ctx.builder.getContext()), Ptr, Align(sizeof(int32_t*)))); - Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_layout_t, size) / sizeof(int32_t)); - Value *SizePtr = ctx.builder.CreateInBoundsGEP(getInt32Ty(ctx.builder.getContext()), Ptr, Idx); + Value *SizePtr = emit_ptrgep(ctx, Ptr, offsetof(jl_datatype_layout_t, size)); Value *Size = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt32Ty(ctx.builder.getContext()), SizePtr, Align(sizeof(int32_t)))); setName(ctx.emission_context, Size, "datatype_size"); if (add_isunion) { - Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_layout_t, flags) / sizeof(int8_t)); - Value *FlagPtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), Ptr, Idx); + Value *FlagPtr = emit_ptrgep(ctx, Ptr, offsetof(jl_datatype_layout_t, flags)); Value *Flag = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt16Ty(ctx.builder.getContext()), FlagPtr, Align(sizeof(int16_t)))); Flag = ctx.builder.CreateLShr(Flag, 4); Flag = ctx.builder.CreateAnd(Flag, ConstantInt::get(Flag->getType(), 1)); @@ -1308,7 +1293,7 @@ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) static Value *emit_datatype_isprimitivetype(jl_codectx_t &ctx, Value *typ) { Value *isprimitive; - isprimitive = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), decay_derived(ctx, typ), offsetof(jl_datatype_t, hash) + sizeof(((jl_datatype_t*)nullptr)->hash)); + isprimitive = emit_ptrgep(ctx, decay_derived(ctx, typ), offsetof(jl_datatype_t, hash) + sizeof(((jl_datatype_t*)nullptr)->hash)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); isprimitive = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), isprimitive, Align(1))); isprimitive = ctx.builder.CreateLShr(isprimitive, 7); @@ -1320,10 +1305,7 @@ static Value *emit_datatype_isprimitivetype(jl_codectx_t &ctx, Value *typ) static Value *emit_datatype_name(jl_codectx_t &ctx, Value *dt) { unsigned n = offsetof(jl_datatype_t, name) / sizeof(char*); - Value *vptr = ctx.builder.CreateInBoundsGEP( - ctx.types().T_pjlvalue, - maybe_decay_tracked(ctx, dt), - ConstantInt::get(ctx.types().T_size, n)); + Value *vptr = emit_ptrgep(ctx, maybe_decay_tracked(ctx, dt), n * sizeof(jl_value_t*)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); auto name = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, vptr, Align(sizeof(void*)))); setName(ctx.emission_context, name, "datatype_name"); @@ -1522,7 +1504,7 @@ static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull, bool just // we lied a bit: this wasn't really an object (though it was valid for GC rooting) // and we need to use it as an index to get the real object now Module *M = jl_Module; - Value *smallp = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), prepare_global_in(M, jl_small_typeof_var), tag); + Value *smallp = emit_ptrgep(ctx, prepare_global_in(M, jl_small_typeof_var), tag); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); auto small = ctx.builder.CreateAlignedLoad(typetag->getType(), smallp, M->getDataLayout().getPointerABIAlignment(0)); small->setMetadata(LLVMContext::MD_nonnull, MDNode::get(M->getContext(), None)); @@ -1802,7 +1784,7 @@ static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *t static Value *emit_isconcrete(jl_codectx_t &ctx, Value *typ) { Value *isconcrete; - isconcrete = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), decay_derived(ctx, typ), offsetof(jl_datatype_t, hash) + sizeof(((jl_datatype_t*)nullptr)->hash)); + isconcrete = emit_ptrgep(ctx, decay_derived(ctx, typ), offsetof(jl_datatype_t, hash) + sizeof(((jl_datatype_t*)nullptr)->hash)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); isconcrete = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), isconcrete, Align(1))); isconcrete = ctx.builder.CreateLShr(isconcrete, 1); @@ -2848,36 +2830,14 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st if (strct.ispointer()) { auto tbaa = best_field_tbaa(ctx, strct, jt, idx, byte_offset); Value *staddr = data_pointer(ctx, strct); - bool isboxed; - Type *lt = julia_type_to_llvm(ctx, (jl_value_t*)jt, &isboxed); Value *addr; - if (isboxed) { - // byte_offset == 0 is an important special case here, e.g. - // for single field wrapper types. Introducing the bitcast - // can pessimize mem2reg - if (byte_offset > 0) { - addr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), - staddr, - ConstantInt::get(ctx.types().T_size, byte_offset)); - } - else { - addr = staddr; - } - } - else { - if (jl_is_vecelement_type((jl_value_t*)jt)) - addr = staddr; // VecElement types are unwrapped in LLVM. - else if (isa(lt)) - addr = emit_struct_gep(ctx, lt, staddr, byte_offset); - else - addr = ctx.builder.CreateConstInBoundsGEP2_32(lt, staddr, 0, idx); - if (addr != staddr) { - setNameWithField(ctx.emission_context, addr, get_objname, jt, idx, Twine("_ptr")); - } - } - if (jl_field_isptr(jt, idx)) { + if (jl_is_vecelement_type((jl_value_t*)jt) || byte_offset == 0) + addr = staddr; // VecElement types are unwrapped in LLVM. + else + addr = emit_ptrgep(ctx, staddr, byte_offset); + if (addr != staddr) setNameWithField(ctx.emission_context, addr, get_objname, jt, idx, Twine("_ptr")); + if (jl_field_isptr(jt, idx)) { LoadInst *Load = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, addr, Align(sizeof(void*))); setNameWithField(ctx.emission_context, Load, get_objname, jt, idx, Twine()); Load->setOrdering(order <= jl_memory_order_notatomic ? AtomicOrdering::Unordered : get_llvm_atomic_order(order)); @@ -2894,14 +2854,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st bool isptr = (union_max == 0); assert(!isptr && fsz < jl_field_size(jt, idx)); (void)isptr; size_t fsz1 = jl_field_size(jt, idx) - 1; - Value *ptindex; - if (isboxed) { - ptindex = ctx.builder.CreateConstInBoundsGEP1_32( - getInt8Ty(ctx.builder.getContext()), staddr, byte_offset + fsz1); - } - else { - ptindex = emit_struct_gep(ctx, cast(lt), staddr, byte_offset + fsz1); - } + Value *ptindex = emit_ptrgep(ctx, staddr, byte_offset + fsz1); 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()); @@ -2957,15 +2910,15 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st for (; i < fsz / align; i++) { unsigned fld = st_idx + i; Value *fldv = ctx.builder.CreateExtractValue(obj, ArrayRef(fld)); - Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + Value *fldp = emit_ptrgep(ctx, lv, i * align); ctx.builder.CreateAlignedStore(fldv, fldp, Align(align)); } // emit remaining bytes up to tindex if (i < ptindex - st_idx) { - Value *staddr = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + Value *staddr = emit_ptrgep(ctx, lv, i * align); for (; i < ptindex - st_idx; i++) { Value *fldv = ctx.builder.CreateExtractValue(obj, ArrayRef(st_idx + i)); - Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), staddr, i); + Value *fldp = emit_ptrgep(ctx, staddr, i); ctx.builder.CreateAlignedStore(fldv, fldp, Align(1)); } } @@ -3105,7 +3058,7 @@ static Value *emit_genericmemoryowner(jl_codectx_t &ctx, Value *t) LI->setMetadata(LLVMContext::MD_nonnull, MDNode::get(ctx.builder.getContext(), None)); jl_aliasinfo_t aliasinfo_mem = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memoryown); aliasinfo_mem.decorateInst(LI); - addr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, m, JL_SMALL_BYTE_ALIGNMENT / sizeof(void*)); + addr = emit_ptrgep(ctx, m, JL_SMALL_BYTE_ALIGNMENT); Value *foreign = ctx.builder.CreateICmpNE(addr, decay_derived(ctx, LI)); return emit_guarded_test(ctx, foreign, t, [&] { addr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_jlgenericmemory, m, 1); @@ -3867,19 +3820,14 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx, auto tbaa = best_field_tbaa(ctx, strct, sty, idx0, byte_offset); Value *addr = data_pointer(ctx, strct); if (byte_offset > 0) { - addr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), - addr, - ConstantInt::get(ctx.types().T_size, byte_offset)); + addr = emit_ptrgep(ctx, addr, byte_offset); setNameWithField(ctx.emission_context, addr, get_objname, sty, idx0, Twine("_ptr")); } jl_value_t *jfty = jl_field_type(sty, idx0); bool isboxed = jl_field_isptr(sty, idx0); if (!isboxed && jl_is_uniontype(jfty)) { size_t fsz1 = jl_field_size(sty, idx0) - 1; - Value *ptindex = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), - addr, - ConstantInt::get(ctx.types().T_size, fsz1)); + Value *ptindex = emit_ptrgep(ctx, addr, fsz1); setNameWithField(ctx.emission_context, ptindex, get_objname, sty, idx0, Twine(".tindex_ptr")); return union_store(ctx, addr, ptindex, rhs, cmp, jfty, tbaa, ctx.tbaa().tbaa_unionselbyte, Order, FailOrder, @@ -3971,8 +3919,8 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (!init_as_value) { // avoid unboxing the argument explicitly // and use memcpy instead - Instruction *inst; - dest = inst = cast(ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx)); + Instruction *inst = cast(emit_ptrgep(ctx, strct, offs)); + dest = inst; // Our promotion point needs to come before // A) All of our arguments' promotion points // B) Any instructions we insert at any of our arguments' promotion points @@ -4025,16 +3973,16 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // emit all of the align-sized words unsigned i = 0; for (; i < fsz1 / al; i++) { - Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + Value *fldp = emit_ptrgep(ctx, lv, i * al); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); Value *fldv = ai.decorateInst(ctx.builder.CreateAlignedLoad(ET, fldp, Align(al))); strct = ctx.builder.CreateInsertValue(strct, fldv, ArrayRef(llvm_idx + i)); } // emit remaining bytes up to tindex if (i < ptindex - llvm_idx) { - Value *staddr = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + Value *staddr = emit_ptrgep(ctx, lv, i * al); for (; i < ptindex - llvm_idx; i++) { - Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), staddr, i); + Value *fldp = emit_ptrgep(ctx, staddr, i); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); Value *fldv = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), fldp, Align(1))); strct = ctx.builder.CreateInsertValue(strct, fldv, ArrayRef(llvm_idx + i)); @@ -4047,7 +3995,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg fval = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(llvm_idx)); } else { - Value *ptindex = emit_struct_gep(ctx, lt, strct, offs + fsz1); + Value *ptindex = emit_ptrgep(ctx, strct, offs + fsz1); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); ai.decorateInst(ctx.builder.CreateAlignedStore(tindex, ptindex, Align(1))); if (!rhs_union.isghost) @@ -4083,14 +4031,15 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { unsigned offs = jl_field_offset(sty, i); int fsz = jl_field_size(sty, i) - 1; - unsigned llvm_idx = convert_struct_offset(ctx, cast(lt), offs + fsz); - if (init_as_value) + if (init_as_value) { + unsigned llvm_idx = convert_struct_offset(ctx, cast(lt), offs + fsz); strct = ctx.builder.CreateInsertValue(strct, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), ArrayRef(llvm_idx)); + } else { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); ai.decorateInst(ctx.builder.CreateAlignedStore( ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), - ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx), + emit_ptrgep(ctx, strct, offs + fsz), Align(1))); } } @@ -4126,8 +4075,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); ai.decorateInst(ctx.builder.CreateAlignedStore( ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), - ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), strct, - ConstantInt::get(ctx.types().T_size, jl_field_offset(sty, i) + jl_field_size(sty, i) - 1)), + emit_ptrgep(ctx, strct, jl_field_offset(sty, i) + jl_field_size(sty, i) - 1), Align(1))); } } @@ -4169,9 +4117,7 @@ static Value *emit_defer_signal(jl_codectx_t &ctx) { ++EmittedDeferSignal; Value *ptls = get_current_ptls(ctx); - Constant *offset = ConstantInt::getSigned(getInt32Ty(ctx.builder.getContext()), - offsetof(jl_tls_states_t, defer_signal) / sizeof(sig_atomic_t)); - return ctx.builder.CreateInBoundsGEP(ctx.types().T_sigatomic, ptls, ArrayRef(offset), "jl_defer_signal"); + return emit_ptrgep(ctx, ptls, offsetof(jl_tls_states_t, defer_signal)); } #ifndef JL_NDEBUG diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f80791f2882d..9184e4895ab6d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2161,6 +2161,20 @@ static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) return cast(local); } +static Value *emit_ptrgep(jl_codectx_t &ctx, Value *base, size_t byte_offset, const Twine &Name="") +{ + auto *gep = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), base, byte_offset); + setName(ctx.emission_context, gep, Name); + return gep; +} + +static Value *emit_ptrgep(jl_codectx_t &ctx, Value *base, Value *byte_offset, const Twine &Name="") +{ + auto *gep = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), base, byte_offset, Name); + setName(ctx.emission_context, gep, Name); + return gep; +} + // --- convenience functions for tagging llvm values with julia types --- @@ -2211,7 +2225,7 @@ static void undef_derived_strct(jl_codectx_t &ctx, Value *ptr, jl_datatype_t *st size_t i, np = sty->layout->npointers; auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx.builder.getContext()); for (i = 0; i < np; i++) { - Value *fld = ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue, ptr, jl_ptr_offset(sty, i)); + Value *fld = emit_ptrgep(ctx, ptr, jl_ptr_offset(sty, i) * sizeof(jl_value_t*)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); ai.decorateInst(ctx.builder.CreateStore(Constant::getNullValue(T_prjlvalue), fld)); } @@ -3542,8 +3556,6 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a return ctx.builder.CreateICmpEQ(answer, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)); } else if (sz > 512 && jl_struct_try_layout(sty) && sty->layout->flags.isbitsegal) { - Type *TInt8 = getInt8Ty(ctx.builder.getContext()); - Type *TInt1 = getInt1Ty(ctx.builder.getContext()); Value *varg1 = arg1.ispointer() ? data_pointer(ctx, arg1) : value_to_pointer(ctx, arg1).V; Value *varg2 = arg2.ispointer() ? data_pointer(ctx, arg2) : @@ -3562,8 +3574,8 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a Value *ptr1 = varg1; Value *ptr2 = varg2; if (desc.offset != 0) { - ptr1 = ctx.builder.CreateConstInBoundsGEP1_32(TInt8, ptr1, desc.offset); - ptr2 = ctx.builder.CreateConstInBoundsGEP1_32(TInt8, ptr2, desc.offset); + ptr1 = emit_ptrgep(ctx, ptr1, desc.offset); + ptr2 = emit_ptrgep(ctx, ptr2, desc.offset); } Value *new_ptr1 = ptr1; @@ -3573,7 +3585,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a PHINode *answerphi = nullptr; if (desc.nrepeats != 1) { // Set up loop - endptr1 = ctx.builder.CreateConstInBoundsGEP1_32(TInt8, ptr1, desc.nrepeats * (desc.data_bytes + desc.padding_bytes));; + endptr1 = emit_ptrgep(ctx, ptr1, desc.nrepeats * (desc.data_bytes + desc.padding_bytes));; BasicBlock *currBB = ctx.builder.GetInsertBlock(); loopBB = BasicBlock::Create(ctx.builder.getContext(), "egal_loop", ctx.f); @@ -3581,6 +3593,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a ctx.builder.CreateBr(loopBB); ctx.builder.SetInsertPoint(loopBB); + Type *TInt1 = getInt1Ty(ctx.builder.getContext()); answerphi = ctx.builder.CreatePHI(TInt1, 2); answerphi->addIncoming(answer ? answer : ConstantInt::get(TInt1, 1), currBB); answer = answerphi; @@ -3588,11 +3601,11 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a PHINode *itr1 = ctx.builder.CreatePHI(ptr1->getType(), 2); PHINode *itr2 = ctx.builder.CreatePHI(ptr2->getType(), 2); - new_ptr1 = ctx.builder.CreateConstInBoundsGEP1_32(TInt8, itr1, desc.data_bytes + desc.padding_bytes); + new_ptr1 = emit_ptrgep(ctx, itr1, desc.data_bytes + desc.padding_bytes); itr1->addIncoming(ptr1, currBB); itr1->addIncoming(new_ptr1, loopBB); - Value *new_ptr2 = ctx.builder.CreateConstInBoundsGEP1_32(TInt8, itr2, desc.data_bytes + desc.padding_bytes); + Value *new_ptr2 = emit_ptrgep(ctx, itr2, desc.data_bytes + desc.padding_bytes); itr2->addIncoming(ptr2, currBB); itr2->addIncoming(new_ptr2, loopBB); @@ -4074,7 +4087,7 @@ static bool emit_f_opmemory(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ptindex = ctx.builder.CreateInBoundsGEP(AT, data, mlen); data = ctx.builder.CreateInBoundsGEP(AT, data, idx0); } - ptindex = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), ptindex, idx0); + ptindex = emit_ptrgep(ctx, ptindex, idx0); *ret = union_store(ctx, data, ptindex, val, cmp, ety, ctx.tbaa().tbaa_arraybuf, ctx.tbaa().tbaa_arrayselbyte, Order, FailOrder, @@ -4089,7 +4102,7 @@ static bool emit_f_opmemory(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, assert(ptr); lock = ptr; // ptr += sizeof(lock); - ptr = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), ptr, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); + ptr = emit_ptrgep(ctx, ptr, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); } Value *data_owner = NULL; // owner object against which the write barrier must check if (isboxed || layout->first_ptr >= 0) { // if elements are just bits, don't need a write barrier @@ -4204,7 +4217,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, #ifdef _P64 nva = ctx.builder.CreateTrunc(nva, getInt32Ty(ctx.builder.getContext())); #endif - Value *theArgs = ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(ctx.types().T_size, ctx.nReqArgs)); + Value *theArgs = emit_ptrgep(ctx, ctx.argArray, ctx.nReqArgs * sizeof(jl_value_t*)); Value *r = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { theF, theArgs, nva }); *ret = mark_julia_type(ctx, r, true, jl_any_type); return true; @@ -4354,7 +4367,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ptindex = ctx.builder.CreateInBoundsGEP(AT, data, mlen); data = ctx.builder.CreateInBoundsGEP(AT, data, idx0); } - ptindex = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), ptindex, idx0); + ptindex = emit_ptrgep(ctx, ptindex, idx0); size_t elsz_c = 0, al_c = 0; int union_max = jl_islayout_inline(ety, &elsz_c, &al_c); assert(union_max && LLT_ALIGN(elsz_c, al_c) == elsz && al_c == al); @@ -4367,7 +4380,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, assert(ptr); lock = ptr; // ptr += sizeof(lock); - ptr = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), ptr, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); + ptr = emit_ptrgep(ctx, ptr, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); emit_lockstate_value(ctx, lock, true); } *ret = typed_load(ctx, ptr, nullptr, ety, @@ -4458,10 +4471,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (needlock) { // n.b. no actual lock acquire needed, as the check itself only needs to load a single pointer and check for null // elem += sizeof(lock); - elem = ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), elem, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); + elem = emit_ptrgep(ctx, elem, LLT_ALIGN(sizeof(jl_mutex_t), JL_SMALL_BYTE_ALIGNMENT)); } if (!isboxed) - elem = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, elem, layout->first_ptr); + elem = emit_ptrgep(ctx, elem, layout->first_ptr * sizeof(void*)); // emit this using the same type as jl_builtin_memoryrefget // so that LLVM may be able to load-load forward them and fold the result auto tbaa = isboxed ? ctx.tbaa().tbaa_ptrarraybuf : ctx.tbaa().tbaa_arraybuf; @@ -4549,7 +4562,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (load->getPointerOperand() == ctx.slots[ctx.vaSlot].boxroot && ctx.argArray) { Value *valen = emit_n_varargs(ctx); jl_cgval_t va_ary( // fake instantiation of a cgval, in order to call emit_bounds_check (it only checks the `.V` field) - ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(ctx.types().T_size, ctx.nReqArgs)), + emit_ptrgep(ctx, ctx.argArray, ctx.nReqArgs * sizeof(jl_value_t*)), NULL, NULL); Value *idx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen, boundscheck); @@ -4899,7 +4912,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (!jl_field_isptr(stt, fieldidx)) offs += ((jl_datatype_t*)jl_field_type(stt, fieldidx))->layout->first_ptr; Value *ptr = data_pointer(ctx, obj); - Value *addr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, ptr, offs); + Value *addr = emit_ptrgep(ctx, ptr, offs * sizeof(jl_value_t*)); // emit this using the same type as emit_getfield_knownidx // so that LLVM may be able to load-load forward them and fold the result jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); @@ -5583,10 +5596,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) } } assert(ctx.spvals_ptr != NULL); - Value *bp = ctx.builder.CreateConstInBoundsGEP1_32( - ctx.types().T_prjlvalue, - ctx.spvals_ptr, - i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); + Value *bp = emit_ptrgep(ctx, ctx.spvals_ptr, i * sizeof(jl_value_t*) + sizeof(jl_svec_t)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *sp = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); setName(ctx.emission_context, sp, "sparam"); @@ -5639,10 +5649,7 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym, int allow_i } } assert(ctx.spvals_ptr != NULL); - Value *bp = ctx.builder.CreateConstInBoundsGEP1_32( - ctx.types().T_prjlvalue, - ctx.spvals_ptr, - i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); + Value *bp = emit_ptrgep(ctx, ctx.spvals_ptr, i * sizeof(jl_value_t*) + sizeof(jl_svec_t)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *sp = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false, true), emit_tagfrom(ctx, jl_tvar_type)); @@ -6753,34 +6760,26 @@ static void allocate_gc_frame(jl_codectx_t &ctx, BasicBlock *b0, bool or_new=fal static Value *get_current_task(jl_codectx_t &ctx) { - return get_current_task_from_pgcstack(ctx.builder, ctx.types().T_size, ctx.pgcstack); + return get_current_task_from_pgcstack(ctx.builder, ctx.pgcstack); } // Get PTLS through current task. static Value *get_current_ptls(jl_codectx_t &ctx) { - return get_current_ptls_from_task(ctx.builder, ctx.types().T_size, get_current_task(ctx), ctx.tbaa().tbaa_gcframe); + return get_current_ptls_from_task(ctx.builder, get_current_task(ctx), ctx.tbaa().tbaa_gcframe); } // Get the address of the world age of the current task static Value *get_tls_world_age_field(jl_codectx_t &ctx) { Value *ct = get_current_task(ctx); - return ctx.builder.CreateInBoundsGEP( - ctx.types().T_size, - ct, - ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, world_age) / ctx.types().sizeof_ptr), - "world_age"); + return emit_ptrgep(ctx, ct, offsetof(jl_task_t, world_age), "world_age"); } static Value *get_scope_field(jl_codectx_t &ctx) { Value *ct = get_current_task(ctx); - return ctx.builder.CreateInBoundsGEP( - ctx.types().T_prjlvalue, - ct, - ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, scope) / ctx.types().sizeof_ptr), - "current_scope"); + return emit_ptrgep(ctx, ct, offsetof(jl_task_t, scope), "current_scope"); } static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, Module *M, jl_codegen_params_t ¶ms) @@ -6912,10 +6911,7 @@ static void emit_cfunc_invalidate( case jl_returninfo_t::SRet: { if (return_roots) { Value *root1 = gf_thunk->arg_begin() + 1; // root1 has type [n x {}*]* - #if JL_LLVM_VERSION < 170000 - assert(cast(root1->getType())->isOpaqueOrPointeeTypeMatches(get_returnroots_type(ctx, return_roots))); - #endif - root1 = ctx.builder.CreateConstInBoundsGEP2_32(get_returnroots_type(ctx, return_roots), root1, 0, 0); + // store the whole object in the first slot ctx.builder.CreateStore(gf_ret, root1); } Align alignment(julia_alignment(rettype)); @@ -7094,10 +7090,7 @@ static Function* gen_cfun_wrapper( if (calltype) { LoadInst *lam_max = ctx.builder.CreateAlignedLoad( ctx.types().T_size, - ctx.builder.CreateConstInBoundsGEP1_32( - ctx.types().T_size, - literal_pointer_val(ctx, (jl_value_t*)codeinst), - offsetof(jl_code_instance_t, max_world) / ctx.types().sizeof_ptr), + emit_ptrgep(ctx, literal_pointer_val(ctx, (jl_value_t*)codeinst), offsetof(jl_code_instance_t, max_world)), ctx.types().alignof_ptr); age_ok = ctx.builder.CreateICmpUGE(lam_max, world_v); } @@ -7178,7 +7171,7 @@ static Function* gen_cfun_wrapper( *closure_types = jl_alloc_vec_any(0); jl_array_ptr_1d_push(*closure_types, jargty); Value *runtime_dt = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, - ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, nestPtr, jl_array_nrows(*closure_types)), + emit_ptrgep(ctx, nestPtr, jl_array_nrows(*closure_types) * ctx.types().sizeof_ptr), Align(sizeof(void*))); BasicBlock *boxedBB = BasicBlock::Create(ctx.builder.getContext(), "isboxed", cw); BasicBlock *loadBB = BasicBlock::Create(ctx.builder.getContext(), "need-load", cw); @@ -7244,7 +7237,7 @@ static Function* gen_cfun_wrapper( *closure_types = jl_alloc_vec_any(0); jl_array_ptr_1d_push(*closure_types, jargty); Value *runtime_dt = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, - ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, nestPtr, jl_array_nrows(*closure_types)), + emit_ptrgep(ctx, nestPtr, jl_array_nrows(*closure_types) * ctx.types().sizeof_ptr), Align(sizeof(void*))); Value *strct = box_ccall_result(ctx, val, runtime_dt, jargty); inputarg = mark_julia_type(ctx, strct, true, jargty_proper); @@ -7823,7 +7816,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret theArg = funcArg; } else { - Value *argPtr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, argArray, i - 1); + Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); theArg = ai.decorateInst(maybe_mark_load_dereferenceable( ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), @@ -7850,7 +7843,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret theArg = funcArg; else theArg = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, - ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, argArray, retarg - 1), + emit_ptrgep(ctx, argArray, (retarg - 1) * ctx.types().sizeof_ptr), Align(sizeof(void*))); retval = mark_julia_type(ctx, theArg, true, jl_any_type); } @@ -7969,7 +7962,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::NoUndef); attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param)); - fsig.push_back(get_returnroots_type(ctx, props.return_roots)->getPointerTo(0)); + fsig.push_back(ctx.types().T_ptr); argnames.push_back("return_roots"); } @@ -8069,9 +8062,9 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value return props; } -static void emit_sret_roots(jl_codectx_t &ctx, bool isptr, Value *Src, Type *T, Value *Shadow, Type *ShadowT, unsigned count) +static void emit_sret_roots(jl_codectx_t &ctx, bool isptr, Value *Src, Type *T, Value *Shadow, unsigned count) { - unsigned emitted = TrackWithShadow(Src, T, isptr, Shadow, ShadowT, ctx.builder); //This comes from Late-GC-Lowering?? + unsigned emitted = TrackWithShadow(Src, T, isptr, Shadow, ctx.builder); //This comes from Late-GC-Lowering?? assert(emitted == count); (void)emitted; (void)count; } @@ -8773,9 +8766,7 @@ static jl_llvm_functions_t // Load closure world Value *oc_this = decay_derived(ctx, &*AI++); Value *argaddr = oc_this; - Value *worldaddr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, world))); + Value *worldaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, world)); jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); @@ -8783,9 +8774,7 @@ static jl_llvm_functions_t emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); // Load closure env - Value *envaddr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, captures))); + Value *envaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, captures)); jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, nullptr, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); @@ -8800,7 +8789,7 @@ static jl_llvm_functions_t theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); } else { - Value *argPtr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, argArray, i - 1); + Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *load = ai.decorateInst(maybe_mark_load_dereferenceable( ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), @@ -8876,10 +8865,8 @@ static jl_llvm_functions_t restTuple = ctx.builder.CreateCall(F, { Constant::getNullValue(ctx.types().T_prjlvalue), - ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, argArray, - ConstantInt::get(ctx.types().T_size, nreq - 1)), - ctx.builder.CreateSub(argCount, - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), nreq - 1)) }); + emit_ptrgep(ctx, argArray, (nreq - 1) * sizeof(jl_value_t*)), + ctx.builder.CreateSub(argCount, ctx.builder.getInt32(nreq - 1)) }); restTuple->setAttributes(F->getAttributes()); ctx.builder.CreateStore(restTuple, vi.boxroot); } @@ -9319,7 +9306,7 @@ static jl_llvm_functions_t if (retvalinfo.ispointer()) { if (returninfo.return_roots) { Type *store_ty = julia_type_to_llvm(ctx, retvalinfo.typ); - emit_sret_roots(ctx, true, data_pointer(ctx, retvalinfo), store_ty, f->arg_begin() + 1, get_returnroots_type(ctx, returninfo.return_roots), returninfo.return_roots); + emit_sret_roots(ctx, true, data_pointer(ctx, retvalinfo), store_ty, f->arg_begin() + 1, returninfo.return_roots); } if (returninfo.cc == jl_returninfo_t::SRet) { assert(jl_is_concrete_type(jlrettype)); @@ -9336,7 +9323,7 @@ static jl_llvm_functions_t Value *Val = retvalinfo.V; if (returninfo.return_roots) { assert(julia_type_to_llvm(ctx, retvalinfo.typ) == store_ty); - emit_sret_roots(ctx, false, Val, store_ty, f->arg_begin() + 1, get_returnroots_type(ctx, returninfo.return_roots), returninfo.return_roots); + emit_sret_roots(ctx, false, Val, store_ty, f->arg_begin() + 1, returninfo.return_roots); } ctx.builder.CreateAlignedStore(Val, sret, Align(julia_alignment(retvalinfo.typ))); assert(retvalinfo.TIndex == NULL && "unreachable"); // unimplemented representation @@ -9447,11 +9434,7 @@ static jl_llvm_functions_t ctx.builder.CreateBr(handlr); } ctx.builder.SetInsertPoint(tryblk); - auto ehptr = ctx.builder.CreateInBoundsGEP( - ctx.types().T_ptr, - ct, - ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, eh) / ctx.types().sizeof_ptr), - "eh"); + auto ehptr = emit_ptrgep(ctx, ct, offsetof(jl_task_t, eh)); ctx.builder.CreateAlignedStore(ehbuf, ehptr, ctx.types().alignof_ptr); } } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 4bfe3f184d24b..194b45886bb0d 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -767,7 +767,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, ArrayRef argv) LLT_ALIGN(size, jl_datatype_align(ety)))); setName(ctx.emission_context, im1, "pointerref_offset"); Value *thePtr = emit_unbox(ctx, getPointerTy(ctx.builder.getContext()), e, e.typ); - thePtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); + thePtr = emit_ptrgep(ctx, thePtr, im1); setName(ctx.emission_context, thePtr, "pointerref_src"); MDNode *tbaa = best_tbaa(ctx.tbaa(), ety); emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, Align(sizeof(jl_value_t*)), Align(align_nb)); @@ -848,7 +848,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); setName(ctx.emission_context, im1, "pointerset_offset"); - auto gep = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); + auto gep = emit_ptrgep(ctx, thePtr, im1); setName(ctx.emission_context, gep, "pointerset_ptr"); emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, Align(align_nb), Align(julia_alignment(ety))); } diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 5984ad55d221c..188955fd50972 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -770,26 +770,7 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocF user->replaceUsesOfWith(orig_i, replace); } else if (isa(user) || isa(user)) { - #if JL_LLVM_VERSION >= 170000 - #ifndef JL_NDEBUG - auto cast_t = PointerType::get(user->getType(), new_i->getType()->getPointerAddressSpace()); - Type *new_t = new_i->getType(); - assert(cast_t == new_t); - #endif - auto replace_i = new_i; - #else - auto cast_t = PointerType::getWithSamePointeeType(cast(user->getType()), new_i->getType()->getPointerAddressSpace()); - auto replace_i = new_i; - Type *new_t = new_i->getType(); - if (cast_t != new_t) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(cast_t->getContext().supportsTypedPointers()); - replace_i = new BitCastInst(replace_i, cast_t, "", user); - replace_i->setDebugLoc(user->getDebugLoc()); - replace_i->takeName(user); - } - #endif - push_frame(user, replace_i); + push_frame(user, new_i); } else if (auto gep = dyn_cast(user)) { SmallVector IdxOperands(gep->idx_begin(), gep->idx_end()); diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index 242dab021f101..956c04dbc7ded 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -125,7 +125,7 @@ struct CountTrackedPointers { CountTrackedPointers(llvm::Type *T, bool ignore_loaded=false); }; -unsigned TrackWithShadow(llvm::Value *Src, llvm::Type *T, bool isptr, llvm::Value *Dst, llvm::Type *DTy, llvm::IRBuilder<> &irbuilder); +unsigned TrackWithShadow(llvm::Value *Src, llvm::Type *T, bool isptr, llvm::Value *Dst, llvm::IRBuilder<> &irbuilder); llvm::SmallVector ExtractTrackedValues(llvm::Value *Src, llvm::Type *STy, bool isptr, llvm::IRBuilder<> &irbuilder, llvm::ArrayRef perm_offsets={}); static inline void llvm_dump(llvm::Value *v) @@ -187,45 +187,39 @@ static inline llvm::Instruction *tbaa_decorate(llvm::MDNode *md, llvm::Instructi } // Get PTLS through current task. -static inline llvm::Value *get_current_task_from_pgcstack(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *pgcstack) +static inline llvm::Value *get_current_task_from_pgcstack(llvm::IRBuilder<> &builder, llvm::Value *pgcstack) { using namespace llvm; - auto T_pjlvalue = JuliaType::get_pjlvalue_ty(builder.getContext()); + auto i8 = builder.getInt8Ty(); const int pgcstack_offset = offsetof(jl_task_t, gcstack); - return builder.CreateInBoundsGEP( - T_pjlvalue, pgcstack, - ConstantInt::get(T_size, -(pgcstack_offset / sizeof(void *))), - "current_task"); + return builder.CreateConstInBoundsGEP1_32(i8, pgcstack, -pgcstack_offset, "current_task"); } // Get PTLS through current task. -static inline llvm::Value *get_current_ptls_from_task(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *current_task, llvm::MDNode *tbaa) +static inline llvm::Value *get_current_ptls_from_task(llvm::IRBuilder<> &builder, llvm::Value *current_task, llvm::MDNode *tbaa) { using namespace llvm; - auto T_pjlvalue = JuliaType::get_pjlvalue_ty(builder.getContext()); + auto i8 = builder.getInt8Ty(); + auto T_ptr = builder.getPtrTy(); const int ptls_offset = offsetof(jl_task_t, ptls); - llvm::Value *pptls = builder.CreateInBoundsGEP( - T_pjlvalue, current_task, - ConstantInt::get(T_size, ptls_offset / sizeof(void *)), - "ptls_field"); - LoadInst *ptls_load = builder.CreateAlignedLoad(T_pjlvalue, - pptls, Align(sizeof(void *)), "ptls_load"); + llvm::Value *pptls = builder.CreateConstInBoundsGEP1_32(i8, current_task, ptls_offset, "ptls_field"); + LoadInst *ptls_load = builder.CreateAlignedLoad(T_ptr, pptls, Align(sizeof(void *)), "ptls_load"); // Note: Corresponding store (`t->ptls = ptls`) happens in `ctx_switch` of tasks.c. tbaa_decorate(tbaa, ptls_load); return ptls_load; } // Get signal page through current task. -static inline llvm::Value *get_current_signal_page_from_ptls(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::MDNode *tbaa) +static inline llvm::Value *get_current_signal_page_from_ptls(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::MDNode *tbaa) { using namespace llvm; // return builder.CreateCall(prepare_call(reuse_signal_page_func)); - auto T_psize = T_size->getPointerTo(); - int nthfield = offsetof(jl_tls_states_t, safepoint) / sizeof(void *); - llvm::Value *psafepoint = builder.CreateInBoundsGEP( - T_psize, ptls, ConstantInt::get(T_size, nthfield)); + auto T_ptr = builder.getPtrTy(); + auto i8 = builder.getInt8Ty(); + int nthfield = offsetof(jl_tls_states_t, safepoint); + llvm::Value *psafepoint = builder.CreateConstInBoundsGEP1_32(i8, ptls, nthfield); LoadInst *ptls_load = builder.CreateAlignedLoad( - T_psize, psafepoint, Align(sizeof(void *)), "safepoint"); + T_ptr, psafepoint, Align(sizeof(void *)), "safepoint"); tbaa_decorate(tbaa, ptls_load); return ptls_load; } @@ -239,7 +233,7 @@ static inline void emit_signal_fence(llvm::IRBuilder<> &builder) static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::MDNode *tbaa, bool final = false) { using namespace llvm; - llvm::Value *signal_page = get_current_signal_page_from_ptls(builder, T_size, ptls, tbaa); + llvm::Value *signal_page = get_current_signal_page_from_ptls(builder, ptls, tbaa); emit_signal_fence(builder); Module *M = builder.GetInsertBlock()->getModule(); LLVMContext &C = builder.getContext(); @@ -250,8 +244,7 @@ static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Type *T_s else { Function *F = M->getFunction("julia.safepoint"); if (!F) { - auto T_psize = T_size->getPointerTo(); - FunctionType *FT = FunctionType::get(Type::getVoidTy(C), {T_psize}, false); + FunctionType *FT = FunctionType::get(Type::getVoidTy(C), {T_size->getPointerTo()}, false); F = Function::Create(FT, Function::ExternalLinkage, "julia.safepoint", M); #if JL_LLVM_VERSION >= 160000 F->setMemoryEffects(MemoryEffects::inaccessibleOrArgMemOnly()); @@ -268,8 +261,8 @@ static inline llvm::Value *emit_gc_state_set(llvm::IRBuilder<> &builder, llvm::T { using namespace llvm; Type *T_int8 = state->getType(); - Constant *offset = ConstantInt::getSigned(builder.getInt32Ty(), offsetof(jl_tls_states_t, gc_state)); - Value *gc_state = builder.CreateInBoundsGEP(T_int8, ptls, ArrayRef(offset), "gc_state"); + unsigned offset = offsetof(jl_tls_states_t, gc_state); + Value *gc_state = builder.CreateConstInBoundsGEP1_32(T_int8, ptls, offset, "gc_state"); if (old_state == nullptr) { old_state = builder.CreateLoad(T_int8, gc_state); cast(old_state)->setOrdering(AtomicOrdering::Monotonic); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index e08f08860dfaf..8d1d5ff73b261 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -350,15 +350,7 @@ void LateLowerGCFrame::LiftSelect(State &S, SelectInst *SI) { ConstantInt::get(Type::getInt32Ty(Cond->getContext()), i), "", SI); } - #if JL_LLVM_VERSION >= 170000 assert(FalseElem->getType() == TrueElem->getType()); - #else - if (FalseElem->getType() != TrueElem->getType()) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(FalseElem->getContext().supportsTypedPointers()); - FalseElem = new BitCastInst(FalseElem, TrueElem->getType(), "", SI); - } - #endif SelectInst *SelectBase = SelectInst::Create(Cond, TrueElem, FalseElem, "gclift", SI); int Number = ++S.MaxPtrNumber; S.AllPtrNumbering[SelectBase] = Number; @@ -427,33 +419,7 @@ void LateLowerGCFrame::LiftPhi(State &S, PHINode *Phi) { BaseElem = Base; else BaseElem = IncomingBases[i]; - #if JL_LLVM_VERSION >= 170000 assert(BaseElem->getType() == T_prjlvalue); - #else - if (BaseElem->getType() != T_prjlvalue) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(BaseElem->getContext().supportsTypedPointers()); - auto &remap = CastedRoots[i][BaseElem]; - if (!remap) { - if (auto constant = dyn_cast(BaseElem)) { - remap = ConstantExpr::getBitCast(constant, T_prjlvalue, ""); - } else { - Instruction *InsertBefore; - if (auto arg = dyn_cast(BaseElem)) { - InsertBefore = &*arg->getParent()->getEntryBlock().getFirstInsertionPt(); - } else { - assert(isa(BaseElem) && "Unknown value type detected!"); - InsertBefore = cast(BaseElem)->getNextNonDebugInstruction(); - } - while (isa(InsertBefore)) { - InsertBefore = InsertBefore->getNextNonDebugInstruction(); - } - remap = new BitCastInst(BaseElem, T_prjlvalue, "", InsertBefore); - } - } - BaseElem = remap; - } - #endif lift->addIncoming(BaseElem, IncomingBB); } } @@ -1528,14 +1494,11 @@ SmallVector ExtractTrackedValues(Value *Src, Type *STy, bool isptr, I return Ptrs; } -unsigned TrackWithShadow(Value *Src, Type *STy, bool isptr, Value *Dst, Type *DTy, IRBuilder<> &irbuilder) { +unsigned TrackWithShadow(Value *Src, Type *STy, bool isptr, Value *Dst, IRBuilder<> &irbuilder) { auto Ptrs = ExtractTrackedValues(Src, STy, isptr, irbuilder); for (unsigned i = 0; i < Ptrs.size(); ++i) { - Value *Elem = Ptrs[i];// Dst has type `[n x {}*]*` - Value *Slot = irbuilder.CreateConstInBoundsGEP2_32(DTy, Dst, 0, i); - #if JL_LLVM_VERSION < 170000 - assert(cast(Dst->getType())->isOpaqueOrPointeeTypeMatches(DTy)); - #endif + Value *Elem = Ptrs[i]; + Value *Slot = irbuilder.CreateConstInBoundsGEP1_32(irbuilder.getInt8Ty(), Dst, i * sizeof(void*)); StoreInst *shadowStore = irbuilder.CreateAlignedStore(Elem, Slot, Align(sizeof(void*))); shadowStore->setOrdering(AtomicOrdering::NotAtomic); // TODO: shadowStore->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe); @@ -2133,7 +2096,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { // the type tag. (Note that if the size is not a constant, it will call // gc_alloc_obj, and will redundantly set the tag.) auto allocBytesIntrinsic = getOrDeclare(jl_intrinsics::GCAllocBytes); - auto ptls = get_current_ptls_from_task(builder, T_size, CI->getArgOperand(0), tbaa_gcframe); + auto ptls = get_current_ptls_from_task(builder, CI->getArgOperand(0), tbaa_gcframe); auto newI = builder.CreateCall( allocBytesIntrinsic, { @@ -2319,15 +2282,7 @@ void LateLowerGCFrame::PlaceGCFrameStore(State &S, unsigned R, unsigned MinColor // Pointee types don't have semantics, so the optimizer is // free to rewrite them if convenient. We need to change // it back here for the store. - #if JL_LLVM_VERSION >= 170000 assert(Val->getType() == T_prjlvalue); - #else - if (Val->getType() != T_prjlvalue) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(Val->getContext().supportsTypedPointers()); - Val = new BitCastInst(Val, T_prjlvalue, "", InsertBefore); - } - #endif new StoreInst(Val, slotAddress, InsertBefore); } @@ -2407,18 +2362,7 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St for (CallInst *II : ToDelete) { II->eraseFromParent(); } - #if JL_LLVM_VERSION >= 170000 assert(slotAddress->getType() == AI->getType()); - #else - if (slotAddress->getType() != AI->getType()) { - // If we're replacing an ArrayAlloca, the pointer element type may need to be fixed up - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(slotAddress->getContext().supportsTypedPointers()); - auto BCI = new BitCastInst(slotAddress, AI->getType()); - BCI->insertAfter(slotAddress); - slotAddress = BCI; - } - #endif AI->replaceAllUsesWith(slotAddress); AI->eraseFromParent(); AI = NULL; @@ -2443,15 +2387,7 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St slotAddress->insertAfter(gcframe); auto ValExpr = std::make_pair(Base, isa(Base->getType()) ? -1 : i); auto Elem = MaybeExtractScalar(S, ValExpr, SI); - #if JL_LLVM_VERSION >= 170000 assert(Elem->getType() == T_prjlvalue); - #else - if (Elem->getType() != T_prjlvalue) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(Elem->getContext().supportsTypedPointers()); - Elem = new BitCastInst(Elem, T_prjlvalue, "", SI); - } - #endif //auto Idxs = ArrayRef(Tracked[i]); //Value *Elem = ExtractScalar(Base, true, Idxs, SI); Value *shadowStore = new StoreInst(Elem, slotAddress, SI); diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 736c1acd9525a..488dd46cade21 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -191,7 +191,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, builder.SetInsertPoint(fastTerm->getParent()); fastTerm->removeFromParent(); MDNode *tbaa = tbaa_gcframe; - Value *prior = emit_gc_unsafe_enter(builder, T_size, get_current_ptls_from_task(builder, T_size, get_current_task_from_pgcstack(builder, T_size, pgcstack), tbaa), true); + Value *prior = emit_gc_unsafe_enter(builder, T_size, get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, pgcstack), tbaa), true); builder.Insert(fastTerm); phi->addIncoming(pgcstack, fastTerm->getParent()); // emit pre-return cleanup @@ -203,7 +203,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, for (auto &BB : *pgcstack->getParent()->getParent()) { if (isa(BB.getTerminator())) { builder.SetInsertPoint(BB.getTerminator()); - emit_gc_unsafe_leave(builder, T_size, get_current_ptls_from_task(builder, T_size, get_current_task_from_pgcstack(builder, T_size, phi), tbaa), last_gc_state, true); + emit_gc_unsafe_leave(builder, T_size, get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, phi), tbaa), last_gc_state, true); } } } diff --git a/test/llvmpasses/alloc-opt-gcframe.ll b/test/llvmpasses/alloc-opt-gcframe.ll index e8644899f0914..f53a4d5c01df7 100644 --- a/test/llvmpasses/alloc-opt-gcframe.ll +++ b/test/llvmpasses/alloc-opt-gcframe.ll @@ -10,7 +10,7 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" ; CHECK-NOT: @julia.gc_alloc_obj ; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %gcstack, i64 -12 -; OPAQUE: [[ptls_field:%.*]] = getelementptr inbounds ptr, ptr %current_task, i64 16 +; OPAQUE: [[ptls_field:%.*]] = getelementptr inbounds i8, ptr %current_task, ; OPAQUE-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 ; OPAQUE-NEXT: %v = call noalias nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) ptr addrspace(10) @ijl_gc_small_alloc(ptr [[ptls_load]], i32 [[SIZE_T:[0-9]+]], i32 16, i64 {{.*}} @tag {{.*}}) ; OPAQUE: store atomic ptr addrspace(10) @tag, ptr addrspace(10) {{.*}} unordered, align 8, !tbaa !4 diff --git a/test/llvmpasses/late-lower-gc-addrspaces.ll b/test/llvmpasses/late-lower-gc-addrspaces.ll index 702e44b2b0e28..9c041664a9682 100644 --- a/test/llvmpasses/late-lower-gc-addrspaces.ll +++ b/test/llvmpasses/late-lower-gc-addrspaces.ll @@ -1,6 +1,6 @@ ; This file is a part of Julia. License is MIT: https://julialang.org/license -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s target triple = "amdgcn-amd-amdhsa" target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7-ni:10:11:12:13" @@ -19,28 +19,28 @@ define void @gc_frame_lowering(i64 %a, i64 %b) { top: ; CHECK-LABEL: @gc_frame_lowering -; OPAQUE: %gcframe = call ptr @julia.new_gc_frame(i32 2) -; OPAQUE: %pgcstack = call ptr @julia.get_pgcstack() +; CHECK: %gcframe = call ptr @julia.new_gc_frame(i32 2) +; CHECK: %pgcstack = call ptr @julia.get_pgcstack() %pgcstack = call {}*** @julia.get_pgcstack() -; OPAQUE-NEXT: call void @julia.push_gc_frame(ptr %gcframe, i32 2) -; OPAQUE-NEXT: call ptr addrspace(10) @jl_box_int64 +; CHECK-NEXT: call void @julia.push_gc_frame(ptr %gcframe, i32 2) +; CHECK-NEXT: call ptr addrspace(10) @jl_box_int64 %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a) -; OPAQUE: [[GEP0:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]) -; OPAQUE-NEXT: store ptr addrspace(10) %aboxed, ptr [[GEP0]] +; CHECK: [[GEP0:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]) +; CHECK-NEXT: store ptr addrspace(10) %aboxed, ptr [[GEP0]] %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b) ; CHECK-NEXT: %bboxed = ; Make sure the same gc slot isn't re-used -; OPAQUE-NOT: call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0]]) -; OPAQUE: [[GEP1:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT1:[0-9]+]]) -; OPAQUE-NEXT: store ptr addrspace(10) %bboxed, ptr [[GEP1]] +; CHECK-NOT: call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0]]) +; CHECK: [[GEP1:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT1:[0-9]+]]) +; CHECK-NEXT: store ptr addrspace(10) %bboxed, ptr [[GEP1]] ; CHECK-NEXT: call void @boxed_simple call void @boxed_simple({} addrspace(10)* %aboxed, {} addrspace(10)* %bboxed) -; OPAQUE-NEXT: call void @julia.pop_gc_frame(ptr %gcframe) +; CHECK-NEXT: call void @julia.pop_gc_frame(ptr %gcframe) ret void } @@ -51,14 +51,14 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 -; OPAQUE-NEXT: [[ptls_field:%.*]] = getelementptr inbounds ptr, ptr %current_task, i64 16 -; OPAQUE-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 -; OPAQUE-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) -; OPAQUE-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 -; OPAQUE-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 +; CHECK: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds i8, ptr %current_task, +; CHECK-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 +; CHECK-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) +; CHECK-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 +; CHECK-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj({}** %current_task, i64 8, {} addrspace(10)* @tag) -; OPAQUE-NEXT: ret ptr addrspace(10) %v +; CHECK-NEXT: ret ptr addrspace(10) %v ret {} addrspace(10)* %v } @@ -74,20 +74,20 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 -; OPAQUE-NEXT: [[ptls_field:%.*]] = getelementptr inbounds ptr, ptr %current_task, i64 16 -; OPAQUE-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 -; OPAQUE-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) -; OPAQUE-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 -; OPAQUE-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 +; CHECK: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds i8, ptr %current_task, +; CHECK-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 +; CHECK-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) +; CHECK-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 +; CHECK-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj({}** %current_task, i64 8, {} addrspace(10)* @tag) -; OPAQUE-NEXT: %v64 = bitcast ptr addrspace(10) %v to ptr addrspace(10) +; CHECK-NEXT: %v64 = bitcast ptr addrspace(10) %v to ptr addrspace(10) %v64 = bitcast {} addrspace(10)* %v to i64 addrspace(10)* -; OPAQUE-NEXT: %loadedval = load i64, ptr addrspace(10) %v64, align 8, !range !7 +; CHECK-NEXT: %loadedval = load i64, ptr addrspace(10) %v64, align 8, !range !7 %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !invariant.load !1 -; OPAQUE-NEXT: store i64 %loadedval, ptr addrspace(10) %v64, align 8, !noalias !8 +; CHECK-NEXT: store i64 %loadedval, ptr addrspace(10) %v64, align 8, !noalias !8 store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !2 -; OPAQUE-NEXT: %lv2 = load i64, ptr addrspace(10) %v64, align 8, !tbaa !11, !range !7 +; CHECK-NEXT: %lv2 = load i64, ptr addrspace(10) %v64, align 8, !tbaa !11, !range !7 %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !tbaa !4 ; CHECK-NEXT: ret void ret void diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 093cab1358141..d294847db8f9d 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -1,6 +1,6 @@ ; This file is a part of Julia. License is MIT: https://julialang.org/license -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s @tag = external addrspace(10) global {}, align 16 @@ -16,28 +16,28 @@ define void @gc_frame_lowering(i64 %a, i64 %b) { top: ; CHECK-LABEL: @gc_frame_lowering -; OPAQUE: %gcframe = call ptr @julia.new_gc_frame(i32 2) -; OPAQUE: %pgcstack = call ptr @julia.get_pgcstack() +; CHECK: %gcframe = call ptr @julia.new_gc_frame(i32 2) +; CHECK: %pgcstack = call ptr @julia.get_pgcstack() %pgcstack = call {}*** @julia.get_pgcstack() -; OPAQUE-NEXT: call void @julia.push_gc_frame(ptr %gcframe, i32 2) -; OPAQUE-NEXT: call ptr addrspace(10) @jl_box_int64 +; CHECK-NEXT: call void @julia.push_gc_frame(ptr %gcframe, i32 2) +; CHECK-NEXT: call ptr addrspace(10) @jl_box_int64 %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a) -; OPAQUE: [[GEP0:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]) -; OPAQUE-NEXT: store ptr addrspace(10) %aboxed, ptr [[GEP0]] +; CHECK: [[GEP0:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]) +; CHECK-NEXT: store ptr addrspace(10) %aboxed, ptr [[GEP0]] %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b) ; CHECK-NEXT: %bboxed = ; Make sure the same gc slot isn't re-used -; OPAQUE-NOT: call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0]]) -; OPAQUE: [[GEP1:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT1:[0-9]+]]) -; OPAQUE-NEXT: store ptr addrspace(10) %bboxed, ptr [[GEP1]] +; CHECK-NOT: call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT0]]) +; CHECK: [[GEP1:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 [[GEPSLOT1:[0-9]+]]) +; CHECK-NEXT: store ptr addrspace(10) %bboxed, ptr [[GEP1]] ; CHECK-NEXT: call void @boxed_simple call void @boxed_simple({} addrspace(10)* %aboxed, {} addrspace(10)* %bboxed) -; OPAQUE-NEXT: call void @julia.pop_gc_frame(ptr %gcframe) +; CHECK-NEXT: call void @julia.pop_gc_frame(ptr %gcframe) ret void } @@ -48,14 +48,14 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 -; OPAQUE-NEXT: [[ptls_field:%.*]] = getelementptr inbounds ptr, ptr %current_task, i64 16 -; OPAQUE-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 -; OPAQUE-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) -; OPAQUE-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 -; OPAQUE-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 +; CHECK: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds i8, ptr %current_task, +; CHECK-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 +; CHECK-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) +; CHECK-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 +; CHECK-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj({}** %current_task, i64 8, {} addrspace(10)* @tag) -; OPAQUE-NEXT: ret ptr addrspace(10) %v +; CHECK-NEXT: ret ptr addrspace(10) %v ret {} addrspace(10)* %v } @@ -71,20 +71,20 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 -; OPAQUE-NEXT: [[ptls_field:%.*]] = getelementptr inbounds ptr, ptr %current_task, i64 16 -; OPAQUE-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 -; OPAQUE-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) -; OPAQUE-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 -; OPAQUE-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 +; CHECK: %current_task = getelementptr inbounds ptr, ptr %0, i64 -12 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds i8, ptr %current_task, +; CHECK-NEXT: [[ptls_load:%.*]] = load ptr, ptr [[ptls_field]], align 8, !tbaa !0 +; CHECK-NEXT: %v = call noalias nonnull ptr addrspace(10) @julia.gc_alloc_bytes(ptr [[ptls_load]], [[SIZE_T:i.[0-9]+]] 8, i64 {{.*}} @tag {{.*}}) +; CHECK-NEXT: [[V_HEADROOM:%.*]] = getelementptr inbounds ptr addrspace(10), ptr addrspace(10) %v, i64 -1 +; CHECK-NEXT: store atomic ptr addrspace(10) @tag, ptr addrspace(10) [[V_HEADROOM]] unordered, align 8, !tbaa !4 %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj({}** %current_task, i64 8, {} addrspace(10)* @tag) -; OPAQUE-NEXT: %v64 = bitcast ptr addrspace(10) %v to ptr addrspace(10) +; CHECK-NEXT: %v64 = bitcast ptr addrspace(10) %v to ptr addrspace(10) %v64 = bitcast {} addrspace(10)* %v to i64 addrspace(10)* -; OPAQUE-NEXT: %loadedval = load i64, ptr addrspace(10) %v64, align 8, !range !7 +; CHECK-NEXT: %loadedval = load i64, ptr addrspace(10) %v64, align 8, !range !7 %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !invariant.load !1 -; OPAQUE-NEXT: store i64 %loadedval, ptr addrspace(10) %v64, align 8, !noalias !8 +; CHECK-NEXT: store i64 %loadedval, ptr addrspace(10) %v64, align 8, !noalias !8 store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !2 -; OPAQUE-NEXT: %lv2 = load i64, ptr addrspace(10) %v64, align 8, !tbaa !11, !range !7 +; CHECK-NEXT: %lv2 = load i64, ptr addrspace(10) %v64, align 8, !tbaa !11, !range !7 %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !tbaa !4 ; CHECK-NEXT: ret void ret void @@ -162,13 +162,13 @@ define void @decayar([2 x {} addrspace(10)* addrspace(11)*] %ar) { ; CHECK-LABEL: @decayar -; OPAQUE: %gcframe = call ptr @julia.new_gc_frame(i32 2) -; OPAQUE: [[gc_slot_addr_:%.*]]1 = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 1) -; OPAQUE: store ptr addrspace(10) %l0, ptr [[gc_slot_addr_:%.*]], align 8 -; OPAQUE: [[gc_slot_addr_:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 0) -; OPAQUE: store ptr addrspace(10) %l1, ptr [[gc_slot_addr_:%.*]], align 8 -; OPAQUE: %r = call i32 @callee_root(ptr addrspace(10) %l0, ptr addrspace(10) %l1) -; OPAQUE: call void @julia.pop_gc_frame(ptr %gcframe) +; CHECK: %gcframe = call ptr @julia.new_gc_frame(i32 2) +; CHECK: [[gc_slot_addr_:%.*]]1 = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 1) +; CHECK: store ptr addrspace(10) %l0, ptr [[gc_slot_addr_:%.*]], align 8 +; CHECK: [[gc_slot_addr_:%.*]] = call ptr @julia.get_gc_frame_slot(ptr %gcframe, i32 0) +; CHECK: store ptr addrspace(10) %l1, ptr [[gc_slot_addr_:%.*]], align 8 +; CHECK: %r = call i32 @callee_root(ptr addrspace(10) %l0, ptr addrspace(10) %l1) +; CHECK: call void @julia.pop_gc_frame(ptr %gcframe) !0 = !{i64 0, i64 23} !1 = !{!1} diff --git a/test/llvmpasses/names.jl b/test/llvmpasses/names.jl index fe692d0fab787..1ab2204044804 100644 --- a/test/llvmpasses/names.jl +++ b/test/llvmpasses/names.jl @@ -135,7 +135,8 @@ emit(f2, Float64, Float64, Float64, Float64, Float64, Float64, Float64) # CHECK: define {{(swiftcc )?}}nonnull ptr @julia_f5 # CHECK-SAME: %"a::A" -# CHECK: %"a::A.b_ptr.c_ptr.d +# CHECK: %"a::A.d +# COM: this text check relies on our LLVM code emission being relatively poor, which is not always the case emit(f5, A) # CHECK: define {{(swiftcc )?}}nonnull ptr @julia_f6 From bcf41ba0cdd2aa8c1f21c8aa08b52b017ffbd014 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 11 Sep 2024 02:04:35 +0900 Subject: [PATCH 196/548] minor fixup for JuliaLang/julia#55705 (#55726) --- base/compiler/abstractinterpretation.jl | 3 ++- test/compiler/inference.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 83a39ce10d891..bb5f2dd1ad180 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2232,7 +2232,8 @@ function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vecto elseif !isvarargtype(argtypes[2]) MethodError else - tmerge(𝕃ᵢ, MethodError, ArgumentError) + ⊔ = join(typeinf_lattice(interp)) + MethodError ⊔ ArgumentError end return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index f15df49d75745..9454c53a09fb7 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6137,3 +6137,7 @@ end == TypeError @test Base.infer_exception_type((Char,)) do x invoke(f_invoke_exct, Tuple{Number}, x) end == TypeError + +@test Base.infer_exception_type((Vector{Any},)) do args + Core.throw_methoderror(args...) +end == Union{MethodError,ArgumentError} From a7c9235afe27fd34e31fcce387ade7f508d45003 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 10 Sep 2024 14:43:33 -0400 Subject: [PATCH 197/548] [REPL] prevent silent hang if precompile script async blocks fail (#55685) --- stdlib/REPL/src/Terminals.jl | 2 ++ stdlib/REPL/src/precompile.jl | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index 4f3e99f1d206c..0cf6888d248e8 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -97,6 +97,7 @@ abstract type UnixTerminal <: TextTerminal end pipe_reader(t::UnixTerminal) = t.in_stream::IO pipe_writer(t::UnixTerminal) = t.out_stream::IO +@nospecialize mutable struct TerminalBuffer <: UnixTerminal out_stream::IO end @@ -107,6 +108,7 @@ mutable struct TTYTerminal <: UnixTerminal out_stream::IO err_stream::IO end +@specialize const CSI = "\x1b[" diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index a6effb9f013fc..82a1a0bb78ee8 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -96,7 +96,7 @@ let repltask = @task try Base.run_std_repl(REPL, false, :yes, true) finally - redirect_stderr(isopen(orig_stderr) ? orig_stderr : devnull) + redirect_stdin(isopen(orig_stdin) ? orig_stdin : devnull) redirect_stdout(isopen(orig_stdout) ? orig_stdout : devnull) close(pts) end @@ -106,14 +106,14 @@ let redirect_stdin(pts) redirect_stdout(pts) redirect_stderr(pts) - REPL.print_qualified_access_warning(Base.Iterators, Base, :minimum) # trigger the warning while stderr is suppressed try - schedule(repltask) - # wait for the definitive prompt before start writing to the TTY - readuntil(output_copy, JULIA_PROMPT) + REPL.print_qualified_access_warning(Base.Iterators, Base, :minimum) # trigger the warning while stderr is suppressed finally redirect_stderr(isopen(orig_stderr) ? orig_stderr : devnull) end + schedule(repltask) + # wait for the definitive prompt before start writing to the TTY + readuntil(output_copy, JULIA_PROMPT) write(debug_output, "\n#### REPL STARTED ####\n") sleep(0.1) readavailable(output_copy) @@ -148,9 +148,9 @@ let write(ptm, "$CTRL_D") wait(repltask) finally - close(pts) redirect_stdin(isopen(orig_stdin) ? orig_stdin : devnull) redirect_stdout(isopen(orig_stdout) ? orig_stdout : devnull) + close(pts) end wait(tee) end From 56451d8eb18f6a1f9339eeb656033b417ec615b6 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Tue, 10 Sep 2024 20:45:38 +0200 Subject: [PATCH 198/548] Various fixes to byte / bytearray search (#54579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was originally intended as a targeted fix to #54578, but I ran into a bunch of smaller issues with this code that also needed to be solved and it turned out to be difficult to fix them with small, trivial PRs. I would also like to refactor this whole file, but I want these correctness fixes to be merged first, because a larger refactoring has higher risk of getting stuck without getting reviewed and merged. ## Larger things that needs decisions * The internal union `Base.ByteArray` has been deleted. Instead, the unions `DenseInt8` and `DenseUInt8` have been added. These more comprehensively cover the types that was meant, e.g. `Memory{UInt8}` was incorrectly not covered by the former. As stated in the TODO, the concept of a "memory backed dense byte array" is needed throughout Julia, so this ideally needs to be implemented as a single type and used throughout Base. The fix here is a decent temporary solution. See #53178 #54581 * The `findall` docstring between two arrays was incorrectly not attached to the method - now it is. **Note that this change _changes_ the documentation** since it includes a docstring that was previously missed. Hence, it's an API addition. * Added a new minimal `testhelpers/OffsetDenseArrays.jl` which provide a `DenseVector` with offset axes for testing purposes. ## Trivial fixes * `findfirst(==(Int8(-1)), [0xff])` and similar findlast, findnext and findprev is no longer buggy, see #54578 * `findfirst([0x0ff], Int8[-1])` is similarly no longer buggy, see #54578 * `findnext(==('\xa6'), "æ", 1)` and `findprev(==('\xa6'), "æa", 2)` no longer incorrectly throws an error * The byte-oriented find* functions now work correctly with offset arrays * Fixed incorrect use of `GC.@preserve`, where the pointer was taken before the preserve block. * More of the optimised string methods now also apply to `SubString{String}` Closes #54578 Co-authored-by: Martin Holters --- base/char.jl | 1 + base/strings/search.jl | 136 +++++++++++++++++--------- test/strings/search.jl | 49 ++++++++++ test/testhelpers/OffsetDenseArrays.jl | 31 ++++++ 4 files changed, 170 insertions(+), 47 deletions(-) create mode 100644 test/testhelpers/OffsetDenseArrays.jl diff --git a/base/char.jl b/base/char.jl index bc68a672ce0ca..2e8410f6903e2 100644 --- a/base/char.jl +++ b/base/char.jl @@ -223,6 +223,7 @@ hash(x::Char, h::UInt) = hash_uint64(((bitcast(UInt32, x) + UInt64(0xd4d64234)) << 32) ⊻ UInt64(h)) first_utf8_byte(c::Char) = (bitcast(UInt32, c) >> 24) % UInt8 +first_utf8_byte(c::AbstractChar) = first_utf8_byte(Char(c)::Char) # fallbacks: isless(x::AbstractChar, y::AbstractChar) = isless(Char(x), Char(y)) diff --git a/base/strings/search.jl b/base/strings/search.jl index b9c14f06e0898..9bd69ae2f8a03 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -10,7 +10,29 @@ match strings with [`match`](@ref). """ abstract type AbstractPattern end -nothing_sentinel(i) = i == 0 ? nothing : i +# TODO: These unions represent bytes in memory that can be accessed via a pointer. +# this property is used throughout Julia, e.g. also in IO code. +# This deserves a better solution - see #53178. +# If such a better solution comes in place, these unions should be replaced. +const DenseInt8 = Union{ + DenseArray{Int8}, + FastContiguousSubArray{Int8,N,<:DenseArray} where N +} + +# Note: This union is different from that above in that it includes CodeUnits. +# Currently, this is redundant as CodeUnits <: DenseVector, but this subtyping +# is buggy and may be removed in the future, see #54002 +const DenseUInt8 = Union{ + DenseArray{UInt8}, + FastContiguousSubArray{UInt8,N,<:DenseArray} where N, + CodeUnits{UInt8, <:Union{String, SubString{String}}}, + FastContiguousSubArray{UInt8,N,<:CodeUnits{UInt8, <:Union{String, SubString{String}}}} where N, +} + +const DenseUInt8OrInt8 = Union{DenseUInt8, DenseInt8} + +last_byteindex(x::Union{String, SubString{String}}) = ncodeunits(x) +last_byteindex(x::DenseUInt8OrInt8) = lastindex(x) function last_utf8_byte(c::Char) u = reinterpret(UInt32, c) @@ -30,11 +52,11 @@ function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar} end @inbounds isvalid(s, i) || string_index_err(s, i) c = pred.x - c ≤ '\x7f' && return nothing_sentinel(_search(s, c % UInt8, i)) + c ≤ '\x7f' && return _search(s, first_utf8_byte(c), i) while true i = _search(s, first_utf8_byte(c), i) - i == 0 && return nothing - pred(s[i]) && return i + i === nothing && return nothing + isvalid(s, i) && pred(s[i]) && return i i = nextind(s, i) end end @@ -47,31 +69,41 @@ const DenseBytes = Union{ CodeUnits{UInt8, <:Union{String, SubString{String}}}, } -const ByteArray = Union{DenseBytes, DenseArrayType{Int8}} +function findfirst(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{UInt8, Int8}}, a::Union{DenseInt8, DenseUInt8}) + findnext(pred, a, firstindex(a)) +end -findfirst(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = - nothing_sentinel(_search(a, pred.x)) +function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},UInt8}, a::DenseUInt8, i::Integer) + _search(a, pred.x, i) +end -findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = - nothing_sentinel(_search(a, pred.x, i)) +function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Int8}, a::DenseInt8, i::Integer) + _search(a, pred.x, i) +end -findfirst(::typeof(iszero), a::ByteArray) = nothing_sentinel(_search(a, zero(UInt8))) -findnext(::typeof(iszero), a::ByteArray, i::Integer) = nothing_sentinel(_search(a, zero(UInt8), i)) +# iszero is special, in that the bitpattern for zero for Int8 and UInt8 is the same, +# so we can use memchr even if we search for an Int8 in an UInt8 array or vice versa +findfirst(::typeof(iszero), a::DenseUInt8OrInt8) = _search(a, zero(UInt8)) +findnext(::typeof(iszero), a::DenseUInt8OrInt8, i::Integer) = _search(a, zero(UInt8), i) -function _search(a::Union{String,SubString{String},<:ByteArray}, b::Union{Int8,UInt8}, i::Integer = 1) - if i < 1 +function _search(a::Union{String,SubString{String},DenseUInt8OrInt8}, b::Union{Int8,UInt8}, i::Integer = firstindex(a)) + fst = firstindex(a) + lst = last_byteindex(a) + if i < fst throw(BoundsError(a, i)) end - n = sizeof(a) - if i > n - return i == n+1 ? 0 : throw(BoundsError(a, i)) + n_bytes = lst - i + 1 + if i > lst + return i == lst+1 ? nothing : throw(BoundsError(a, i)) end - p = pointer(a) - q = GC.@preserve a ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-1, b, n-i+1) - return q == C_NULL ? 0 : Int(q-p+1) + GC.@preserve a begin + p = pointer(a) + q = ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-fst, b, n_bytes) + end + return q == C_NULL ? nothing : (q-p+fst) % Int end -function _search(a::ByteArray, b::AbstractChar, i::Integer = 1) +function _search(a::DenseUInt8, b::AbstractChar, i::Integer = firstindex(a)) if isascii(b) _search(a,UInt8(b),i) else @@ -80,41 +112,51 @@ function _search(a::ByteArray, b::AbstractChar, i::Integer = 1) end function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar}, - s::String, i::Integer) + s::Union{String, SubString{String}}, i::Integer) c = pred.x - c ≤ '\x7f' && return nothing_sentinel(_rsearch(s, c % UInt8, i)) + c ≤ '\x7f' && return _rsearch(s, first_utf8_byte(c), i) b = first_utf8_byte(c) while true i = _rsearch(s, b, i) - i == 0 && return nothing - pred(s[i]) && return i + i == nothing && return nothing + isvalid(s, i) && pred(s[i]) && return i i = prevind(s, i) end end -findlast(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = - nothing_sentinel(_rsearch(a, pred.x)) +function findlast(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::DenseUInt8OrInt8) + findprev(pred, a, lastindex(a)) +end -findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = - nothing_sentinel(_rsearch(a, pred.x, i)) +function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Int8}, a::DenseInt8, i::Integer) + _rsearch(a, pred.x, i) +end -findlast(::typeof(iszero), a::ByteArray) = nothing_sentinel(_rsearch(a, zero(UInt8))) -findprev(::typeof(iszero), a::ByteArray, i::Integer) = nothing_sentinel(_rsearch(a, zero(UInt8), i)) +function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},UInt8}, a::DenseUInt8, i::Integer) + _rsearch(a, pred.x, i) +end -function _rsearch(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = sizeof(a)) - if i < 1 - return i == 0 ? 0 : throw(BoundsError(a, i)) +# See comments above for findfirst(::typeof(iszero)) methods +findlast(::typeof(iszero), a::DenseUInt8OrInt8) = _rsearch(a, zero(UInt8)) +findprev(::typeof(iszero), a::DenseUInt8OrInt8, i::Integer) = _rsearch(a, zero(UInt8), i) + +function _rsearch(a::Union{String,SubString{String},DenseUInt8OrInt8}, b::Union{Int8,UInt8}, i::Integer = last_byteindex(a)) + fst = firstindex(a) + lst = last_byteindex(a) + if i < fst + return i == fst - 1 ? nothing : throw(BoundsError(a, i)) + end + if i > lst + return i == lst+1 ? nothing : throw(BoundsError(a, i)) end - n = sizeof(a) - if i > n - return i == n+1 ? 0 : throw(BoundsError(a, i)) + GC.@preserve a begin + p = pointer(a) + q = ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i-fst+1) end - p = pointer(a) - q = GC.@preserve a ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i) - return q == C_NULL ? 0 : Int(q-p+1) + return q == C_NULL ? nothing : (q-p+fst) % Int end -function _rsearch(a::ByteArray, b::AbstractChar, i::Integer = length(a)) +function _rsearch(a::DenseUInt8, b::AbstractChar, i::Integer = length(a)) if isascii(b) _rsearch(a,UInt8(b),i) else @@ -224,18 +266,19 @@ end in(c::AbstractChar, s::AbstractString) = (findfirst(isequal(c),s)!==nothing) -function _searchindex(s::Union{AbstractString,ByteArray}, +function _searchindex(s::Union{AbstractString,DenseUInt8OrInt8}, t::Union{AbstractString,AbstractChar,Int8,UInt8}, i::Integer) + sentinel = firstindex(s) - 1 x = Iterators.peel(t) if isnothing(x) - return 1 <= i <= nextind(s,lastindex(s))::Int ? i : + return firstindex(s) <= i <= nextind(s,lastindex(s))::Int ? i : throw(BoundsError(s, i)) end t1, trest = x while true i = findnext(isequal(t1),s,i) - if i === nothing return 0 end + if i === nothing return sentinel end ii = nextind(s, i)::Int a = Iterators.Stateful(trest) matched = all(splat(==), zip(SubString(s, ii), a)) @@ -509,9 +552,8 @@ julia> findall(UInt8[1,2], UInt8[1,2,3,1,2]) !!! compat "Julia 1.3" This method requires at least Julia 1.3. """ - -function findall(t::Union{AbstractString, AbstractPattern, AbstractVector{<:Union{Int8,UInt8}}}, - s::Union{AbstractString, AbstractPattern, AbstractVector{<:Union{Int8,UInt8}}}, +function findall(t::Union{AbstractString, AbstractPattern, AbstractVector{UInt8}}, + s::Union{AbstractString, AbstractPattern, AbstractVector{UInt8}}, ; overlap::Bool=false) found = UnitRange{Int}[] i, e = firstindex(s), lastindex(s) @@ -564,7 +606,7 @@ function _rsearchindex(s::AbstractString, end end -function _rsearchindex(s::String, t::String, i::Integer) +function _rsearchindex(s::Union{String, SubString{String}}, t::Union{String, SubString{String}}, i::Integer) # Check for fast case of a single byte if lastindex(t) == 1 return something(findprev(isequal(t[1]), s, i), 0) diff --git a/test/strings/search.jl b/test/strings/search.jl index 692286359868d..d8883bad24b48 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -155,6 +155,16 @@ for str in [u8str] @test findprev(isequal('ε'), str, 4) === nothing end +# See the comments in #54579 +@testset "Search for invalid chars" begin + @test findfirst(==('\xff'), "abc\xffde") == 4 + @test findprev(isequal('\xa6'), "abc\xa69", 5) == 4 + @test isnothing(findfirst(==('\xff'), "abcdeæd")) + + @test isnothing(findnext(==('\xa6'), "æ", 1)) + @test isnothing(findprev(==('\xa6'), "æa", 2)) +end + # string forward search with a single-char string @test findfirst("x", astr) === nothing @test findfirst("H", astr) == 1:1 @@ -445,6 +455,45 @@ end @test_throws BoundsError findprev(pattern, A, -3) end end + + @test findall([0x01, 0x02], [0x03, 0x01, 0x02, 0x01, 0x02, 0x06]) == [2:3, 4:5] + @test isempty(findall([0x04, 0x05], [0x03, 0x04, 0x06])) +end + +# Issue 54578 +@testset "No conflation of Int8 and UInt8" begin + # Work for mixed types if the values are the same + @test findfirst(==(Int8(1)), [0x01]) == 1 + @test findnext(iszero, Int8[0, -2, 0, -3], 2) == 3 + @test findfirst(Int8[1,4], UInt8[0, 2, 4, 1, 8, 1, 4, 2]) == 6:7 + @test findprev(UInt8[5, 6], Int8[1, 9, 2, 5, 6, 3], 6) == 4:5 + + # Returns nothing for the same methods if the values are different, + # even if the bitpatterns are the same + @test isnothing(findfirst(==(Int8(-1)), [0xff])) + @test isnothing(findnext(isequal(0xff), Int8[-1, -2, -1], 2)) + @test isnothing(findfirst(UInt8[0xff, 0xfe], Int8[0, -1, -2, 1, 8, 1, 4, 2])) + @test isnothing(findprev(UInt8[0xff, 0xfe], Int8[1, 9, 2, -1, -2, 3], 6)) +end + +@testset "DenseArray with offsets" begin + isdefined(Main, :OffsetDenseArrays) || @eval Main include("../testhelpers/OffsetDenseArrays.jl") + OffsetDenseArrays = Main.OffsetDenseArrays + + A = OffsetDenseArrays.OffsetDenseArray(collect(0x61:0x69), 100) + @test findfirst(==(0x61), A) == 101 + @test findlast(==(0x61), A) == 101 + @test findfirst(==(0x00), A) === nothing + + @test findfirst([0x62, 0x63, 0x64], A) == 102:104 + @test findlast([0x63, 0x64], A) == 103:104 + @test findall([0x62, 0x63], A) == [102:103] + + @test findfirst(iszero, A) === nothing + A = OffsetDenseArrays.OffsetDenseArray([0x01, 0x02, 0x00, 0x03], -100) + @test findfirst(iszero, A) == -97 + @test findnext(==(0x02), A, -99) == -98 + @test findnext(==(0x02), A, -97) === nothing end # issue 32568 diff --git a/test/testhelpers/OffsetDenseArrays.jl b/test/testhelpers/OffsetDenseArrays.jl new file mode 100644 index 0000000000000..44a1b8d627800 --- /dev/null +++ b/test/testhelpers/OffsetDenseArrays.jl @@ -0,0 +1,31 @@ +""" + module OffsetDenseArrays + +A minimal implementation of an offset array which is also <: DenseArray. +""" +module OffsetDenseArrays + +struct OffsetDenseArray{A <: DenseVector, T} <: DenseVector{T} + x::A + offset::Int +end +OffsetDenseArray(x::AbstractVector{T}, i::Integer) where {T} = OffsetDenseArray{typeof(x), T}(x, Int(i)) + +Base.size(x::OffsetDenseArray) = size(x.x) +Base.pointer(x::OffsetDenseArray) = pointer(x.x) + +function Base.getindex(x::OffsetDenseArray, i::Integer) + @boundscheck checkbounds(x.x, i - x.offset) + x.x[i - x.offset] +end + +function Base.setindex(x::OffsetDenseArray, v, i::Integer) + @boundscheck checkbounds(x.x, i - x.offset) + x.x[i - x.offset] = v +end + +IndexStyle(::Type{<:OffsetDenseArray}) = Base.IndexLinear() +Base.axes(x::OffsetDenseArray) = (x.offset + 1 : x.offset + length(x.x),) +Base.keys(x::OffsetDenseArray) = only(axes(x)) + +end # module From c6c449ccdb75fbd72700704ae669e1f98e512206 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 10 Sep 2024 16:13:52 -0400 Subject: [PATCH 199/548] codegen: deduplicate code for calling a specsig (#55728) I am tired of having 3 gratuitously different versions of this code to maintain. --- src/codegen.cpp | 296 +++++++++--------------------------------------- 1 file changed, 56 insertions(+), 240 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9184e4895ab6d..a82056eb36e21 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2342,7 +2342,8 @@ static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isbox // replace T::Type{T} with T return ghostValue(ctx, typ); } - } else if (jl_is_datatype(typ) && jl_is_datatype_singleton((jl_datatype_t*)typ)) { + } + else if (jl_is_datatype(typ) && jl_is_datatype_singleton((jl_datatype_t*)typ)) { // no need to explicitly load/store a constant/ghost value return ghostValue(ctx, typ); } @@ -5007,17 +5008,13 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value return emit_jlcall(ctx, prepare_call(theFptr), theF, argv, nargs, trampoline); } -static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, llvm::Value *callee, StringRef specFunctionObject, jl_code_instance_t *fromexternal, - ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty) +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, jl_returninfo_t &returninfo, jl_code_instance_t *fromexternal, + ArrayRef argv, size_t nargs) { ++EmittedSpecfunCalls; // emit specialized call site bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); - jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, callee, specFunctionObject, specTypes, jlretty, is_opaque_closure, gcstack_arg); FunctionType *cft = returninfo.decl.getFunctionType(); - *cc = returninfo.cc; - *return_roots = returninfo.return_roots; - size_t nfargs = cft->getNumParams(); SmallVector argvals(nfargs); unsigned idx = 0; @@ -5059,16 +5056,17 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos // n.b.: specTypes is required to be a datatype by construction for specsig jl_cgval_t arg = argv[i]; if (is_opaque_closure && i == 0) { - // Special optimization for opaque closures: We know that specsig opaque - // closures don't look at their type tag (they are fairly quickly discarded - // for their environments). Therefore, we can just pass these as a pointer, - // rather than a boxed value. + // Special implementation for opaque closures: their jt and thus + // julia_type_to_llvm values are likely wrong, so override the + // behavior here to directly pass the expected pointer based instead + // just on passing arg as a pointer arg = value_to_pointer(ctx, arg); argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); } else if (is_uniquerep_Type(jt)) { continue; - } else { + } + else { bool isboxed = deserves_argbox(jt); Type *et = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); if (type_is_ghost(et)) @@ -5079,7 +5077,6 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos } else if (et->isAggregateType()) { arg = value_to_pointer(ctx, arg); - // can lazy load on demand, no copy needed argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); } else { @@ -5135,7 +5132,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos ctx.builder.CreateICmpEQ( ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)), - decay_derived(ctx, argvals[0]), + decay_derived(ctx, result), decay_derived(ctx, box) ); retval = mark_julia_slot(derived, @@ -5149,6 +5146,19 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos retval = mark_julia_slot(NULL, jlretty, call, ctx.tbaa().tbaa_stack); break; } + return retval; +} + +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, llvm::Value *callee, StringRef specFunctionObject, jl_code_instance_t *fromexternal, + ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *nreturn_roots, jl_value_t *inferred_retty) +{ + ++EmittedSpecfunCalls; + // emit specialized call site + bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); + jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, callee, specFunctionObject, specTypes, jlretty, is_opaque_closure, gcstack_arg); + *cc = returninfo.cc; + *nreturn_roots = returninfo.return_roots; + jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, specTypes, jlretty, returninfo, fromexternal, argv, nargs); // see if inference has a different / better type for the call than the lambda return update_julia_type(ctx, retval, inferred_retty); } @@ -6248,7 +6258,8 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met if (closure_method->source) { mi = jl_specializations_get_linfo(closure_method, sigtype, jl_emptysvec); ci = (jl_code_instance_t*)jl_rettype_inferred_addr(mi, ctx.min_world, ctx.max_world); - } else { + } + else { mi = (jl_method_instance_t*)jl_atomic_load_relaxed(&closure_method->specializations); assert(jl_is_method_instance(mi)); ci = jl_atomic_load_relaxed(&mi->cache); @@ -6291,7 +6302,8 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met closure_decls.specFunctionObject; if (GlobalValue *V = jl_Module->getNamedValue(fname)) { F = cast(V); - } else { + } + else { F = Function::Create(get_func_sig(ctx.builder.getContext()), Function::ExternalLinkage, fname, jl_Module); @@ -6302,7 +6314,8 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met Function *specF = NULL; if (!isspecsig) { specF = F; - } else { + } + else { //emission context holds context lock so can get module specF = closure_m.getModuleUnlocked()->getFunction(closure_decls.specFunctionObject); if (specF) { @@ -6817,14 +6830,6 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptr return f; } -static Type *get_returnroots_type(jl_codectx_t &ctx, unsigned rootcount) { - return ArrayType::get(ctx.types().T_prjlvalue, rootcount); -} - -static Type *get_unionbytes_type(LLVMContext &C, unsigned unionbytes) { - return ArrayType::get(getInt8Ty(C), unionbytes); -} - static void emit_cfunc_invalidate( Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, @@ -6876,15 +6881,13 @@ static void emit_cfunc_invalidate( else { Value *arg_v = &*AI; ++AI; - Type *at = arg_v->getType(); if ((i == 0 && is_for_opaque_closure) || (!isboxed && et->isAggregateType())) { myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const); } else { - assert(at == et); + assert(arg_v->getType() == et); myargs[i] = mark_julia_type(ctx, arg_v, isboxed, jt); } - (void)at; } } assert(AI == gf_thunk->arg_end()); @@ -7306,77 +7309,9 @@ static Function* gen_cfun_wrapper( bool is_opaque_closure = jl_is_method(lam->def.value) && lam->def.method->is_for_opaque_closure; assert(calltype == 3); // emit a specsig call - bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); StringRef protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)callptr, invoke, codeinst); + bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); jl_returninfo_t returninfo = get_specsig_function(ctx, M, NULL, protoname, lam->specTypes, astrt, is_opaque_closure, gcstack_arg); - FunctionType *cft = returninfo.decl.getFunctionType(); - jlfunc_sret = (returninfo.cc == jl_returninfo_t::SRet); - - // TODO: Can use use emit_call_specfun_other here? - SmallVector args; - Value *result = nullptr; - if (jlfunc_sret || returninfo.cc == jl_returninfo_t::Union) { - // fuse the two sret together, or emit an alloca to hold it - if (sig.sret && jlfunc_sret) { - result = emit_bitcast(ctx, sretPtr, cft->getParamType(0)); - } - else { - if (jlfunc_sret) { - result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType()); - setName(ctx.emission_context, result, "sret"); - #if JL_LLVM_VERSION < 170000 - assert(cast(result->getType())->hasSameElementTypeAs(cast(cft->getParamType(0)))); - #endif - } else { - result = emit_static_alloca(ctx, get_unionbytes_type(ctx.builder.getContext(), returninfo.union_bytes)); - setName(ctx.emission_context, result, "result_union"); - #if JL_LLVM_VERSION < 170000 - assert(cast(result->getType())->hasSameElementTypeAs(cast(cft->getParamType(0)))); - #endif - } - } - args.push_back(result); - } - if (returninfo.return_roots) { - AllocaInst *return_roots = emit_static_alloca(ctx, get_returnroots_type(ctx, returninfo.return_roots)); - setName(ctx.emission_context, return_roots, "return_roots"); - args.push_back(return_roots); - } - if (gcstack_arg) - args.push_back(ctx.pgcstack); - for (size_t i = 0; i < nargs + 1; i++) { - // figure out how to repack the arguments - jl_cgval_t &inputarg = inputargs[i]; - Value *arg; - jl_value_t *spect = (i == 0 && is_opaque_closure) ? (jl_value_t*)jl_any_type : - jl_nth_slot_type(lam->specTypes, i); - // n.b. specTypes is required to be a datatype by construction for specsig - bool isboxed = deserves_argbox(spect); - Type *T = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, spect); - if (is_uniquerep_Type(spect)) { - continue; - } - else if (isboxed) { - arg = boxed(ctx, inputarg); - } - else if (type_is_ghost(T)) { - continue; // ghost types are skipped by the specsig method signature - } - else if (T->isAggregateType()) { - // aggregate types are passed by pointer - inputarg = value_to_pointer(ctx, inputarg); - arg = decay_derived(ctx, data_pointer(ctx, inputarg)); - } - else { - arg = emit_unbox(ctx, T, inputarg, spect); - assert(!isa(arg)); - } - - // add to argument list - args.push_back(arg); - } - Value *theFptr = returninfo.decl.getCallee(); - assert(theFptr); if (age_ok) { funcName += "_gfthunk"; Function *gf_thunk = Function::Create(returninfo.decl.getFunctionType(), @@ -7388,49 +7323,17 @@ static Function* gen_cfun_wrapper( // but which has the signature of a specsig emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, is_opaque_closure, nargs + 1, ctx.emission_context, min_world, max_world); - theFptr = ctx.builder.CreateSelect(age_ok, theFptr, gf_thunk); + returninfo.decl = FunctionCallee(returninfo.decl.getFunctionType(), ctx.builder.CreateSelect(age_ok, returninfo.decl.getCallee(), gf_thunk)); } - - #if JL_LLVM_VERSION < 170000 - assert(cast(theFptr->getType())->isOpaqueOrPointeeTypeMatches(returninfo.decl.getFunctionType())); - #endif - CallInst *call = ctx.builder.CreateCall( - returninfo.decl.getFunctionType(), - theFptr, ArrayRef(args)); - call->setAttributes(returninfo.attrs); - if (gcstack_arg) - call->setCallingConv(CallingConv::Swift); - - switch (returninfo.cc) { - case jl_returninfo_t::Boxed: - retval = mark_julia_type(ctx, call, true, astrt); - break; - case jl_returninfo_t::Register: - retval = mark_julia_type(ctx, call, false, astrt); - break; - case jl_returninfo_t::SRet: - retval = mark_julia_slot(result, astrt, NULL, ctx.tbaa().tbaa_stack); - break; - case jl_returninfo_t::Union: { - Value *box = ctx.builder.CreateExtractValue(call, 0); - Value *tindex = ctx.builder.CreateExtractValue(call, 1); - Value *derived = ctx.builder.CreateSelect( - ctx.builder.CreateICmpEQ( - ctx.builder.CreateAnd(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), - ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)), - decay_derived(ctx, result), - decay_derived(ctx, box)); - retval = mark_julia_slot(derived, - astrt, - tindex, - ctx.tbaa().tbaa_stack); - assert(box->getType() == ctx.types().T_prjlvalue); - retval.Vboxed = box; - break; - } - case jl_returninfo_t::Ghosts: - retval = mark_julia_slot(NULL, astrt, call, ctx.tbaa().tbaa_stack); - break; + retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, codeinst->rettype, returninfo, nullptr, inputargs, nargs + 1); + jlfunc_sret = (returninfo.cc == jl_returninfo_t::SRet); + if (jlfunc_sret && sig.sret) { + // fuse the two sret together + assert(retval.ispointer()); + AllocaInst *result = cast(retval.V); + retval.V = sretPtr; + result->replaceAllUsesWith(sretPtr); + result->eraseFromParent(); } } @@ -7729,7 +7632,7 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi } // generate a julia-callable function that calls f (AKA lam) -static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, const jl_returninfo_t &f, int retarg, StringRef funcName, +static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, jl_returninfo_t &f, unsigned nargs, int retarg, StringRef funcName, Module *M, jl_codegen_params_t ¶ms) { ++GeneratedInvokeWrappers; @@ -7757,86 +7660,26 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret ctx.builder.SetCurrentDebugLocation(noDbg); allocate_gc_frame(ctx, b0); - // TODO: replace this with emit_call_specfun_other? - FunctionType *ftype = const_cast(f.decl).getFunctionType(); - size_t nfargs = ftype->getNumParams(); - SmallVector args(nfargs); - unsigned idx = 0; - AllocaInst *result = NULL; - switch (f.cc) { - case jl_returninfo_t::Boxed: - case jl_returninfo_t::Register: - case jl_returninfo_t::Ghosts: - break; - case jl_returninfo_t::SRet: - #if JL_LLVM_VERSION < 170000 - assert(cast(ftype->getParamType(0))->isOpaqueOrPointeeTypeMatches(getAttributeAtIndex(f.attrs, 1, Attribute::StructRet).getValueAsType())); - #endif - result = ctx.builder.CreateAlloca(getAttributeAtIndex(f.attrs, 1, Attribute::StructRet).getValueAsType()); - setName(ctx.emission_context, result, "sret"); - args[idx] = result; - idx++; - break; - case jl_returninfo_t::Union: - result = ctx.builder.CreateAlloca(ArrayType::get(getInt8Ty(ctx.builder.getContext()), f.union_bytes)); - if (f.union_align > 1) - result->setAlignment(Align(f.union_align)); - args[idx] = result; - idx++; - setName(ctx.emission_context, result, "result_union"); - break; - } - if (f.return_roots) { - AllocaInst *return_roots = emit_static_alloca(ctx, ArrayType::get(ctx.types().T_prjlvalue, f.return_roots)); - setName(ctx.emission_context, return_roots, "return_roots"); - args[idx] = return_roots; - idx++; - } - bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); - if (gcstack_arg) { - args[idx] = ctx.pgcstack; - idx++; - } + SmallVector argv(nargs); bool is_opaque_closure = jl_is_method(lam->def.value) && lam->def.method->is_for_opaque_closure; - for (size_t i = 0; i < jl_nparams(lam->specTypes) && idx < nfargs; ++i) { + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + for (size_t i = 0; i < nargs; ++i) { jl_value_t *ty = ((i == 0) && is_opaque_closure) ? (jl_value_t*)jl_any_type : jl_nth_slot_type(lam->specTypes, i); - // n.b. specTypes is required to be a datatype by construction for specsig - bool isboxed = deserves_argbox(ty); - Type *lty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, ty); - if (type_is_ghost(lty) || is_uniquerep_Type(ty)) - continue; Value *theArg; if (i == 0) { - // This function adapts from generic jlcall to OC specsig. Generic jlcall pointers - // come in as ::Tracked, but specsig expected ::Derived. - if (is_opaque_closure) - theArg = decay_derived(ctx, funcArg); - else - theArg = funcArg; + theArg = funcArg; } else { Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); theArg = ai.decorateInst(maybe_mark_load_dereferenceable( ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), false, ty)); } - if (!isboxed) { - theArg = decay_derived(ctx, theArg); - if (!lty->isAggregateType()) // keep "aggregate" type values in place as pointers - theArg = ctx.builder.CreateAlignedLoad(lty, theArg, Align(julia_alignment(ty))); - } - assert(!isa(theArg)); - args[idx] = theArg; - idx++; + argv[i] = mark_julia_type(ctx, theArg, true, ty); } - CallInst *call = ctx.builder.CreateCall(f.decl, args); - call->setAttributes(f.attrs); - if (gcstack_arg) - call->setCallingConv(CallingConv::Swift); - jl_cgval_t retval; + jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, jlretty, f, nullptr, argv, nargs); if (retarg != -1) { Value *theArg; if (retarg == 0) @@ -7847,34 +7690,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret Align(sizeof(void*))); retval = mark_julia_type(ctx, theArg, true, jl_any_type); } - else { - switch (f.cc) { - case jl_returninfo_t::Boxed: - retval = mark_julia_type(ctx, call, true, jlretty); - break; - case jl_returninfo_t::Register: - retval = mark_julia_type(ctx, call, false, jlretty); - break; - case jl_returninfo_t::SRet: - retval = mark_julia_slot(result, jlretty, NULL, ctx.tbaa().tbaa_stack); - break; - case jl_returninfo_t::Union: - // result is technically not right here, but `boxed` will only look at it - // for the unboxed values, so it's ok. - retval = mark_julia_slot(result, - jlretty, - ctx.builder.CreateExtractValue(call, 1), - ctx.tbaa().tbaa_stack); - retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); - assert(retval.Vboxed->getType() == ctx.types().T_prjlvalue); - break; - case jl_returninfo_t::Ghosts: - retval = mark_julia_slot(NULL, jlretty, call, ctx.tbaa().tbaa_stack); - break; - } - } ctx.builder.CreateRet(boxed(ctx, retval)); - return w; } 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, size_t *arg_offset) @@ -7986,22 +7802,21 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value bool isboxed = false; Type *ty = NULL; if (i == 0 && is_opaque_closure) { - ty = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); - isboxed = true; // true-ish anyway - we might not have the type tag + ty = nullptr; // special token to avoid computing this unnecessarily } else { if (is_uniquerep_Type(jt)) continue; isboxed = deserves_argbox(jt); ty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); + if (type_is_ghost(ty)) + continue; } - if (type_is_ghost(ty)) - continue; AttrBuilder param(ctx.builder.getContext()); - if (ty->isAggregateType()) { // aggregate types are passed by pointer + if (ty == nullptr || ty->isAggregateType()) { // aggregate types are passed by pointer param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::ReadOnly); - ty = PointerType::get(ty, AddressSpace::Derived); + ty = ctx.builder.getPtrTy(AddressSpace::Derived); } else if (isboxed && jl_is_immutable_datatype(jt)) { param.addAttribute(Attribute::ReadOnly); @@ -8351,7 +8166,8 @@ static jl_llvm_functions_t std::string wrapName; raw_string_ostream(wrapName) << "jfptr_" << ctx.name << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); declarations.functionObject = wrapName; - (void)gen_invoke_wrapper(lam, jlrettype, returninfo, retarg, declarations.functionObject, M, ctx.emission_context); + size_t nparams = jl_nparams(lam->specTypes); + gen_invoke_wrapper(lam, jlrettype, returninfo, nparams, retarg, declarations.functionObject, M, ctx.emission_context); // TODO: add attributes: maybe_mark_argument_dereferenceable(Arg, argType) // TODO: add attributes: dereferenceable // TODO: (if needsparams) add attributes: dereferenceable, readonly, nocapture From d7e417d6b8112cf90aa689ca6a00197729aeedb3 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:35:40 -0400 Subject: [PATCH 200/548] Fix "Various fixes to byte / bytearray search" (#55734) Fixes the conflict between #54593 and #54579 `_search` returns `nothing` instead of zero as a sentinal in #54579 --- base/strings/search.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/strings/search.jl b/base/strings/search.jl index 9bd69ae2f8a03..a481b3af775e0 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -178,7 +178,7 @@ function findall( i = firstindex(s) while true i = _search(s, byte, i) - iszero(i) && return result + isnothing(i) && return result i += 1 index = i - ncu # If the char is invalid, it's possible that its first byte is From bee75f73bbc4b295ff0899ac9aced852c6719364 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Wed, 11 Sep 2024 11:52:14 +0100 Subject: [PATCH 201/548] Fix `make binary-dist` when using `USE_BINARYBUILDER_LLVM=0` (#55731) `make binary-dist` expects lld to be in usr/tools but it ends up in usr/bin so I copied it into usr/tools. Should fix the scheduled source tests which currently fail at linking. I think this is also broken with `USE_BINARYBUILDER_LLVM=0` and `BUILD_LLD=0`, maybe https://github.com/JuliaLang/julia/commit/ceaeb7b71bc76afaca2f3b80998164a47e30ce33 is the fix? --------- Co-authored-by: Zentrik --- deps/llvm.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps/llvm.mk b/deps/llvm.mk index 08aff443dcff8..73697069a4fac 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -292,6 +292,9 @@ ifeq ($(OS),Darwin) # https://github.com/JuliaLang/julia/issues/29981 LLVM_INSTALL += && ln -s libLLVM.dylib $2$$(build_shlibdir)/libLLVM-$$(LLVM_VER_SHORT).dylib endif +ifeq ($(BUILD_LLD), 1) +LLVM_INSTALL += && cp $2$$(build_bindir)/lld$$(EXE) $2$$(build_depsbindir) +endif $(eval $(call staged-install, \ llvm,$$(LLVM_SRC_DIR)/build_$$(LLVM_BUILDTYPE), \ From 255162c7197e973d0427cc11d1e0117cdd76a1bf Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 11 Sep 2024 11:50:05 -0400 Subject: [PATCH 202/548] Precompile the `@time_imports` printing so it doesn't confuse reports (#55729) Makes functions for the report printing that can be precompiled into the sysimage. --- base/loading.jl | 94 +++++++++++++++++++++------------- contrib/generate_precompile.jl | 9 ++++ 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 4e70d2bc257ea..8d180845f942f 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1254,22 +1254,9 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No if parentmodule(M) === M && PkgId(M) == pkg register && register_root_module(M) if timing_imports - elapsed = round((time_ns() - t_before) / 1e6, digits = 1) + elapsed_time = time_ns() - t_before comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before - print(lpad(elapsed, 9), " ms ") - ext_parent = extension_parent_name(M) - if ext_parent !== nothing - print(ext_parent::String, " → ") - end - print(pkg.name) - if comp_time > 0 - printstyled(" ", Ryu.writefixed(Float64(100 * comp_time / (elapsed * 1e6)), 2), "% compilation time", color = Base.info_color()) - end - if recomp_time > 0 - perc = Float64(100 * recomp_time / comp_time) - printstyled(" (", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% recompilation)", color = Base.warn_color()) - end - println() + print_time_imports_report(M, elapsed_time, comp_time, recomp_time) end return M end @@ -1281,6 +1268,52 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No end end +# printing functions for @time_imports +# note that the time inputs are UInt64 on all platforms. Give default values here so that we don't have +# confusing UInt64 types in generate_precompile.jl +function print_time_imports_report( + mod::Module, + elapsed_time::UInt64=UInt64(1), + comp_time::UInt64=UInt64(1), + recomp_time::UInt64=UInt64(1) + ) + print(lpad(round(elapsed_time / 1e6, digits=1), 9), " ms ") + ext_parent = extension_parent_name(mod) + if ext_parent !== nothing + print(ext_parent::String, " → ") + end + print(string(mod)) + if comp_time > 0 + perc = Ryu.writefixed(Float64(100 * comp_time / (elapsed_time)), 2) + printstyled(" $perc% compilation time", color = Base.info_color()) + end + if recomp_time > 0 + perc = Float64(100 * recomp_time / comp_time) + perc_show = perc < 1 ? "<1" : Ryu.writefixed(perc, 0) + printstyled(" ($perc_show% recompilation)", color = Base.warn_color()) + end + println() +end +function print_time_imports_report_init( + mod::Module, i::Int=1, + elapsed_time::UInt64=UInt64(1), + comp_time::UInt64=UInt64(1), + recomp_time::UInt64=UInt64(1) + ) + connector = i > 1 ? "├" : "┌" + printstyled(" $connector ", color = :light_black) + print("$(round(elapsed_time / 1e6, digits=1)) ms $mod.__init__() ") + if comp_time > 0 + perc = Ryu.writefixed(Float64(100 * (comp_time) / elapsed_time), 2) + printstyled("$perc% compilation time", color = Base.info_color()) + end + if recomp_time > 0 + perc = Float64(100 * recomp_time / comp_time) + printstyled(" ($(perc < 1 ? "<1" : Ryu.writefixed(perc, 0))% recompilation)", color = Base.warn_color()) + end + println() +end + # if M is an extension, return the string name of the parent. Otherwise return nothing function extension_parent_name(M::Module) rootmodule = moduleroot(M) @@ -1338,31 +1371,18 @@ function run_module_init(mod::Module, i::Int=1) # `i` informs ordering for the `@time_imports` report formatting if TIMING_IMPORTS[] == 0 ccall(:jl_init_restored_module, Cvoid, (Any,), mod) - else - if isdefined(mod, :__init__) - connector = i > 1 ? "├" : "┌" - printstyled(" $connector ", color = :light_black) - - elapsedtime = time_ns() - cumulative_compile_timing(true) - compile_elapsedtimes = cumulative_compile_time_ns() + elseif isdefined(mod, :__init__) + elapsed_time = time_ns() + cumulative_compile_timing(true) + compile_elapsedtimes = cumulative_compile_time_ns() - ccall(:jl_init_restored_module, Cvoid, (Any,), mod) + ccall(:jl_init_restored_module, Cvoid, (Any,), mod) - elapsedtime = (time_ns() - elapsedtime) / 1e6 - cumulative_compile_timing(false); - comp_time, recomp_time = (cumulative_compile_time_ns() .- compile_elapsedtimes) ./ 1e6 + elapsed_time = time_ns() - elapsed_time + cumulative_compile_timing(false); + comp_time, recomp_time = cumulative_compile_time_ns() .- compile_elapsedtimes - print("$(round(elapsedtime, digits=1)) ms $mod.__init__() ") - if comp_time > 0 - printstyled(Ryu.writefixed(Float64(100 * comp_time / elapsedtime), 2), "% compilation time", color = Base.info_color()) - end - if recomp_time > 0 - perc = Float64(100 * recomp_time / comp_time) - printstyled(" ($(perc < 1 ? "<1" : Ryu.writefixed(perc, 0))% recompilation)", color = Base.warn_color()) - end - println() - end + print_time_imports_report_init(mod, i, elapsed_time, comp_time, recomp_time) end end diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 2a0e4faff7f1c..50ae560e99401 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -39,6 +39,8 @@ precompile(Base.__require_prelocked, (Base.PkgId, Nothing)) precompile(Base._require, (Base.PkgId, Nothing)) precompile(Base.indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int)) precompile(Base.indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int, Int)) +precompile(Tuple{typeof(Base.Threads.atomic_add!), Base.Threads.Atomic{Int}, Int}) +precompile(Tuple{typeof(Base.Threads.atomic_sub!), Base.Threads.Atomic{Int}, Int}) # Pkg loading precompile(Tuple{typeof(Base.Filesystem.normpath), String, String, Vararg{String}}) @@ -161,6 +163,8 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) push!(Expr[], Expr(:return, false)) vcat(String[], String[]) k, v = (:hello => nothing) + Base.print_time_imports_report(Base) + Base.print_time_imports_report_init(Base) # Preferences uses these get(Dict{String,Any}(), "missing", nothing) @@ -172,6 +176,11 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) # interactive startup uses this write(IOBuffer(), "") + # not critical, but helps hide unrelated compilation from @time when using --trace-compile + foo() = rand(2,2) * rand(2,2) + @time foo() + @time foo() + break # only actually need to do this once end """ From 1eabe90fa054abc74f541798ae4dfcc9445bb564 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 Sep 2024 14:04:10 -0400 Subject: [PATCH 203/548] codegen: some cleanup of layout computations (#55730) Change Alloca to take an explicit alignment, rather than relying on LLVM to guess our intended alignment from the DataLayout. Eventually we should try to change this code to just get all layout data from julia queries (jl_field_offset, julia_alignment, etc.) instead of relying on creating an LLVM element type for memory and inspecting it (CountTrackedPointers, DataLayout, and so on). --- src/ccall.cpp | 14 +++++--------- src/cgutils.cpp | 36 ++++++++++++++++-------------------- src/codegen.cpp | 34 +++++++++++++++------------------- src/intrinsics.cpp | 14 ++++++++------ 4 files changed, 44 insertions(+), 54 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index eac130ea43189..e336de8e3574f 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -446,15 +446,13 @@ static Value *llvm_type_rewrite( const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); Align align = std::max(DL.getPrefTypeAlign(target_type), DL.getPrefTypeAlign(from_type)); if (DL.getTypeAllocSize(target_type) >= DL.getTypeAllocSize(from_type)) { - to = emit_static_alloca(ctx, target_type); + to = emit_static_alloca(ctx, target_type, align); setName(ctx.emission_context, to, "type_rewrite_buffer"); - cast(to)->setAlignment(align); from = to; } else { - from = emit_static_alloca(ctx, from_type); + from = emit_static_alloca(ctx, from_type, align); setName(ctx.emission_context, from, "type_rewrite_buffer"); - cast(from)->setAlignment(align); to = from; } ctx.builder.CreateAlignedStore(v, from, align); @@ -555,9 +553,8 @@ static Value *julia_to_native( // pass the address of an alloca'd thing, not a box // since those are immutable. - Value *slot = emit_static_alloca(ctx, to); Align align(julia_alignment(jlto)); - cast(slot)->setAlignment(align); + Value *slot = emit_static_alloca(ctx, to, align); setName(ctx.emission_context, slot, "native_convert_buffer"); if (!jvinfo.ispointer()) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); @@ -2090,7 +2087,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (sret) { assert(!retboxed && jl_is_datatype(rt) && "sret return type invalid"); if (jl_is_pointerfree(rt)) { - result = emit_static_alloca(ctx, lrt); + result = emit_static_alloca(ctx, lrt, Align(julia_alignment(rt))); setName(ctx.emission_context, result, "ccall_sret"); sretty = lrt; argvals[0] = result; @@ -2266,9 +2263,8 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (DL.getTypeStoreSize(resultTy) > rtsz) { // ARM and AArch64 can use a LLVM type larger than the julia type. // When this happens, cast through memory. - auto slot = emit_static_alloca(ctx, resultTy); + auto slot = emit_static_alloca(ctx, resultTy, boxalign); setName(ctx.emission_context, slot, "type_pun_slot"); - slot->setAlignment(boxalign); ctx.builder.CreateAlignedStore(result, slot, boxalign); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); emit_memcpy(ctx, strct, ai, slot, ai, rtsz, boxalign, boxalign); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 2a234f399f5c1..bf5c67ae9f849 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1937,18 +1937,22 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j ctx.builder.CreateFence(Order); return ghostValue(ctx, jltype); } + if (isboxed) + alignment = sizeof(void*); + else if (!alignment) + alignment = julia_alignment(jltype); unsigned nb = isboxed ? sizeof(void*) : jl_datatype_size(jltype); // note that nb == jl_Module->getDataLayout().getTypeAllocSize(elty) or getTypeStoreSize, depending on whether it is a struct or primitive type AllocaInst *intcast = NULL; if (Order == AtomicOrdering::NotAtomic) { if (!isboxed && !aliasscope && elty->isAggregateType() && !CountTrackedPointers(elty).count) { - intcast = emit_static_alloca(ctx, elty); + intcast = emit_static_alloca(ctx, elty, Align(alignment)); setName(ctx.emission_context, intcast, "aggregate_load_box"); } } else { if (!isboxed && !elty->isIntOrPtrTy()) { - intcast = emit_static_alloca(ctx, elty); + intcast = emit_static_alloca(ctx, elty, Align(alignment)); setName(ctx.emission_context, intcast, "atomic_load_box"); elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); } @@ -1963,10 +1967,6 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j if (idx_0based) data = ctx.builder.CreateInBoundsGEP(elty, data, idx_0based); Value *instr = nullptr; - if (isboxed) - alignment = sizeof(void*); - else if (!alignment) - alignment = julia_alignment(jltype); if (intcast && Order == AtomicOrdering::NotAtomic) { emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, Align(alignment), intcast->getAlign()); } @@ -2053,6 +2053,10 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ret = update_julia_type(ctx, ret, jltype); return ret; }; + if (isboxed) + alignment = sizeof(void*); + else if (!alignment) + alignment = julia_alignment(jltype); Type *elty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jltype); if (type_is_ghost(elty) || (issetfieldonce && !maybe_null_if_boxed) || @@ -2095,7 +2099,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, intcast_eltyp = elty; elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); if (!issetfield) { - intcast = emit_static_alloca(ctx, elty); + intcast = emit_static_alloca(ctx, elty, Align(alignment)); setName(ctx.emission_context, intcast, "atomic_store_box"); } } @@ -2121,10 +2125,6 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (realelty != elty) r = ctx.builder.CreateZExt(r, elty); } - if (isboxed) - alignment = sizeof(void*); - else if (!alignment) - alignment = julia_alignment(jltype); Value *instr = nullptr; Value *Compare = nullptr; Value *Success = nullptr; @@ -2657,10 +2657,8 @@ static jl_cgval_t emit_unionload(jl_codectx_t &ctx, Value *addr, Value *ptindex, if (fsz > 0 && mutabl) { // move value to an immutable stack slot (excluding tindex) Type *AT = ArrayType::get(IntegerType::get(ctx.builder.getContext(), 8 * al), (fsz + al - 1) / al); - AllocaInst *lv = emit_static_alloca(ctx, AT); + AllocaInst *lv = emit_static_alloca(ctx, AT, Align(al)); setName(ctx.emission_context, lv, "immutable_union"); - if (al > 1) - lv->setAlignment(Align(al)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); emit_memcpy(ctx, lv, ai, addr, ai, fsz, Align(al), Align(al)); addr = lv; @@ -2903,7 +2901,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st unsigned st_idx = convert_struct_offset(ctx, T, byte_offset); IntegerType *ET = cast(T->getStructElementType(st_idx)); unsigned align = (ET->getBitWidth() + 7) / 8; - lv = emit_static_alloca(ctx, ET); + lv = emit_static_alloca(ctx, ET, Align(align)); lv->setOperand(0, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), (fsz + align - 1) / align)); // emit all of the align-sized words unsigned i = 0; @@ -3324,10 +3322,8 @@ static AllocaInst *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, // at least some of the values can live on the stack // try to pick an Integer type size such that SROA will emit reasonable code Type *AT = ArrayType::get(IntegerType::get(ctx.builder.getContext(), 8 * min_align), (nbytes + min_align - 1) / min_align); - AllocaInst *lv = emit_static_alloca(ctx, AT); + AllocaInst *lv = emit_static_alloca(ctx, AT, Align(align)); setName(ctx.emission_context, lv, "unionalloca"); - if (align > 1) - lv->setAlignment(Align(align)); return lv; } return NULL; @@ -3886,7 +3882,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } } else { - strct = emit_static_alloca(ctx, lt); + strct = emit_static_alloca(ctx, lt, Align(julia_alignment(ty))); setName(ctx.emission_context, strct, arg_typename); if (tracked.count) undef_derived_strct(ctx, strct, sty, ctx.tbaa().tbaa_stack); @@ -3966,7 +3962,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (fsz1 > 0 && !fval_info.isghost) { Type *ET = IntegerType::get(ctx.builder.getContext(), 8 * al); assert(lt->getStructElementType(llvm_idx) == ET); - AllocaInst *lv = emit_static_alloca(ctx, ET); + AllocaInst *lv = emit_static_alloca(ctx, ET, Align(al)); setName(ctx.emission_context, lv, "unioninit"); lv->setOperand(0, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), (fsz1 + al - 1) / al)); emit_unionmove(ctx, lv, ctx.tbaa().tbaa_stack, fval_info, nullptr); diff --git a/src/codegen.cpp b/src/codegen.cpp index a82056eb36e21..73a5f844b31da 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2208,10 +2208,10 @@ static GlobalVariable *get_pointer_to_constant(jl_codegen_params_t &emission_con return gv; } -static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, Type *lty) +static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, Type *lty, Align align) { ++EmittedAllocas; - return new AllocaInst(lty, ctx.topalloca->getModule()->getDataLayout().getAllocaAddrSpace(), "", /*InsertBefore=*/ctx.topalloca); + return new AllocaInst(lty, ctx.topalloca->getModule()->getDataLayout().getAllocaAddrSpace(), nullptr, align, "", /*InsertBefore=*/ctx.topalloca); } static void undef_derived_strct(jl_codectx_t &ctx, Value *ptr, jl_datatype_t *sty, MDNode *tbaa) @@ -2323,7 +2323,7 @@ static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_ loc = get_pointer_to_constant(ctx.emission_context, cast(v), Align(julia_alignment(typ)), "_j_const", *jl_Module); } else { - loc = emit_static_alloca(ctx, v->getType()); + loc = emit_static_alloca(ctx, v->getType(), Align(julia_alignment(typ))); ctx.builder.CreateStore(v, loc); } return mark_julia_slot(loc, typ, tindex, ctx.tbaa().tbaa_stack); @@ -2435,7 +2435,7 @@ static void alloc_def_flag(jl_codectx_t &ctx, jl_varinfo_t& vi) { assert((!vi.boxroot || vi.pTIndex) && "undef check is null pointer for boxed things"); if (vi.usedUndef) { - vi.defFlag = emit_static_alloca(ctx, getInt1Ty(ctx.builder.getContext())); + vi.defFlag = emit_static_alloca(ctx, getInt1Ty(ctx.builder.getContext()), Align(1)); setName(ctx.emission_context, vi.defFlag, "isdefined"); store_def_flag(ctx, vi, false); } @@ -5025,25 +5025,20 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos case jl_returninfo_t::Ghosts: break; case jl_returninfo_t::SRet: - result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType()); - #if JL_LLVM_VERSION < 170000 - assert(cast(result->getType())->hasSameElementTypeAs(cast(cft->getParamType(0)))); - #endif + result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType(), Align(julia_alignment(jlretty))); argvals[idx] = result; idx++; break; case jl_returninfo_t::Union: - result = emit_static_alloca(ctx, ArrayType::get(getInt8Ty(ctx.builder.getContext()), returninfo.union_bytes)); + result = emit_static_alloca(ctx, ArrayType::get(getInt8Ty(ctx.builder.getContext()), returninfo.union_bytes), Align(returninfo.union_align)); setName(ctx.emission_context, result, "sret_box"); - if (returninfo.union_align > 1) - result->setAlignment(Align(returninfo.union_align)); argvals[idx] = result; idx++; break; } if (returninfo.return_roots) { - AllocaInst *return_roots = emit_static_alloca(ctx, ArrayType::get(ctx.types().T_prjlvalue, returninfo.return_roots)); + AllocaInst *return_roots = emit_static_alloca(ctx, ArrayType::get(ctx.types().T_prjlvalue, returninfo.return_roots), Align(alignof(jl_value_t*))); argvals[idx] = return_roots; idx++; } @@ -5922,11 +5917,10 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) if (vtype->isAggregateType() && CountTrackedPointers(vtype).count == 0) { // the value will be moved into dest in the predecessor critical block. // here it's moved into phi in the successor (from dest) - dest = emit_static_alloca(ctx, vtype); - Value *phi = emit_static_alloca(ctx, vtype); - ctx.builder.CreateMemCpy(phi, Align(julia_alignment(phiType)), - dest, dest->getAlign(), - jl_datatype_size(phiType), false); + Align align(julia_alignment(phiType)); + dest = emit_static_alloca(ctx, vtype, align); + Value *phi = emit_static_alloca(ctx, vtype, align); + ctx.builder.CreateMemCpy(phi, align, dest, align, jl_datatype_size(phiType), false); ctx.builder.CreateLifetimeEnd(dest); slot = mark_julia_slot(phi, phiType, NULL, ctx.tbaa().tbaa_stack); } @@ -7737,6 +7731,8 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value if (tracked.count && !tracked.all) props.return_roots = tracked.count; props.cc = jl_returninfo_t::SRet; + props.union_bytes = jl_datatype_size(jlrettype); + props.union_align = props.union_minalign = jl_datatype_align(jlrettype); // sret is always passed from alloca assert(M); fsig.push_back(rt->getPointerTo(M->getDataLayout().getAllocaAddrSpace())); @@ -8365,13 +8361,13 @@ static jl_llvm_functions_t if (lv) { lv->setName(jl_symbol_name(s)); varinfo.value = mark_julia_slot(lv, jt, NULL, ctx.tbaa().tbaa_stack); - varinfo.pTIndex = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext())); + varinfo.pTIndex = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext()), Align(1)); setName(ctx.emission_context, varinfo.pTIndex, "tindex"); // TODO: attach debug metadata to this variable } else if (allunbox) { // all ghost values just need a selector allocated - AllocaInst *lv = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext())); + AllocaInst *lv = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext()), Align(1)); lv->setName(jl_symbol_name(s)); varinfo.pTIndex = lv; varinfo.value.tbaa = NULL; diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 194b45886bb0d..c747edfeffe5f 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -405,10 +405,11 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { assert(DL.getTypeSizeInBits(ty) == DL.getTypeSizeInBits(to)); - AllocaInst *cast = emit_static_alloca(ctx, ty); + Align align = std::max(DL.getPrefTypeAlign(ty), DL.getPrefTypeAlign(to)); + AllocaInst *cast = emit_static_alloca(ctx, ty, align); setName(ctx.emission_context, cast, "coercion"); - ctx.builder.CreateStore(unboxed, cast); - unboxed = ctx.builder.CreateLoad(to, cast); + ctx.builder.CreateAlignedStore(unboxed, cast, align); + unboxed = ctx.builder.CreateAlignedLoad(to, cast, align); } else if (frompointer) { Type *INTT_to = INTT(to, DL); @@ -692,10 +693,11 @@ static jl_cgval_t generic_cast( // understood that everything is implicitly rounded to 23 bits, // but if we start looking at more bits we need to actually do the // rounding first instead of carrying around incorrect low bits. - Value *jlfloattemp_var = emit_static_alloca(ctx, from->getType()); + Align align(julia_alignment((jl_value_t*)jlto)); + Value *jlfloattemp_var = emit_static_alloca(ctx, from->getType(), align); setName(ctx.emission_context, jlfloattemp_var, "rounding_slot"); - ctx.builder.CreateStore(from, jlfloattemp_var); - from = ctx.builder.CreateLoad(from->getType(), jlfloattemp_var, /*force this to load from the stack*/true); + ctx.builder.CreateAlignedStore(from, jlfloattemp_var, align); + from = ctx.builder.CreateAlignedLoad(from->getType(), jlfloattemp_var, align, /*force this to load from the stack*/true); setName(ctx.emission_context, from, "rounded"); } } From bf6962ca282831a29c6b4e8a2617e63fce623150 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 11 Sep 2024 19:25:01 -0400 Subject: [PATCH 204/548] Add some loading / LazyArtifacts precompiles to the sysimage (#55740) Fixes https://github.com/JuliaLang/julia/issues/55725 These help LazyArtifacts mainly but seem beneficial for the sysimage. --- contrib/generate_precompile.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 50ae560e99401..d3e73a1b1865a 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -42,6 +42,13 @@ precompile(Base.indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int, Int precompile(Tuple{typeof(Base.Threads.atomic_add!), Base.Threads.Atomic{Int}, Int}) precompile(Tuple{typeof(Base.Threads.atomic_sub!), Base.Threads.Atomic{Int}, Int}) +# LazyArtifacts (but more generally helpful) +precompile(Tuple{Type{Base.Val{x} where x}, Module}) +precompile(Tuple{Type{NamedTuple{(:honor_overrides,), T} where T<:Tuple}, Tuple{Bool}}) +precompile(Tuple{typeof(Base.unique!), Array{String, 1}}) +precompile(Tuple{typeof(Base.invokelatest), Any}) +precompile(Tuple{typeof(Base.vcat), Array{String, 1}, Array{String, 1}}) + # Pkg loading precompile(Tuple{typeof(Base.Filesystem.normpath), String, String, Vararg{String}}) precompile(Tuple{typeof(Base.append!), Array{String, 1}, Array{String, 1}}) From 22eded8bbfeb6557c012e4b78c3c69c993d0d4e9 Mon Sep 17 00:00:00 2001 From: Gabriele Bozzola Date: Wed, 11 Sep 2024 21:14:37 -0700 Subject: [PATCH 205/548] Update stable version number in readme to v1.10.5 (#55742) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd9e9b9c0bd02..465adcf049922 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.10.4 + git checkout v1.10.5 To build the `julia` executable, run `make` from within the julia directory. From 945517ba4e15f7470b8790a696ba5404ef047f2f Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 12 Sep 2024 03:41:49 -0400 Subject: [PATCH 206/548] Add `invokelatest` barrier to `string(...)` in `@assert` (#55739) This change protects `@assert` from invalidations to `Base.string(...)` by adding an `invokelatest` barrier. A common source of invalidations right now is `print(io, join(args...))`. The problem is: 1. Inference concludes that `join(::Any...)` returns `Union{String,AnnotatedString}` 2. The `print` call is union-split to `String` and `AnnotatedString` 3. This code is now invalidated when StyledStrings defines `print(io, ::AnnotatedString)` The invalidation chain for `@assert` is similar: ` @assert 1 == 1` calls into `string(::Expr)` which calls into `print(io, join(args::Any...))`. Unfortunately that leads to the invalidation of almost all `@assert`s without an explicit error message Similar to https://github.com/JuliaLang/julia/pull/55583#issuecomment-2308969806 --- base/error.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/error.jl b/base/error.jl index d169cdc8085ac..ee533cee0b57d 100644 --- a/base/error.jl +++ b/base/error.jl @@ -232,12 +232,12 @@ macro assert(ex, msgs...) msg = msg # pass-through elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol)) # message is an expression needing evaluating - msg = :(Main.Base.string($(esc(msg)))) + msg = :(Main.Base.invokelatest(Main.Base.string, $(esc(msg)))) elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg) msg = Main.Base.string(msg) else # string() might not be defined during bootstrap - msg = :(_assert_tostring($(Expr(:quote,msg)))) + msg = :(Main.Base.invokelatest(_assert_tostring, $(Expr(:quote,msg)))) end return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end From 8a9f384d594878e58dd46ce30a42ba03e50ce824 Mon Sep 17 00:00:00 2001 From: Christian Guinard <28689358+christiangnrd@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:59:20 -0300 Subject: [PATCH 207/548] Don't show string concatenation error hint with zero arg `+` (#55749) Closes #55745 --- base/errorshow.jl | 2 +- test/errorshow.jl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index a3bf464439d44..d805cb64fb81e 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -1067,7 +1067,7 @@ Experimental.register_error_hint(nonsetable_type_hint_handler, MethodError) # (probably attempting concatenation) function string_concatenation_hint_handler(io, ex, arg_types, kwargs) @nospecialize - if (ex.f === +) && all(i -> i <: AbstractString, arg_types) + if (ex.f === +) && !isempty(arg_types) && all(i -> i <: AbstractString, arg_types) print(io, "\nString concatenation is performed with ") printstyled(io, "*", color=:cyan) print(io, " (See also: https://docs.julialang.org/en/v1/manual/strings/#man-concatenation).") diff --git a/test/errorshow.jl b/test/errorshow.jl index 80352ddeaa9cf..a82ab7743dc5a 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -1079,6 +1079,12 @@ let err_str @test occursin("String concatenation is performed with *", err_str) end +# https://github.com/JuliaLang/julia/issues/55745 +let err_str + err_str = @except_str +() MethodError + @test !occursin("String concatenation is performed with *", err_str) +end + struct MissingLength; end struct MissingSize; end Base.IteratorSize(::Type{MissingSize}) = Base.HasShape{2}() From 76428563cdccf34aa030dffe1303bff8e12d742c Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Thu, 12 Sep 2024 20:17:17 +0100 Subject: [PATCH 208/548] Don't leave trailing whitespace when printing do-block expr (#55738) Before, when printing a `do`-block, we'd print a white-space after `do` even if no arguments follow. Now we don't print that space. --------- Co-authored-by: Lilith Orion Hafner --- base/show.jl | 8 ++++++-- test/show.jl | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/base/show.jl b/base/show.jl index 0a2976e7ebe42..ec6776d81f2d5 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2196,8 +2196,12 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In elseif head === :do && nargs == 2 iob = IOContext(io, beginsym=>false) show_unquoted(iob, args[1], indent, -1, quote_level) - print(io, " do ") - show_list(iob, (((args[2]::Expr).args[1])::Expr).args, ", ", 0, 0, quote_level) + print(io, " do") + do_args = (((args[2]::Expr).args[1])::Expr).args + if !isempty(do_args) + print(io, ' ') + show_list(iob, do_args, ", ", 0, 0, quote_level) + end for stmt in (((args[2]::Expr).args[2])::Expr).args print(io, '\n', " "^(indent + indent_width)) show_unquoted(iob, stmt, indent + indent_width, -1, quote_level) diff --git a/test/show.jl b/test/show.jl index 65c65111606c5..d9c3585b7c1df 100644 --- a/test/show.jl +++ b/test/show.jl @@ -2768,3 +2768,8 @@ let topmi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()); topmi.def = Main @test contains(repr(topmi), "Toplevel MethodInstance") end + +@testset "show() no trailing whitespace" begin + do_expr1 = :(foo() do; bar(); end) + @test !contains(sprint(show, do_expr1), " \n") +end From 4079648edf487626cc56cc8e6a518e67343614a7 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 12 Sep 2024 16:52:47 -0300 Subject: [PATCH 209/548] Don't pass lSystem to the linker since macos always links it (#55722) This stops it complaing about duplicated libs. For libunwind there isn't much we can do because it's part of lsystem and we also need out own. --- cli/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/Makefile b/cli/Makefile index 7b8d3587f5386..3cc0af1a76afd 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -25,8 +25,6 @@ else ifeq ($(OS),FreeBSD) LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed else ifeq ($(OS),OpenBSD) LOADER_LDFLAGS += -Wl,--no-as-needed -lpthread -rdynamic -lc -Wl,--as-needed -else ifeq ($(OS),Darwin) -LOADER_LDFLAGS += -lSystem endif # Build list of dependent libraries that must be opened From e52a46c5c192bfe16853ae0b63ac33b280fba063 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 12 Sep 2024 23:02:46 +0200 Subject: [PATCH 210/548] define `numerator` and `denominator` for `Complex` (#55694) Fixes #55693 --- base/rational.jl | 11 +++++++++-- test/rational.jl | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/base/rational.jl b/base/rational.jl index fb1824acb6b31..b4e450fd73abc 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -293,8 +293,14 @@ julia> numerator(4) 4 ``` """ -numerator(x::Integer) = x +numerator(x::Union{Integer,Complex{<:Integer}}) = x numerator(x::Rational) = x.num +function numerator(z::Complex{<:Rational}) + den = denominator(z) + reim = (real(z), imag(z)) + result = checked_mul.(numerator.(reim), div.(den, denominator.(reim))) + complex(result...) +end """ denominator(x) @@ -310,8 +316,9 @@ julia> denominator(4) 1 ``` """ -denominator(x::Integer) = one(x) +denominator(x::Union{Integer,Complex{<:Integer}}) = one(x) denominator(x::Rational) = x.den +denominator(z::Complex{<:Rational}) = lcm(denominator(real(z)), denominator(imag(z))) sign(x::Rational) = oftype(x, sign(x.num)) signbit(x::Rational) = signbit(x.num) diff --git a/test/rational.jl b/test/rational.jl index c6f81372de0b9..20a0971068876 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -801,3 +801,20 @@ end @test rationalize(Int64, nextfloat(0.1) * im; tol=0) == precise_next * im @test rationalize(0.1im; tol=eps(0.1)) == rationalize(0.1im) end + +@testset "complex numerator, denominator" begin + z = complex(3*3, 2*3*5) + @test z === numerator(z) === numerator(z // 2) === numerator(z // 5) + @test complex(3, 2*5) === numerator(z // 3) + @test isone(denominator(z)) + @test 2 === denominator(z // 2) + @test 1 === denominator(z // 3) + @test 5 === denominator(z // 5) + for den ∈ 1:10 + q = z // den + @test q === (numerator(q)//denominator(q)) + end + @testset "do not overflow silently" begin + @test_throws OverflowError numerator(Int8(1)//Int8(31) + Int8(8)im//Int8(3)) + end +end From 94f8a3d6723c61df052a431769a1726fd27a8cc7 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 13 Sep 2024 08:17:21 -0400 Subject: [PATCH 211/548] More testsets for SubString and a few missing tests (#55656) Co-authored-by: Simeon David Schaub --- test/strings/types.jl | 527 ++++++++++++++++++++++-------------------- 1 file changed, 271 insertions(+), 256 deletions(-) diff --git a/test/strings/types.jl b/test/strings/types.jl index d1e89e0e85196..c09652c3a608d 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -2,196 +2,211 @@ ## SubString and Cstring tests ## -## SubString tests ## -u8str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" -u8str2 = u8str^2 -len_u8str = length(u8str) -slen_u8str = length(u8str) -len_u8str2 = length(u8str2) -slen_u8str2 = length(u8str2) - -@test len_u8str2 == 2 * len_u8str -@test slen_u8str2 == 2 * slen_u8str - -u8str2plain = String(u8str2) - -for i1 = 1:length(u8str2) - if !isvalid(u8str2, i1); continue; end - for i2 = i1:length(u8str2) - if !isvalid(u8str2, i2); continue; end - @test length(u8str2[i1:i2]) == length(u8str2plain[i1:i2]) - @test length(u8str2[i1:i2]) == length(u8str2plain[i1:i2]) - @test u8str2[i1:i2] == u8str2plain[i1:i2] +@testset "SubString" begin + u8str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" + u8str2 = u8str^2 + len_u8str = length(u8str) + slen_u8str = length(u8str) + len_u8str2 = length(u8str2) + slen_u8str2 = length(u8str2) + + @test len_u8str2 == 2 * len_u8str + @test slen_u8str2 == 2 * slen_u8str + + u8str2plain = String(u8str2) + @test !isascii(u8str2) + @test cmp(u8str2, u8str^3) == -1 + @test cmp(u8str2, u8str2) == 0 + @test cmp(u8str^3, u8str2) == 1 + @test codeunit(u8str2) == codeunit(u8str2plain) + + @test convert(Union{String, SubString{String}}, u8str2) === u8str2 + @test convert(Union{String, SubString{String}}, u8str2plain) === u8str2plain + + for i1 = 1:ncodeunits(u8str2) + if !isvalid(u8str2, i1); continue; end + for i2 = i1:ncodeunits(u8str2) + if !isvalid(u8str2, i2); continue; end + @test length(u8str2[i1:i2]) == length(u8str2plain[i1:i2]) + @test length(u8str2[i1:i2]) == length(u8str2plain[i1:i2]) + @test u8str2[i1:i2] == u8str2plain[i1:i2] + end end -end -# tests that SubString of a single multibyte `Char` string, like "∀" which takes 3 bytes -# gives the same result as `getindex` (except that it is a view not a copy) -for idx in 0:1 - @test SubString("∀", 1, idx) == "∀"[1:idx] -end + # tests that SubString of a single multibyte `Char` string, like "∀" which takes 3 bytes + # gives the same result as `getindex` (except that it is a view not a copy) + for idx in 0:1 + @test SubString("∀", 1, idx) == "∀"[1:idx] + end -# Substring provided with invalid end index throws BoundsError -@test_throws StringIndexError SubString("∀", 1, 2) -@test_throws StringIndexError SubString("∀", 1, 3) -@test_throws BoundsError SubString("∀", 1, 4) - -# Substring provided with invalid start index throws BoundsError -@test SubString("∀∀", 1:1) == "∀" -@test SubString("∀∀", 1:4) == "∀∀" -@test SubString("∀∀", 4:4) == "∀" -@test_throws StringIndexError SubString("∀∀", 1:2) -@test_throws StringIndexError SubString("∀∀", 1:5) -@test_throws StringIndexError SubString("∀∀", 2:4) -@test_throws BoundsError SubString("∀∀", 0:1) -@test_throws BoundsError SubString("∀∀", 0:4) -@test_throws BoundsError SubString("∀∀", 1:7) -@test_throws BoundsError SubString("∀∀", 4:7) - -# tests for SubString of more than one multibyte `Char` string -# we are consistent with `getindex` for `String` -for idx in [0, 1, 4] - @test SubString("∀∀", 1, idx) == "∀∀"[1:idx] - @test SubString("∀∀", 4, idx) == "∀∀"[4:idx] -end + @testset "invalid end index" begin + # Substring provided with invalid end index throws BoundsError + @test_throws StringIndexError SubString("∀", 1, 2) + @test_throws StringIndexError SubString("∀", 1, 3) + @test_throws BoundsError SubString("∀", 1, 4) + end -# index beyond lastindex("∀∀") -for idx in [2:3; 5:6] - @test_throws StringIndexError SubString("∀∀", 1, idx) -end -for idx in 7:8 - @test_throws BoundsError SubString("∀∀", 1, idx) -end + @testset "invalid start index" begin + # Substring provided with invalid start index throws BoundsError + @test SubString("∀∀", 1:1) == "∀" + @test SubString("∀∀", 1:4) == "∀∀" + @test SubString("∀∀", 4:4) == "∀" + @test_throws StringIndexError SubString("∀∀", 1:2) + @test_throws StringIndexError SubString("∀∀", 1:5) + @test_throws StringIndexError SubString("∀∀", 2:4) + @test_throws BoundsError SubString("∀∀", 0:1) + @test_throws BoundsError SubString("∀∀", 0:4) + @test_throws BoundsError SubString("∀∀", 1:7) + @test_throws BoundsError SubString("∀∀", 4:7) + end + + # tests for SubString of more than one multibyte `Char` string + # we are consistent with `getindex` for `String` + for idx in [0, 1, 4] + @test SubString("∀∀", 1, idx) == "∀∀"[1:idx] + @test SubString("∀∀", 4, idx) == "∀∀"[4:idx] + end -let str="tempus fugit" #length(str)==12 - ss=SubString(str,1,lastindex(str)) #match source string - @test length(ss)==length(str) + @testset "index beyond lastindex(\"∀∀\")" begin + for idx in [2:3; 5:6] + @test_throws StringIndexError SubString("∀∀", 1, idx) + end + for idx in 7:8 + @test_throws BoundsError SubString("∀∀", 1, idx) + end + end - ss=SubString(str,1:lastindex(str)) - @test length(ss)==length(str) + let str="tempus fugit" #length(str)==12 + ss=SubString(str,1,lastindex(str)) #match source string + @test length(ss)==length(str) - ss=SubString(str,1,0) #empty SubString - @test length(ss)==0 + ss=SubString(str,1:lastindex(str)) + @test length(ss)==length(str) - ss=SubString(str,1:0) - @test length(ss)==0 + ss=SubString(str,1,0) #empty SubString + @test length(ss)==0 - @test_throws BoundsError SubString(str, 14, 20) #start indexing beyond source string length - @test_throws BoundsError SubString(str, 10, 16) #end indexing beyond source string length + ss=SubString(str,1:0) + @test length(ss)==0 - @test_throws BoundsError SubString("", 1, 4) #empty source string - @test_throws BoundsError SubString("", 1, 1) #empty source string, identical start and end index - @test_throws BoundsError SubString("", 10, 12) - @test SubString("", 12, 10) == "" -end + @test_throws BoundsError SubString(str, 14, 20) #start indexing beyond source string length + @test_throws BoundsError SubString(str, 10, 16) #end indexing beyond source string length -@test SubString("foobar", big(1), big(3)) == "foo" - -let str = "aa\u2200\u2222bb" - u = SubString(str, 3, 6) - @test length(u) == 2 - b = IOBuffer() - write(b, u) - @test String(take!(b)) == "\u2200\u2222" - - @test_throws StringIndexError SubString(str, 4, 5) - @test_throws BoundsError iterate(u, 0) - @test_throws BoundsError iterate(u, 8) - @test_throws BoundsError getindex(u, 0) - @test_throws BoundsError getindex(u, 7) - @test_throws BoundsError getindex(u, 0:1) - @test_throws BoundsError getindex(u, 7:7) - @test reverseind(u, 1) == 4 - @test typeof(Base.cconvert(Ptr{Int8}, u)) == SubString{String} - @test Base.cconvert(Ptr{Int8}, u) == u -end + @test_throws BoundsError SubString("", 1, 4) #empty source string + @test_throws BoundsError SubString("", 1, 1) #empty source string, identical start and end index + @test_throws BoundsError SubString("", 10, 12) + @test SubString("", 12, 10) == "" + end -let str = "føøbar" - @test_throws BoundsError SubString(str, 10, 10) - u = SubString(str, 4, 3) - @test length(u) == 0 - b = IOBuffer() - write(b, u) - @test String(take!(b)) == "" -end + @test SubString("foobar", big(1), big(3)) == "foo" + + let str = "aa\u2200\u2222bb" + u = SubString(str, 3, 6) + @test length(u) == 2 + b = IOBuffer() + write(b, u) + @test String(take!(b)) == "\u2200\u2222" + + @test_throws StringIndexError SubString(str, 4, 5) + @test_throws BoundsError iterate(u, 0) + @test_throws BoundsError iterate(u, 8) + @test_throws BoundsError getindex(u, 0) + @test_throws BoundsError getindex(u, 7) + @test_throws BoundsError getindex(u, 0:1) + @test_throws BoundsError getindex(u, 7:7) + @test reverseind(u, 1) == 4 + @test typeof(Base.cconvert(Ptr{Int8}, u)) == SubString{String} + @test Base.cconvert(Ptr{Int8}, u) == u + end -# search and SubString (issue #5679) -let str = "Hello, world!" - u = SubString(str, 1, 5) - @test findlast("World", u) === nothing - @test findlast(isequal('z'), u) === nothing - @test findlast("ll", u) == 3:4 -end + let str = "føøbar" + @test_throws BoundsError SubString(str, 10, 10) + u = SubString(str, 4, 3) + @test length(u) == 0 + b = IOBuffer() + write(b, u) + @test String(take!(b)) == "" + end -# SubString created from SubString -let str = "Hello, world!" - u = SubString(str, 2, 5) - for idx in 1:4 - @test SubString(u, 2, idx) == u[2:idx] - @test SubString(u, 2:idx) == u[2:idx] + @testset "search and SubString (issue #5679)" begin + str = "Hello, world!" + u = SubString(str, 1, 5) + @test findlast("World", u) === nothing + @test findlast(isequal('z'), u) === nothing + @test findlast("ll", u) == 3:4 end - @test_throws BoundsError SubString(u, 1, 10) - @test_throws BoundsError SubString(u, 1:10) - @test_throws BoundsError SubString(u, 20:30) - @test SubString(u, 20:15) == "" - @test_throws BoundsError SubString(u, -1:10) - @test SubString(u, -1, -10) == "" - @test SubString(SubString("123", 1, 2), -10, -20) == "" -end -# sizeof -@test sizeof(SubString("abc\u2222def",4,4)) == 3 - -# issue #3710 -@test prevind(SubString("{var}",2,4),4) == 3 - -# issue #4183 -@test split(SubString("x", 2, 0), "y") == [""] - -# issue #6772 -@test parse(Float64, SubString("10",1,1)) === 1.0 -@test parse(Float64, SubString("1 0",1,1)) === 1.0 -@test parse(Float32, SubString("10",1,1)) === 1.0f0 - -# issue #5870 -@test !occursin(Regex("aa"), SubString("",1,0)) -@test occursin(Regex(""), SubString("",1,0)) - -# isvalid, length, prevind, nextind for SubString{String} -let s = "lorem ipsum", sdict = Dict( - SubString(s, 1, 11) => "lorem ipsum", - SubString(s, 1, 6) => "lorem ", - SubString(s, 1, 0) => "", - SubString(s, 2, 4) => "ore", - SubString(s, 2, 11) => "orem ipsum", - SubString(s, 15, 14) => "", -) - for (ss, s) in sdict - @test ncodeunits(ss) == ncodeunits(s) - for i in -2:13 - @test isvalid(ss, i) == isvalid(s, i) - end - for i in 1:ncodeunits(ss), j = i-1:ncodeunits(ss) - @test length(ss, i, j) == length(s, i, j) + @testset "SubString created from SubString" begin + str = "Hello, world!" + u = SubString(str, 2, 5) + for idx in 1:4 + @test SubString(u, 2, idx) == u[2:idx] + @test SubString(u, 2:idx) == u[2:idx] end + @test_throws BoundsError SubString(u, 1, 10) + @test_throws BoundsError SubString(u, 1:10) + @test_throws BoundsError SubString(u, 20:30) + @test SubString(u, 20:15) == "" + @test_throws BoundsError SubString(u, -1:10) + @test SubString(u, -1, -10) == "" + @test SubString(SubString("123", 1, 2), -10, -20) == "" + end + + # sizeof + @test sizeof(SubString("abc\u2222def",4,4)) == 3 + + # issue #3710 + @test prevind(SubString("{var}",2,4),4) == 3 + + # issue #4183 + @test split(SubString("x", 2, 0), "y") == [""] + + @testset "issue #6772" begin + @test parse(Float64, SubString("10",1,1)) === 1.0 + @test parse(Float64, SubString("1 0",1,1)) === 1.0 + @test parse(Float32, SubString("10",1,1)) === 1.0f0 + end + + @testset "issue #5870" begin + @test !occursin(Regex("aa"), SubString("",1,0)) + @test occursin(Regex(""), SubString("",1,0)) end - for (ss, s) in sdict - @test length(ss) == length(s) - for i in 0:ncodeunits(ss), j = 0:length(ss)+1 - @test prevind(ss, i+1, j) == prevind(s, i+1, j) - @test nextind(ss, i, j) == nextind(s, i, j) + @testset" isvalid, length, prevind, nextind for SubString{String}" begin + s = "lorem ipsum" + sdict = Dict( + SubString(s, 1, 11) => "lorem ipsum", + SubString(s, 1, 6) => "lorem ", + SubString(s, 1, 0) => "", + SubString(s, 2, 4) => "ore", + SubString(s, 2, 11) => "orem ipsum", + SubString(s, 15, 14) => "", + ) + for (ss, s) in sdict + @test ncodeunits(ss) == ncodeunits(s) + for i in -2:13 + @test isvalid(ss, i) == isvalid(s, i) + end + for i in 1:ncodeunits(ss), j = i-1:ncodeunits(ss) + @test length(ss, i, j) == length(s, i, j) + end + end + for (ss, s) in sdict + @test length(ss) == length(s) + for i in 0:ncodeunits(ss), j = 0:length(ss)+1 + @test prevind(ss, i+1, j) == prevind(s, i+1, j) + @test nextind(ss, i, j) == nextind(s, i, j) + end + @test_throws BoundsError prevind(s, 0) + @test_throws BoundsError prevind(ss, 0) + @test_throws BoundsError nextind(s, ncodeunits(ss)+1) + @test_throws BoundsError nextind(ss, ncodeunits(ss)+1) end - @test_throws BoundsError prevind(s, 0) - @test_throws BoundsError prevind(ss, 0) - @test_throws BoundsError nextind(s, ncodeunits(ss)+1) - @test_throws BoundsError nextind(ss, ncodeunits(ss)+1) end -end -# proper nextind/prevind/thisind for SubString{String} -let rng = MersenneTwister(1), strs = ["∀∃∀"*String(rand(rng, UInt8, 40))*"∀∃∀", + rng = MersenneTwister(1) + strs = ["∀∃∀"*String(rand(rng, UInt8, 40))*"∀∃∀", String(rand(rng, UInt8, 50))] - for s in strs + @testset "proper nextind/prevind/thisind for SubString{String}: $(repr(s))" for s in strs a = 0 while a <= ncodeunits(s) a = nextind(s, a) @@ -223,111 +238,111 @@ let rng = MersenneTwister(1), strs = ["∀∃∀"*String(rand(rng, UInt8, 40))*" end end end -end -# for isvalid(SubString{String}) -let s = "Σx + βz - 2" - for i in -1:ncodeunits(s)+2 - if checkbounds(Bool, s, i) - if isvalid(s, i) - ss = SubString(s, 1, i) - for j = 1:ncodeunits(ss) - @test isvalid(ss, j) == isvalid(s, j) + # for isvalid(SubString{String}) + let s = "Σx + βz - 2" + for i in -1:ncodeunits(s)+2 + if checkbounds(Bool, s, i) + if isvalid(s, i) + ss = SubString(s, 1, i) + for j = 1:ncodeunits(ss) + @test isvalid(ss, j) == isvalid(s, j) + end + else + @test_throws StringIndexError SubString(s, 1, i) end + elseif i > 0 + @test_throws BoundsError SubString(s, 1, i) else - @test_throws StringIndexError SubString(s, 1, i) + @test SubString(s, 1, i) == "" end - elseif i > 0 - @test_throws BoundsError SubString(s, 1, i) - else - @test SubString(s, 1, i) == "" end end -end -let ss = SubString("hello", 1, 5) - @test length(ss, 1, 0) == 0 - @test_throws BoundsError length(ss, 1, -1) - @test_throws BoundsError length(ss, 1, 6) - @test_throws BoundsError length(ss, 1, 10) - @test_throws BoundsError prevind(ss, 0, 1) - @test prevind(ss, 1, 1) == 0 - @test prevind(ss, 6, 1) == 5 - @test_throws BoundsError prevind(ss, 7, 1) - @test_throws BoundsError nextind(ss, -1, 1) - @test nextind(ss, 0, 1) == 1 - @test nextind(ss, 5, 1) == 6 - @test_throws BoundsError nextind(ss, 6, 1) -end + let ss = SubString("hello", 1, 5) + @test length(ss, 1, 0) == 0 + @test_throws BoundsError length(ss, 1, -1) + @test_throws BoundsError length(ss, 1, 6) + @test_throws BoundsError length(ss, 1, 10) + @test_throws BoundsError prevind(ss, 0, 1) + @test prevind(ss, 1, 1) == 0 + @test prevind(ss, 6, 1) == 5 + @test_throws BoundsError prevind(ss, 7, 1) + @test_throws BoundsError nextind(ss, -1, 1) + @test nextind(ss, 0, 1) == 1 + @test nextind(ss, 5, 1) == 6 + @test_throws BoundsError nextind(ss, 6, 1) + end -# length(SubString{String}) performance specialization -let s = "|η(α)-ϕ(κ)| < ε" - @test length(SubString(s, 1, 0)) == length(s[1:0]) - @test length(SubString(s, 4, 4)) == length(s[4:4]) - @test length(SubString(s, 1, 7)) == length(s[1:7]) - @test length(SubString(s, 4, 11)) == length(s[4:11]) -end + # length(SubString{String}) performance specialization + let s = "|η(α)-ϕ(κ)| < ε" + @test length(SubString(s, 1, 0)) == length(s[1:0]) + @test length(SubString(s, 4, 4)) == length(s[4:4]) + @test length(SubString(s, 1, 7)) == length(s[1:7]) + @test length(SubString(s, 4, 11)) == length(s[4:11]) + end -@testset "reverseind" for T in (String, SubString, GenericString) - for prefix in ("", "abcd", "\U0001d6a4\U0001d4c1", "\U0001d6a4\U0001d4c1c", " \U0001d6a4\U0001d4c1") - for suffix in ("", "abcde", "\U0001d4c1β\U0001d6a4", "\U0001d4c1β\U0001d6a4c", " \U0001d4c1β\U0001d6a4") - for c in ('X', 'δ', '\U0001d6a5') - s = convert(T, string(prefix, c, suffix)) - r = reverse(s) - ri = findfirst(isequal(c), r) - @test c == s[reverseind(s, ri)] == r[ri] - s = convert(T, string(prefix, prefix, c, suffix, suffix)) - pre = convert(T, prefix) - sb = SubString(s, nextind(pre, lastindex(pre)), - lastindex(convert(T, string(prefix, prefix, c, suffix)))) - r = reverse(sb) - ri = findfirst(isequal(c), r) - @test c == sb[reverseind(sb, ri)] == r[ri] + @testset "reverseind" for T in (String, SubString, GenericString) + for prefix in ("", "abcd", "\U0001d6a4\U0001d4c1", "\U0001d6a4\U0001d4c1c", " \U0001d6a4\U0001d4c1") + for suffix in ("", "abcde", "\U0001d4c1β\U0001d6a4", "\U0001d4c1β\U0001d6a4c", " \U0001d4c1β\U0001d6a4") + for c in ('X', 'δ', '\U0001d6a5') + s = convert(T, string(prefix, c, suffix)) + r = reverse(s) + ri = findfirst(isequal(c), r) + @test c == s[reverseind(s, ri)] == r[ri] + s = convert(T, string(prefix, prefix, c, suffix, suffix)) + pre = convert(T, prefix) + sb = SubString(s, nextind(pre, lastindex(pre)), + lastindex(convert(T, string(prefix, prefix, c, suffix)))) + r = reverse(sb) + ri = findfirst(isequal(c), r) + @test c == sb[reverseind(sb, ri)] == r[ri] + end end end end -end -@testset "reverseind of empty strings" begin - for s in ("", - SubString("", 1, 0), - SubString("ab", 1, 0), - SubString("ab", 2, 1), - SubString("ab", 3, 2), - GenericString("")) - @test reverseind(s, 0) == 1 - @test reverseind(s, 1) == 0 + @testset "reverseind of empty strings" begin + for s in ("", + SubString("", 1, 0), + SubString("ab", 1, 0), + SubString("ab", 2, 1), + SubString("ab", 3, 2), + GenericString("")) + @test reverseind(s, 0) == 1 + @test reverseind(s, 1) == 0 + end end end -## Cstring tests ## - -@testset "issue #13974: comparison against pointers" begin - str = String("foobar") - ptr = pointer(str) - cstring = Cstring(ptr) - @test ptr == cstring - @test cstring == ptr - - # convenient NULL string creation from Ptr{Cvoid} - nullstr = Cstring(C_NULL) - - # Comparisons against NULL strings - @test ptr != nullstr - @test nullstr != ptr - - # Short-hand comparison against C_NULL - @test nullstr == C_NULL - @test C_NULL == nullstr - @test cstring != C_NULL - @test C_NULL != cstring -end +@testset "Cstring" begin + @testset "issue #13974: comparison against pointers" begin + str = String("foobar") + ptr = pointer(str) + cstring = Cstring(ptr) + @test ptr == cstring + @test cstring == ptr + + # convenient NULL string creation from Ptr{Cvoid} + nullstr = Cstring(C_NULL) + + # Comparisons against NULL strings + @test ptr != nullstr + @test nullstr != ptr + + # Short-hand comparison against C_NULL + @test nullstr == C_NULL + @test C_NULL == nullstr + @test cstring != C_NULL + @test C_NULL != cstring + end -@testset "issue #31381: eltype(Cstring) != Cchar" begin - s = Cstring(C_NULL) - @test eltype(Cstring) == Cchar - @test eltype(s) == Cchar - @test pointer(s) isa Ptr{Cchar} + @testset "issue #31381: eltype(Cstring) != Cchar" begin + s = Cstring(C_NULL) + @test eltype(Cstring) == Cchar + @test eltype(s) == Cchar + @test pointer(s) isa Ptr{Cchar} + end end @testset "Codeunits" begin From 467ab852bb7392843ce74a17de89c221e0f64df0 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 13 Sep 2024 08:18:03 -0400 Subject: [PATCH 212/548] Reorganize search tests into testsets (#55658) Some of these tests are nearly 10 years old! Organized some of them into testsets just in case one breaks in the future, should make it easier to find the problem. --------- Co-authored-by: Simeon David Schaub --- test/strings/search.jl | 506 +++++++++++++++++++++-------------------- 1 file changed, 258 insertions(+), 248 deletions(-) diff --git a/test/strings/search.jl b/test/strings/search.jl index d8883bad24b48..c43327fe2971b 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -4,26 +4,27 @@ astr = "Hello, world.\n" u8str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" -# I think these should give error on 4 also, and "" is not treated -# consistently with SubString("",1,1), nor with Char[] -for ind in (0, 5) - @test_throws BoundsError findnext(SubString("",1,1), "foo", ind) - @test_throws BoundsError findprev(SubString("",1,1), "foo", ind) -end +@testset "BoundsError for findnext/findprev" begin + # I think these should give error on 4 also, and "" is not treated + # consistently with SubString("",1,1), nor with Char[] + for ind in (0, 5) + @test_throws BoundsError findnext(SubString("",1,1), "foo", ind) + @test_throws BoundsError findprev(SubString("",1,1), "foo", ind) + end -# Note: the commented out test will be enabled after fixes to make -# sure that findnext/findprev are consistent -# no matter what type of AbstractString the second argument is -@test_throws BoundsError findnext(isequal('a'), "foo", 0) -@test_throws BoundsError findnext(in(Char[]), "foo", 5) -# @test_throws BoundsError findprev(in(Char[]), "foo", 0) -@test_throws BoundsError findprev(in(Char[]), "foo", 5) + # Note: the commented out test will be enabled after fixes to make + # sure that findnext/findprev are consistent + # no matter what type of AbstractString the second argument is + @test_throws BoundsError findnext(isequal('a'), "foo", 0) + @test_throws BoundsError findnext(in(Char[]), "foo", 5) + # @test_throws BoundsError findprev(in(Char[]), "foo", 0) + @test_throws BoundsError findprev(in(Char[]), "foo", 5) -# @test_throws ErrorException in("foobar","bar") -@test_throws BoundsError findnext(isequal(0x1),b"\x1\x2",0) + # @test_throws ErrorException in("foobar","bar") + @test_throws BoundsError findnext(isequal(0x1),b"\x1\x2",0) +end -# ascii forward search -for str in [astr, GenericString(astr)] +@testset "ascii forward search $(typeof(str))" for str in [astr, GenericString(astr)] @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('∀'), str, 0) @test findfirst(isequal('x'), str) === nothing @@ -41,9 +42,7 @@ for str in [astr, GenericString(astr)] @test findnext(isequal('\n'), str, 15) === nothing @test_throws BoundsError findnext(isequal('ε'), str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) -end -for str in [astr, GenericString(astr)] @test_throws BoundsError findnext('z', str, 0) @test_throws BoundsError findnext('∀', str, 0) @test findfirst('x', str) === nothing @@ -65,8 +64,8 @@ for str in [astr, GenericString(astr)] @test_throws BoundsError findnext('a', str, nextind(str,lastindex(str))+1) end -# ascii backward search -for str in [astr] +@testset "ascii backward search" begin + str = astr @test findlast(isequal('x'), str) === nothing @test findlast(isequal('\0'), str) === nothing @test findlast(isequal('\u80'), str) === nothing @@ -81,9 +80,7 @@ for str in [astr] @test findlast(isequal(','), str) == 6 @test findprev(isequal(','), str, 5) === nothing @test findlast(isequal('\n'), str) == 14 -end -for str in [astr] @test findlast('x', str) === nothing @test findlast('\0', str) === nothing @test findlast('\u80', str) === nothing @@ -102,8 +99,7 @@ for str in [astr] @test findlast('\n', str) == 14 end -# utf-8 forward search -for str in (u8str, GenericString(u8str)) +@testset "utf-8 forward search $(typeof(str))" for str in (u8str, GenericString(u8str)) @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('∀'), str, 0) @test findfirst(isequal('z'), str) === nothing @@ -132,8 +128,8 @@ for str in (u8str, GenericString(u8str)) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end -# utf-8 backward search -for str in [u8str] +@testset "utf-8 backward search" begin + str = u8str @test findlast(isequal('z'), str) === nothing @test findlast(isequal('\0'), str) === nothing @test findlast(isequal('\u80'), str) === nothing @@ -155,6 +151,128 @@ for str in [u8str] @test findprev(isequal('ε'), str, 4) === nothing end +@testset "string forward search with a single-char string" begin + @test findfirst("x", astr) === nothing + @test findfirst("H", astr) == 1:1 + @test findnext("H", astr, 2) === nothing + @test findfirst("l", astr) == 3:3 + @test findnext("l", astr, 4) == 4:4 + @test findnext("l", astr, 5) == 11:11 + @test findnext("l", astr, 12) === nothing + @test findfirst("\n", astr) == 14:14 + @test findnext("\n", astr, 15) === nothing + + @test findfirst("z", u8str) === nothing + @test findfirst("∄", u8str) === nothing + @test findfirst("∀", u8str) == 1:1 + @test findnext("∀", u8str, 4) === nothing + @test findfirst("∃", u8str) == 13:13 + @test findnext("∃", u8str, 16) === nothing + @test findfirst("x", u8str) == 26:26 + @test findnext("x", u8str, 27) == 43:43 + @test findnext("x", u8str, 44) === nothing + @test findfirst("ε", u8str) == 5:5 + @test findnext("ε", u8str, 7) == 54:54 + @test findnext("ε", u8str, 56) === nothing +end + +@testset "findprev backward search with a single-char string" begin + @test findlast("x", astr) === nothing + @test findlast("H", astr) == 1:1 + @test findprev("H", astr, 2) == 1:1 + @test findprev("H", astr, 0) === nothing + @test findlast("l", astr) == 11:11 + @test findprev("l", astr, 10) == 4:4 + @test findprev("l", astr, 4) == 4:4 + @test findprev("l", astr, 3) == 3:3 + @test findprev("l", astr, 2) === nothing + @test findlast("\n", astr) == 14:14 + @test findprev("\n", astr, 13) === nothing + + @test findlast("z", u8str) === nothing + @test findlast("∄", u8str) === nothing + @test findlast("∀", u8str) == 1:1 + @test findprev("∀", u8str, 0) === nothing + #TODO: setting the limit in the middle of a wide char + # makes findnext fail but findprev succeed. + # Should findprev fail as well? + #@test findprev("∀", u8str, 2) === nothing # gives 1:3 + @test findlast("∃", u8str) == 13:13 + @test findprev("∃", u8str, 12) === nothing + @test findlast("x", u8str) == 43:43 + @test findprev("x", u8str, 42) == 26:26 + @test findprev("x", u8str, 25) === nothing + @test findlast("ε", u8str) == 54:54 + @test findprev("ε", u8str, 53) == 5:5 + @test findprev("ε", u8str, 4) === nothing +end + +@testset "string forward search with a single-char regex" begin + @test findfirst(r"x", astr) === nothing + @test findfirst(r"H", astr) == 1:1 + @test findnext(r"H", astr, 2) === nothing + @test findfirst(r"l", astr) == 3:3 + @test findnext(r"l", astr, 4) == 4:4 + @test findnext(r"l", astr, 5) == 11:11 + @test findnext(r"l", astr, 12) === nothing + @test findfirst(r"\n", astr) == 14:14 + @test findnext(r"\n", astr, 15) === nothing + @test findfirst(r"z", u8str) === nothing + @test findfirst(r"∄", u8str) === nothing + @test findfirst(r"∀", u8str) == 1:1 + @test findnext(r"∀", u8str, 4) === nothing + @test findfirst(r"∀", u8str) == findfirst(r"\u2200", u8str) + @test findnext(r"∀", u8str, 4) == findnext(r"\u2200", u8str, 4) + @test findfirst(r"∃", u8str) == 13:13 + @test findnext(r"∃", u8str, 16) === nothing + @test findfirst(r"x", u8str) == 26:26 + @test findnext(r"x", u8str, 27) == 43:43 + @test findnext(r"x", u8str, 44) === nothing + @test findfirst(r"ε", u8str) == 5:5 + @test findnext(r"ε", u8str, 7) == 54:54 + @test findnext(r"ε", u8str, 56) === nothing + for i = 1:lastindex(astr) + @test findnext(r"."s, astr, i) == i:i + end + for i = 1:lastindex(u8str) + if isvalid(u8str,i) + @test findnext(r"."s, u8str, i) == i:i + end + end +end + +@testset "string forward search with a zero-char string" begin + for i = 1:lastindex(astr) + @test findnext("", astr, i) == i:i-1 + end + for i = 1:lastindex(u8str) + @test findnext("", u8str, i) == i:i-1 + end + @test findfirst("", "") === 1:0 +end + +@testset "string backward search with a zero-char string" begin + for i = 1:lastindex(astr) + @test findprev("", astr, i) == i:i-1 + end + for i = 1:lastindex(u8str) + @test findprev("", u8str, i) == i:i-1 + end + @test findlast("", "") === 1:0 +end + +@testset "string forward search with a zero-char regex" begin + for i = 1:lastindex(astr) + @test findnext(r"", astr, i) == i:i-1 + end + for i = 1:lastindex(u8str) + # TODO: should regex search fast-forward invalid indices? + if isvalid(u8str,i) + @test findnext(r"", u8str, i) == i:i-1 + end + end +end + # See the comments in #54579 @testset "Search for invalid chars" begin @test findfirst(==('\xff'), "abc\xffde") == 4 @@ -165,238 +283,130 @@ end @test isnothing(findprev(==('\xa6'), "æa", 2)) end -# string forward search with a single-char string -@test findfirst("x", astr) === nothing -@test findfirst("H", astr) == 1:1 -@test findnext("H", astr, 2) === nothing -@test findfirst("l", astr) == 3:3 -@test findnext("l", astr, 4) == 4:4 -@test findnext("l", astr, 5) == 11:11 -@test findnext("l", astr, 12) === nothing -@test findfirst("\n", astr) == 14:14 -@test findnext("\n", astr, 15) === nothing - -@test findfirst("z", u8str) === nothing -@test findfirst("∄", u8str) === nothing -@test findfirst("∀", u8str) == 1:1 -@test findnext("∀", u8str, 4) === nothing -@test findfirst("∃", u8str) == 13:13 -@test findnext("∃", u8str, 16) === nothing -@test findfirst("x", u8str) == 26:26 -@test findnext("x", u8str, 27) == 43:43 -@test findnext("x", u8str, 44) === nothing -@test findfirst("ε", u8str) == 5:5 -@test findnext("ε", u8str, 7) == 54:54 -@test findnext("ε", u8str, 56) === nothing - -# strifindprev backward search with a single-char string -@test findlast("x", astr) === nothing -@test findlast("H", astr) == 1:1 -@test findprev("H", astr, 2) == 1:1 -@test findprev("H", astr, 0) === nothing -@test findlast("l", astr) == 11:11 -@test findprev("l", astr, 10) == 4:4 -@test findprev("l", astr, 4) == 4:4 -@test findprev("l", astr, 3) == 3:3 -@test findprev("l", astr, 2) === nothing -@test findlast("\n", astr) == 14:14 -@test findprev("\n", astr, 13) === nothing - -@test findlast("z", u8str) === nothing -@test findlast("∄", u8str) === nothing -@test findlast("∀", u8str) == 1:1 -@test findprev("∀", u8str, 0) === nothing -#TODO: setting the limit in the middle of a wide char -# makes findnext fail but findprev succeed. -# Should findprev fail as well? -#@test findprev("∀", u8str, 2) === nothing # gives 1:3 -@test findlast("∃", u8str) == 13:13 -@test findprev("∃", u8str, 12) === nothing -@test findlast("x", u8str) == 43:43 -@test findprev("x", u8str, 42) == 26:26 -@test findprev("x", u8str, 25) === nothing -@test findlast("ε", u8str) == 54:54 -@test findprev("ε", u8str, 53) == 5:5 -@test findprev("ε", u8str, 4) === nothing - -# string forward search with a single-char regex -@test findfirst(r"x", astr) === nothing -@test findfirst(r"H", astr) == 1:1 -@test findnext(r"H", astr, 2) === nothing -@test findfirst(r"l", astr) == 3:3 -@test findnext(r"l", astr, 4) == 4:4 -@test findnext(r"l", astr, 5) == 11:11 -@test findnext(r"l", astr, 12) === nothing -@test findfirst(r"\n", astr) == 14:14 -@test findnext(r"\n", astr, 15) === nothing -@test findfirst(r"z", u8str) === nothing -@test findfirst(r"∄", u8str) === nothing -@test findfirst(r"∀", u8str) == 1:1 -@test findnext(r"∀", u8str, 4) === nothing -@test findfirst(r"∀", u8str) == findfirst(r"\u2200", u8str) -@test findnext(r"∀", u8str, 4) == findnext(r"\u2200", u8str, 4) -@test findfirst(r"∃", u8str) == 13:13 -@test findnext(r"∃", u8str, 16) === nothing -@test findfirst(r"x", u8str) == 26:26 -@test findnext(r"x", u8str, 27) == 43:43 -@test findnext(r"x", u8str, 44) === nothing -@test findfirst(r"ε", u8str) == 5:5 -@test findnext(r"ε", u8str, 7) == 54:54 -@test findnext(r"ε", u8str, 56) === nothing -for i = 1:lastindex(astr) - @test findnext(r"."s, astr, i) == i:i -end -for i = 1:lastindex(u8str) - if isvalid(u8str,i) - @test findnext(r"."s, u8str, i) == i:i - end +@testset "string forward search with a two-char string literal" begin + @test findfirst("xx", "foo,bar,baz") === nothing + @test findfirst("fo", "foo,bar,baz") == 1:2 + @test findnext("fo", "foo,bar,baz", 3) === nothing + @test findfirst("oo", "foo,bar,baz") == 2:3 + @test findnext("oo", "foo,bar,baz", 4) === nothing + @test findfirst("o,", "foo,bar,baz") == 3:4 + @test findnext("o,", "foo,bar,baz", 5) === nothing + @test findfirst(",b", "foo,bar,baz") == 4:5 + @test findnext(",b", "foo,bar,baz", 6) == 8:9 + @test findnext(",b", "foo,bar,baz", 10) === nothing + @test findfirst("az", "foo,bar,baz") == 10:11 + @test findnext("az", "foo,bar,baz", 12) === nothing end -# string forward search with a zero-char string -for i = 1:lastindex(astr) - @test findnext("", astr, i) == i:i-1 +@testset "issue #9365" begin + # string forward search with a two-char UTF-8 (2 byte) string literal + @test findfirst("éé", "ééé") == 1:3 + @test findnext("éé", "ééé", 1) == 1:3 + # string forward search with a two-char UTF-8 (3 byte) string literal + @test findfirst("€€", "€€€") == 1:4 + @test findnext("€€", "€€€", 1) == 1:4 + # string forward search with a two-char UTF-8 (4 byte) string literal + @test findfirst("\U1f596\U1f596", "\U1f596\U1f596\U1f596") == 1:5 + @test findnext("\U1f596\U1f596", "\U1f596\U1f596\U1f596", 1) == 1:5 + + # string forward search with a two-char UTF-8 (2 byte) string literal + @test findfirst("éé", "éé") == 1:3 + @test findnext("éé", "éé", 1) == 1:3 + # string forward search with a two-char UTF-8 (3 byte) string literal + @test findfirst("€€", "€€") == 1:4 + @test findnext("€€", "€€", 1) == 1:4 + # string forward search with a two-char UTF-8 (4 byte) string literal + @test findfirst("\U1f596\U1f596", "\U1f596\U1f596") == 1:5 + @test findnext("\U1f596\U1f596", "\U1f596\U1f596", 1) == 1:5 + + # string backward search with a two-char UTF-8 (2 byte) string literal + @test findlast("éé", "ééé") == 3:5 + @test findprev("éé", "ééé", lastindex("ééé")) == 3:5 + # string backward search with a two-char UTF-8 (3 byte) string literal + @test findlast("€€", "€€€") == 4:7 + @test findprev("€€", "€€€", lastindex("€€€")) == 4:7 + # string backward search with a two-char UTF-8 (4 byte) string literal + @test findlast("\U1f596\U1f596", "\U1f596\U1f596\U1f596") == 5:9 + @test findprev("\U1f596\U1f596", "\U1f596\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 5:9 + + # string backward search with a two-char UTF-8 (2 byte) string literal + @test findlast("éé", "éé") == 1:3 # should really be 1:4! + @test findprev("éé", "éé", lastindex("ééé")) == 1:3 + # string backward search with a two-char UTF-8 (3 byte) string literal + @test findlast("€€", "€€") == 1:4 # should really be 1:6! + @test findprev("€€", "€€", lastindex("€€€")) == 1:4 + # string backward search with a two-char UTF-8 (4 byte) string literal + @test findlast("\U1f596\U1f596", "\U1f596\U1f596") == 1:5 # should really be 1:8! + @test findprev("\U1f596\U1f596", "\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 1:5 end -for i = 1:lastindex(u8str) - @test findnext("", u8str, i) == i:i-1 + +@testset "string backward search with a two-char string literal" begin + @test findlast("xx", "foo,bar,baz") === nothing + @test findlast("fo", "foo,bar,baz") == 1:2 + @test findprev("fo", "foo,bar,baz", 1) === nothing + @test findlast("oo", "foo,bar,baz") == 2:3 + @test findprev("oo", "foo,bar,baz", 2) === nothing + @test findlast("o,", "foo,bar,baz") == 3:4 + @test findprev("o,", "foo,bar,baz", 1) === nothing + @test findlast(",b", "foo,bar,baz") == 8:9 + @test findprev(",b", "foo,bar,baz", 6) == 4:5 + @test findprev(",b", "foo,bar,baz", 3) === nothing + @test findlast("az", "foo,bar,baz") == 10:11 + @test findprev("az", "foo,bar,baz", 10) === nothing end -@test findfirst("", "") === 1:0 -# string backward search with a zero-char string -for i = 1:lastindex(astr) - @test findprev("", astr, i) == i:i-1 +@testset "string search with a two-char regex" begin + @test findfirst(r"xx", "foo,bar,baz") === nothing + @test findfirst(r"fo", "foo,bar,baz") == 1:2 + @test findnext(r"fo", "foo,bar,baz", 3) === nothing + @test findfirst(r"oo", "foo,bar,baz") == 2:3 + @test findnext(r"oo", "foo,bar,baz", 4) === nothing + @test findfirst(r"o,", "foo,bar,baz") == 3:4 + @test findnext(r"o,", "foo,bar,baz", 5) === nothing + @test findfirst(r",b", "foo,bar,baz") == 4:5 + @test findnext(r",b", "foo,bar,baz", 6) == 8:9 + @test findnext(r",b", "foo,bar,baz", 10) === nothing + @test findfirst(r"az", "foo,bar,baz") == 10:11 + @test findnext(r"az", "foo,bar,baz", 12) === nothing end -for i = 1:lastindex(u8str) - @test findprev("", u8str, i) == i:i-1 + +@testset "occursin/contains" begin + # occursin with a String and Char needle + @test occursin("o", "foo") + @test occursin('o', "foo") + # occursin in curried form + @test occursin("foo")("o") + @test occursin("foo")('o') + + # contains + @test contains("foo", "o") + @test contains("foo", 'o') + # contains in curried form + @test contains("o")("foo") + @test contains('o')("foo") + + @test_throws ErrorException "ab" ∈ "abc" end -@test findlast("", "") === 1:0 -# string forward search with a zero-char regex -for i = 1:lastindex(astr) - @test findnext(r"", astr, i) == i:i-1 +@testset "issue #15723" begin + @test findfirst(isequal('('), "⨳(") == 4 + @test findnext(isequal('('), "(⨳(", 2) == 5 + @test findlast(isequal('('), "(⨳(") == 5 + @test findprev(isequal('('), "(⨳(", 2) == 1 + + @test @inferred findall(isequal('a'), "éa") == [3] + @test @inferred findall(isequal('€'), "€€") == [1, 4] + @test @inferred isempty(findall(isequal('é'), "")) end -for i = 1:lastindex(u8str) - # TODO: should regex search fast-forward invalid indices? - if isvalid(u8str,i) - @test findnext(r"", u8str, i) == i:i-1 - end + + +@testset "issue #18109" begin + s_18109 = "fooα🐨βcd3" + @test findlast(isequal('o'), s_18109) == 3 + @test findfirst(isequal('d'), s_18109) == 13 end -# string forward search with a two-char string literal -@test findfirst("xx", "foo,bar,baz") === nothing -@test findfirst("fo", "foo,bar,baz") == 1:2 -@test findnext("fo", "foo,bar,baz", 3) === nothing -@test findfirst("oo", "foo,bar,baz") == 2:3 -@test findnext("oo", "foo,bar,baz", 4) === nothing -@test findfirst("o,", "foo,bar,baz") == 3:4 -@test findnext("o,", "foo,bar,baz", 5) === nothing -@test findfirst(",b", "foo,bar,baz") == 4:5 -@test findnext(",b", "foo,bar,baz", 6) == 8:9 -@test findnext(",b", "foo,bar,baz", 10) === nothing -@test findfirst("az", "foo,bar,baz") == 10:11 -@test findnext("az", "foo,bar,baz", 12) === nothing - -# issue #9365 -# string forward search with a two-char UTF-8 (2 byte) string literal -@test findfirst("éé", "ééé") == 1:3 -@test findnext("éé", "ééé", 1) == 1:3 -# string forward search with a two-char UTF-8 (3 byte) string literal -@test findfirst("€€", "€€€") == 1:4 -@test findnext("€€", "€€€", 1) == 1:4 -# string forward search with a two-char UTF-8 (4 byte) string literal -@test findfirst("\U1f596\U1f596", "\U1f596\U1f596\U1f596") == 1:5 -@test findnext("\U1f596\U1f596", "\U1f596\U1f596\U1f596", 1) == 1:5 - -# string forward search with a two-char UTF-8 (2 byte) string literal -@test findfirst("éé", "éé") == 1:3 -@test findnext("éé", "éé", 1) == 1:3 -# string forward search with a two-char UTF-8 (3 byte) string literal -@test findfirst("€€", "€€") == 1:4 -@test findnext("€€", "€€", 1) == 1:4 -# string forward search with a two-char UTF-8 (4 byte) string literal -@test findfirst("\U1f596\U1f596", "\U1f596\U1f596") == 1:5 -@test findnext("\U1f596\U1f596", "\U1f596\U1f596", 1) == 1:5 - -# string backward search with a two-char UTF-8 (2 byte) string literal -@test findlast("éé", "ééé") == 3:5 -@test findprev("éé", "ééé", lastindex("ééé")) == 3:5 -# string backward search with a two-char UTF-8 (3 byte) string literal -@test findlast("€€", "€€€") == 4:7 -@test findprev("€€", "€€€", lastindex("€€€")) == 4:7 -# string backward search with a two-char UTF-8 (4 byte) string literal -@test findlast("\U1f596\U1f596", "\U1f596\U1f596\U1f596") == 5:9 -@test findprev("\U1f596\U1f596", "\U1f596\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 5:9 - -# string backward search with a two-char UTF-8 (2 byte) string literal -@test findlast("éé", "éé") == 1:3 # should really be 1:4! -@test findprev("éé", "éé", lastindex("ééé")) == 1:3 -# string backward search with a two-char UTF-8 (3 byte) string literal -@test findlast("€€", "€€") == 1:4 # should really be 1:6! -@test findprev("€€", "€€", lastindex("€€€")) == 1:4 -# string backward search with a two-char UTF-8 (4 byte) string literal -@test findlast("\U1f596\U1f596", "\U1f596\U1f596") == 1:5 # should really be 1:8! -@test findprev("\U1f596\U1f596", "\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 1:5 - -# string backward search with a two-char string literal -@test findlast("xx", "foo,bar,baz") === nothing -@test findlast("fo", "foo,bar,baz") == 1:2 -@test findprev("fo", "foo,bar,baz", 1) === nothing -@test findlast("oo", "foo,bar,baz") == 2:3 -@test findprev("oo", "foo,bar,baz", 2) === nothing -@test findlast("o,", "foo,bar,baz") == 3:4 -@test findprev("o,", "foo,bar,baz", 1) === nothing -@test findlast(",b", "foo,bar,baz") == 8:9 -@test findprev(",b", "foo,bar,baz", 6) == 4:5 -@test findprev(",b", "foo,bar,baz", 3) === nothing -@test findlast("az", "foo,bar,baz") == 10:11 -@test findprev("az", "foo,bar,baz", 10) === nothing - -# string search with a two-char regex -@test findfirst(r"xx", "foo,bar,baz") === nothing -@test findfirst(r"fo", "foo,bar,baz") == 1:2 -@test findnext(r"fo", "foo,bar,baz", 3) === nothing -@test findfirst(r"oo", "foo,bar,baz") == 2:3 -@test findnext(r"oo", "foo,bar,baz", 4) === nothing -@test findfirst(r"o,", "foo,bar,baz") == 3:4 -@test findnext(r"o,", "foo,bar,baz", 5) === nothing -@test findfirst(r",b", "foo,bar,baz") == 4:5 -@test findnext(r",b", "foo,bar,baz", 6) == 8:9 -@test findnext(r",b", "foo,bar,baz", 10) === nothing -@test findfirst(r"az", "foo,bar,baz") == 10:11 -@test findnext(r"az", "foo,bar,baz", 12) === nothing - -# occursin with a String and Char needle -@test occursin("o", "foo") -@test occursin('o', "foo") -# occursin in curried form -@test occursin("foo")("o") -@test occursin("foo")('o') - -# contains -@test contains("foo", "o") -@test contains("foo", 'o') -# contains in curried form -@test contains("o")("foo") -@test contains('o')("foo") - -@test_throws ErrorException "ab" ∈ "abc" - -# issue #15723 -@test findfirst(isequal('('), "⨳(") == 4 -@test findnext(isequal('('), "(⨳(", 2) == 5 -@test findlast(isequal('('), "(⨳(") == 5 -@test findprev(isequal('('), "(⨳(", 2) == 1 - -@test @inferred findall(isequal('a'), "éa") == [3] -@test @inferred findall(isequal('€'), "€€") == [1, 4] -@test @inferred isempty(findall(isequal('é'), "")) - -# issue #18109 -s_18109 = "fooα🐨βcd3" -@test findlast(isequal('o'), s_18109) == 3 -@test findfirst(isequal('d'), s_18109) == 13 - -# findall (issue #31788) -@testset "findall" begin +@testset "findall (issue #31788)" begin @test findall("fooo", "foo") == UnitRange{Int}[] @test findall("ing", "Spinning laughing dancing") == [6:8, 15:17, 23:25] @test all(findall("", "foo") .=== [1:0, 2:1, 3:2, 4:3]) # use === to compare empty ranges From 2616634a17fdd286e64d16d454bb8077c54d51c9 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 13 Sep 2024 15:57:19 -0400 Subject: [PATCH 213/548] fix #45494, error in ssa conversion with complex type decl (#55744) We were missing a call to `renumber-assigned-ssavalues` in the case where the declared type is used to assert the type of a value taken from a closure box. --- src/julia-syntax.scm | 5 ++++- test/syntax.jl | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 6815921375184..d6bc03091f37b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3977,7 +3977,10 @@ f(x) = yt(x) (val (if (equal? typ '(core Any)) val `(call (core typeassert) ,val - ,(cl-convert typ fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))))) + ,(let ((convt (cl-convert typ fname lam namemap defined toplevel interp opaq parsed-method-stack globals locals))) + (if (or (symbol-like? convt) (quoted? convt)) + convt + (renumber-assigned-ssavalues convt))))))) `(block ,@(if (eq? box access) '() `((= ,access ,box))) ,undefcheck diff --git a/test/syntax.jl b/test/syntax.jl index da69bd98dc010..1b630a56f84f8 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3975,3 +3975,13 @@ module UsingFailedExplicit using .A: x as x @test x === 1 end + +# issue #45494 +begin + local b::Tuple{<:Any} = (0,) + function f45494() + b = b + b + end +end +@test f45494() === (0,) From 2ee655139d2ac9bae9e33e7af318e477aa40c2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 13 Sep 2024 23:47:28 +0100 Subject: [PATCH 214/548] Revert "Avoid materializing arrays in bidiag matmul" (#55737) Reverts JuliaLang/julia#55450. @jishnub suggested reverting this PR to fix #55727. --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 4 +- stdlib/LinearAlgebra/src/bidiag.jl | 330 +++------------------- stdlib/LinearAlgebra/test/bidiag.jl | 85 ++---- stdlib/LinearAlgebra/test/tridiag.jl | 71 ----- 4 files changed, 68 insertions(+), 422 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 17216845b350c..27d4255fb656b 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -673,9 +673,7 @@ matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) - ev = similar(A, TS, max(0, n-1)) - dv = similar(A, TS, n) - Tridiagonal(ev, dv, similar(ev)) + Tridiagonal(similar(A, TS, n-1), similar(A, TS, n), similar(A, TS, n-1)) end # Special handling for adj/trans vec diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 8bc5b1c47f366..d86bad7e41435 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -557,8 +557,7 @@ end # function to get the internally stored vectors for Bidiagonal and [Sym]Tridiagonal # to avoid allocations in _mul! below (#24324, #24578) _diag(A::Tridiagonal, k) = k == -1 ? A.dl : k == 0 ? A.d : A.du -_diag(A::SymTridiagonal{<:Number}, k) = k == 0 ? A.dv : A.ev -_diag(A::SymTridiagonal, k) = k == 0 ? view(A, diagind(A, IndexStyle(A))) : view(A, diagind(A, 1, IndexStyle(A))) +_diag(A::SymTridiagonal, k) = k == 0 ? A.dv : A.ev function _diag(A::Bidiagonal, k) if k == 0 return A.dv @@ -578,45 +577,12 @@ function _bibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - if n <= 3 - # naive multiplication - for I in CartesianIndices(C) - _modify!(_add, sum(A[I[1], k] * B[k, I[2]] for k in axes(A,2)), C, I) - end - return C - end + n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the # off-diagonal elements for non-zero beta. _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C - @inbounds begin - # first column of C - C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2,1]) - C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) - C[3,1] += _add(A[3,2]*B[2,1]) - # second column of C - C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) - C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) - C[3,2] += _add(A[3,2]*B[2,2] + A[3,3]*B[3,2]) - C[4,2] += _add(A[4,3]*B[3,2]) - end # inbounds - # middle columns - __bibimul!(C, A, B, _add) - @inbounds begin - C[n-3,n-1] += _add(A[n-3,n-2]*B[n-2,n-1]) - C[n-2,n-1] += _add(A[n-2,n-2]*B[n-2,n-1] + A[n-2,n-1]*B[n-1,n-1]) - C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) - C[n, n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) - # last column of C - C[n-2, n] += _add(A[n-2,n-1]*B[n-1,n]) - C[n-1, n] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1,n]*B[n,n ]) - C[n, n] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) - end # inbounds - C -end -function __bibimul!(C, A, B, _add) - n = size(A,1) Al = _diag(A, -1) Ad = _diag(A, 0) Au = _diag(A, 1) @@ -624,198 +590,44 @@ function __bibimul!(C, A, B, _add) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin + # first row of C + C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2, 1]) + C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) + C[1,3] += _add(A[1,2]*B[2,3]) + # second row of C + C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) + C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) + C[2,3] += _add(A[2,2]*B[2,3] + A[2,3]*B[3,3]) + C[2,4] += _add(A[2,3]*B[3,4]) for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] + Ajj = Ad[j] + Ajj₊1 = Au[j] + Bj₋1j₋2 = Bl[j-2] + Bj₋1j₋1 = Bd[j-1] Bj₋1j = Bu[j-1] + Bjj₋1 = Bl[j-1] Bjj = Bd[j] + Bjj₊1 = Bu[j] Bj₊1j = Bl[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - C -end -function __bibimul!(C, A, B::Bidiagonal, _add) - n = size(A,1) - Al = _diag(A, -1) - Ad = _diag(A, 0) - Au = _diag(A, 1) - Bd = _diag(B, 0) - if B.uplo == 'U' - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj) - end - end - else # B.uplo == 'L' - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end - C -end -function __bibimul!(C, A::Bidiagonal, B, _add) - n = size(A,1) - Bl = _diag(B, -1) - Bd = _diag(B, 0) - Bu = _diag(B, 1) - Ad = _diag(A, 0) - if A.uplo == 'U' - Au = _diag(A, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) - end + Bj₊1j₊1 = Bd[j+1] + Bj₊1j₊2 = Bu[j+1] + C[j,j-2] += _add( Ajj₋1*Bj₋1j₋2) + C[j, j-1] += _add(Ajj₋1*Bj₋1j₋1 + Ajj*Bjj₋1) + C[j, j ] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j, j+1] += _add(Ajj *Bjj₊1 + Ajj₊1*Bj₊1j₊1) + C[j, j+2] += _add(Ajj₊1*Bj₊1j₊2) end - else # A.uplo == 'L' - Al = _diag(A, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end - C -end -function __bibimul!(C, A::Bidiagonal, B::Bidiagonal, _add) - n = size(A,1) - Ad = _diag(A, 0) - Bd = _diag(B, 0) - if A.uplo == 'U' && B.uplo == 'U' - Au = _diag(A, 1) - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj) - end - end - elseif A.uplo == 'U' && B.uplo == 'L' - Au = _diag(A, 1) - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) - end - end - elseif A.uplo == 'L' && B.uplo == 'U' - Al = _diag(A, -1) - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj) - end - end - else # A.uplo == 'L' && B.uplo == 'L' - Al = _diag(A, -1) - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j, j] += _add(Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end + # row before last of C + C[n-1,n-3] += _add(A[n-1,n-2]*B[n-2,n-3]) + C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2] + A[n-1,n-2]*B[n-2,n-2]) + C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) + C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1, n]*B[n ,n ]) + # last row of C + C[n,n-2] += _add(A[n,n-1]*B[n-1,n-2]) + C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) + C[n,n ] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) + end # inbounds C end @@ -932,52 +744,7 @@ function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulA nB = size(B,2) (iszero(nA) || iszero(nB)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if nA <= 3 - # naive multiplication - for I in CartesianIndices(C) - col = Base.tail(Tuple(I)) - _modify!(_add, sum(A[I[1], k] * B[k, col...] for k in axes(A,2)), C, I) - end - return C - end - _mul_bitrisym!(C, A, B, _add) -end -function _mul_bitrisym!(C::AbstractVecOrMat, A::Bidiagonal, B::AbstractVecOrMat, _add::MulAddMul) - nA = size(A,1) - nB = size(B,2) - d = A.dv - if A.uplo == 'U' - u = A.ev - @inbounds begin - for j = 1:nB - b₀, b₊ = B[1, j], B[2, j] - _modify!(_add, d[1]*b₀ + u[1]*b₊, C, (1, j)) - for i = 2:nA - 1 - b₀, b₊ = b₊, B[i + 1, j] - _modify!(_add, d[i]*b₀ + u[i]*b₊, C, (i, j)) - end - _modify!(_add, d[nA]*b₊, C, (nA, j)) - end - end - else - l = A.ev - @inbounds begin - for j = 1:nB - b₀, b₊ = B[1, j], B[2, j] - _modify!(_add, d[1]*b₀, C, (1, j)) - for i = 2:nA - 1 - b₋, b₀, b₊ = b₀, b₊, B[i + 1, j] - _modify!(_add, l[i - 1]*b₋ + d[i]*b₀, C, (i, j)) - end - _modify!(_add, l[nA - 1]*b₀ + d[nA]*b₊, C, (nA, j)) - end - end - end - C -end -function _mul_bitrisym!(C::AbstractVecOrMat, A::TriSym, B::AbstractVecOrMat, _add::MulAddMul) - nA = size(A,1) - nB = size(B,2) + nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) l = _diag(A, -1) d = _diag(A, 0) u = _diag(A, 1) @@ -1002,9 +769,8 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) m = size(B,2) (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if m == 1 - B11 = B[1,1] - return mul!(C, A, B11, _add.alpha, _add.beta) + if n <= 3 || m <= 1 + return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end Bl = _diag(B, -1) Bd = _diag(B, 0) @@ -1038,18 +804,21 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAdd m, n = size(A) (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) + if size(A, 1) <= 3 || size(B, 2) <= 1 + return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + end @inbounds if B.uplo == 'U' - for j in n:-1:2, i in 1:m - _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) - end for i in 1:m + for j in n:-1:2 + _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) + end _modify!(_add, A[i,1] * B.dv[1], C, (i, 1)) end else # uplo == 'L' - for j in 1:n-1, i in 1:m - _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) - end for i in 1:m + for j in 1:n-1 + _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) + end _modify!(_add, A[i,n] * B.dv[n], C, (i, n)) end end @@ -1065,12 +834,7 @@ function _dibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - if n <= 3 - for I in CartesianIndices(C) - _modify!(_add, A.diag[I[1]] * B[I[1], I[2]], C, I) - end - return C - end + n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C Ad = A.diag diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 58c228e39e226..ef50658a642fb 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -1026,71 +1026,26 @@ end @test_throws "cannot set entry" B[1,2] = 4 end -@testset "mul for small matrices" begin - @testset for n in 0:6 - D = Diagonal(rand(n)) - v = rand(n) - @testset for uplo in (:L, :U) - B = Bidiagonal(rand(n), rand(max(n-1,0)), uplo) - M = Matrix(B) - - @test B * v ≈ M * v - @test mul!(similar(v), B, v) ≈ M * v - @test mul!(ones(size(v)), B, v, 2, 3) ≈ M * v * 2 .+ 3 - - @test B * B ≈ M * M - @test mul!(similar(B, size(B)), B, B) ≈ M * M - @test mul!(ones(size(B)), B, B, 2, 4) ≈ M * M * 2 .+ 4 - - for m in 0:6 - AL = rand(m,n) - AR = rand(n,m) - @test AL * B ≈ AL * M - @test B * AR ≈ M * AR - @test mul!(similar(AL), AL, B) ≈ AL * M - @test mul!(similar(AR), B, AR) ≈ M * AR - @test mul!(ones(size(AL)), AL, B, 2, 4) ≈ AL * M * 2 .+ 4 - @test mul!(ones(size(AR)), B, AR, 2, 4) ≈ M * AR * 2 .+ 4 - end - - @test B * D ≈ M * D - @test D * B ≈ D * M - @test mul!(similar(B), B, D) ≈ M * D - @test mul!(similar(B), B, D) ≈ M * D - @test mul!(similar(B, size(B)), D, B) ≈ D * M - @test mul!(similar(B, size(B)), B, D) ≈ M * D - @test mul!(ones(size(B)), D, B, 2, 4) ≈ D * M * 2 .+ 4 - @test mul!(ones(size(B)), B, D, 2, 4) ≈ M * D * 2 .+ 4 - end - BL = Bidiagonal(rand(n), rand(max(0, n-1)), :L) - ML = Matrix(BL) - BU = Bidiagonal(rand(n), rand(max(0, n-1)), :U) - MU = Matrix(BU) - T = Tridiagonal(zeros(max(0, n-1)), zeros(n), zeros(max(0, n-1))) - @test mul!(T, BL, BU) ≈ ML * MU - @test mul!(T, BU, BL) ≈ MU * ML - T = Tridiagonal(ones(max(0, n-1)), ones(n), ones(max(0, n-1))) - @test mul!(copy(T), BL, BU, 2, 3) ≈ ML * MU * 2 + T * 3 - @test mul!(copy(T), BU, BL, 2, 3) ≈ MU * ML * 2 + T * 3 - end - - n = 4 - arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) - for B in ( - Bidiagonal(fill(arr,n), fill(arr,n-1), :L), - Bidiagonal(fill(arr,n), fill(arr,n-1), :U), - ) - @test B * B ≈ Matrix(B) * Matrix(B) - BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) - BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) - @test BL * B ≈ Matrix(BL) * Matrix(B) - @test BU * B ≈ Matrix(BU) * Matrix(B) - @test B * BL ≈ Matrix(B) * Matrix(BL) - @test B * BU ≈ Matrix(B) * Matrix(BU) - D = Diagonal(fill(arr,n)) - @test D * B ≈ Matrix(D) * Matrix(B) - @test B * D ≈ Matrix(B) * Matrix(D) - end +@testset "mul with empty arrays" begin + A = zeros(5,0) + B = Bidiagonal(zeros(0), zeros(0), :U) + BL = Bidiagonal(zeros(5), zeros(4), :U) + @test size(A * B) == size(A) + @test size(BL * A) == size(A) + @test size(B * B) == size(B) + C = similar(A) + @test mul!(C, A, B) == A * B + @test mul!(C, BL, A) == BL * A + @test mul!(similar(B), B, B) == B * B + @test mul!(similar(B, size(B)), B, B) == B * B + + v = zeros(size(B,2)) + @test size(B * v) == size(v) + @test mul!(similar(v), B, v) == B * v + + D = Diagonal(zeros(size(B,2))) + @test size(B * D) == size(D * B) == size(D) + @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D end end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 15ac7f9f2147f..3330fa682fe5e 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -970,75 +970,4 @@ end @test sprint(show, S) == "SymTridiagonal($(repr(diag(S))), $(repr(diag(S,1))))" end -@testset "mul for small matrices" begin - @testset for n in 0:6 - for T in ( - Tridiagonal(rand(max(n-1,0)), rand(n), rand(max(n-1,0))), - SymTridiagonal(rand(n), rand(max(n-1,0))), - ) - M = Matrix(T) - @test T * T ≈ M * M - @test mul!(similar(T, size(T)), T, T) ≈ M * M - @test mul!(ones(size(T)), T, T, 2, 4) ≈ M * M * 2 .+ 4 - - for m in 0:6 - AR = rand(n,m) - AL = rand(m,n) - @test AL * T ≈ AL * M - @test T * AR ≈ M * AR - @test mul!(similar(AL), AL, T) ≈ AL * M - @test mul!(similar(AR), T, AR) ≈ M * AR - @test mul!(ones(size(AL)), AL, T, 2, 4) ≈ AL * M * 2 .+ 4 - @test mul!(ones(size(AR)), T, AR, 2, 4) ≈ M * AR * 2 .+ 4 - end - - v = rand(n) - @test T * v ≈ M * v - @test mul!(similar(v), T, v) ≈ M * v - - D = Diagonal(rand(n)) - @test T * D ≈ M * D - @test D * T ≈ D * M - @test mul!(Tridiagonal(similar(T)), D, T) ≈ D * M - @test mul!(Tridiagonal(similar(T)), T, D) ≈ M * D - @test mul!(similar(T, size(T)), D, T) ≈ D * M - @test mul!(similar(T, size(T)), T, D) ≈ M * D - @test mul!(ones(size(T)), D, T, 2, 4) ≈ D * M * 2 .+ 4 - @test mul!(ones(size(T)), T, D, 2, 4) ≈ M * D * 2 .+ 4 - - for uplo in (:U, :L) - B = Bidiagonal(rand(n), rand(max(0, n-1)), uplo) - @test T * B ≈ M * B - @test B * T ≈ B * M - if n <= 2 - @test mul!(Tridiagonal(similar(T)), B, T) ≈ B * M - @test mul!(Tridiagonal(similar(T)), T, B) ≈ M * B - end - @test mul!(similar(T, size(T)), B, T) ≈ B * M - @test mul!(similar(T, size(T)), T, B) ≈ M * B - @test mul!(ones(size(T)), B, T, 2, 4) ≈ B * M * 2 .+ 4 - @test mul!(ones(size(T)), T, B, 2, 4) ≈ M * B * 2 .+ 4 - end - end - end - - n = 4 - arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) - for T in ( - SymTridiagonal(fill(arr,n), fill(arr,n-1)), - Tridiagonal(fill(arr,n-1), fill(arr,n), fill(arr,n-1)), - ) - @test T * T ≈ Matrix(T) * Matrix(T) - BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) - BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) - @test BL * T ≈ Matrix(BL) * Matrix(T) - @test BU * T ≈ Matrix(BU) * Matrix(T) - @test T * BL ≈ Matrix(T) * Matrix(BL) - @test T * BU ≈ Matrix(T) * Matrix(BU) - D = Diagonal(fill(arr,n)) - @test D * T ≈ Matrix(D) * Matrix(T) - @test T * D ≈ Matrix(T) * Matrix(D) - end -end - end # module TestTridiagonal From 243bdede3d686b1eaa634db190f2bab3e5f4de72 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 14 Sep 2024 12:22:31 -0400 Subject: [PATCH 215/548] Add a docs section about loading/precomp/ttfx time tuning (#55569) --- doc/src/manual/performance-tips.md | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 38e27476f0af8..436d58f54754a 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1394,6 +1394,125 @@ Prominent examples include [MKL.jl](https://github.com/JuliaLinearAlgebra/MKL.jl These are external packages, so we will not discuss them in detail here. Please refer to their respective documentations (especially because they have different behaviors than OpenBLAS with respect to multithreading). +## Execution latency, package loading and package precompiling time + +### Reducing time to first plot etc. + +The first time a julia method is called it (and any methods it calls, or ones that can be statically determined) will be +compiled. The [`@time`](@ref) macro family illustrates this. + +``` +julia> foo() = rand(2,2) * rand(2,2) +foo (generic function with 1 method) + +julia> @time @eval foo(); + 0.252395 seconds (1.12 M allocations: 56.178 MiB, 2.93% gc time, 98.12% compilation time) + +julia> @time @eval foo(); + 0.000156 seconds (63 allocations: 2.453 KiB) +``` + +Note that `@time @eval` is better for measuring compilation time because without [`@eval`](@ref), some compilation may +already be done before timing starts. + +When developing a package, you may be able to improve the experience of your users with *precompilation* +so that when they use the package, the code they use is already compiled. To precompile package code effectively, it's +recommended to use [`PrecompileTools.jl`](https://julialang.github.io/PrecompileTools.jl/stable/) to run a +"precompile workload" during precompilation time that is representative of typical package usage, which will cache the +native compiled code into the package `pkgimage` cache, greatly reducing "time to first execution" (often referred to as +TTFX) for such usage. + +Note that [`PrecompileTools.jl`](https://julialang.github.io/PrecompileTools.jl/stable/) workloads can be +disabled and sometimes configured via Preferences if you do not want to spend the extra time precompiling, which +may be the case during development of a package. + +### Reducing package loading time + +Keeping the time taken to load the package down is usually helpful. +General good practice for package developers includes: + +1. Reduce your dependencies to those you really need. Consider using [package extensions](@ref) to support interoperability with other packages without bloating your essential dependencies. +3. Avoid use of [`__init__()`](@ref) functions unless there is no alternative, especially those which might trigger a lot + of compilation, or just take a long time to execute. +4. Where possible, fix [invalidations](https://julialang.org/blog/2020/08/invalidations/) among your dependencies and from your package code. + +The tool [`@time_imports`](@ref) can be useful in the REPL to review the above factors. + +```julia-repl +julia> @time @time_imports using Plots + 0.5 ms Printf + 16.4 ms Dates + 0.7 ms Statistics + ┌ 23.8 ms SuiteSparse_jll.__init__() 86.11% compilation time (100% recompilation) + 90.1 ms SuiteSparse_jll 91.57% compilation time (82% recompilation) + 0.9 ms Serialization + ┌ 39.8 ms SparseArrays.CHOLMOD.__init__() 99.47% compilation time (100% recompilation) + 166.9 ms SparseArrays 23.74% compilation time (100% recompilation) + 0.4 ms Statistics → SparseArraysExt + 0.5 ms TOML + 8.0 ms Preferences + 0.3 ms PrecompileTools + 0.2 ms Reexport +... many deps omitted for example ... + 1.4 ms Tar + ┌ 73.8 ms p7zip_jll.__init__() 99.93% compilation time (100% recompilation) + 79.4 ms p7zip_jll 92.91% compilation time (100% recompilation) + ┌ 27.7 ms GR.GRPreferences.__init__() 99.77% compilation time (100% recompilation) + 43.0 ms GR 64.26% compilation time (100% recompilation) + ┌ 2.1 ms Plots.__init__() 91.80% compilation time (100% recompilation) + 300.9 ms Plots 0.65% compilation time (100% recompilation) + 1.795602 seconds (3.33 M allocations: 190.153 MiB, 7.91% gc time, 39.45% compilation time: 97% of which was recompilation) + +``` + +Notice that in this example there are multiple packages loaded, some with `__init__()` functions, some of which cause +compilation of which some is recompilation. Recompilation is caused by earlier packages invalidating methods, then in +these cases when the following packages run their `__init__()` function some hit recompilation before the code can be run. + +Further, note the `Statistics` extension `SparseArraysExt` has been activated because `SparseArrays` is in the dependency +tree. i.e. see `0.4 ms Statistics → SparseArraysExt`. + +This report gives a good opportunity to review whether the cost of dependency load time is worth the functionality it brings. +Also the `Pkg` utility `why` can be used to report why a an indirect dependency exists. + +``` +(CustomPackage) pkg> why FFMPEG_jll + Plots → FFMPEG → FFMPEG_jll + Plots → GR → GR_jll → FFMPEG_jll +``` + +or to see the indirect dependencies that a package brings in, you can `pkg> rm` the package, see the deps that are removed +from the manifest, then revert the change with `pkg> undo`. + +If loading time is dominated by slow `__init__()` methods having compilation, one verbose way to identify what is being +compiled is to use the julia args `--trace-compile=stderr --trace-compile-timing` which will report a [`precompile`](@ref) +statement each time a method is compiled, along with how long compilation took. For instance, the full setup would be: + +``` +$ julia --startup-file=no --trace-compile=stderr --trace-compile-timing +julia> @time @time_imports using CustomPackage +... +``` + +Note the `--startup-file=no` which helps isolate the test from packages you may have in your `startup.jl`. + +More analysis of the reasons for recompilation can be achieved with the +[`SnoopCompile`](https://github.com/timholy/SnoopCompile.jl) package. + +### Reducing precompilation time + +If package precompilation is taking a long time, one option is to set the following internal and then precompile. +``` +julia> Base.PRECOMPILE_TRACE_COMPILE[] = "stderr" + +pkg> precompile +``` + +This has the effect of setting `--trace-compile=stderr --trace-compile-timing` in the precompilation processes themselves, +so will show which methods are precompiled and how long they took to precompile. + +There are also profiling options such as [using the external profiler Tracy to profile the precompilation process](@ref Profiling-package-precompilation-with-Tracy). + ## Miscellaneous From 346f38bceabf3dab1d3912fe822a663735c91d4a Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 14 Sep 2024 14:40:10 -0500 Subject: [PATCH 216/548] Add compat entry for `Base.donotdelete` (#55773) --- base/docs/basedocs.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index e03d0db78f29f..0fc253bd73d1c 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -3712,6 +3712,9 @@ unused and delete the entire benchmark code). which the value of the arguments of this intrinsic were available (in a register, in memory, etc.). +!!! compat "Julia 1.8" + This method was added in Julia 1.8. + # Examples ```julia From f4fb87b0f9c3d8e5bcc9901701acd81534789293 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 15 Sep 2024 07:12:12 -0400 Subject: [PATCH 217/548] REPL: precompile in its own module because Main is closed. Add check for unexpected errors. (#55759) --- stdlib/REPL/src/REPL.jl | 4 ++-- stdlib/REPL/src/precompile.jl | 27 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index ddf2f55d0b9f7..44fe0446240c6 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -758,11 +758,11 @@ setmodifiers!(c::REPLCompletionProvider, m::LineEdit.Modifiers) = c.modifiers = Set `mod` as the default contextual module in the REPL, both for evaluating expressions and printing them. """ -function activate(mod::Module=Main) +function activate(mod::Module=Main; interactive_utils::Bool=true) mistate = (Base.active_repl::LineEditREPL).mistate mistate === nothing && return nothing mistate.active_module = mod - Base.load_InteractiveUtils(mod) + interactive_utils && Base.load_InteractiveUtils(mod) return nothing end diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 82a1a0bb78ee8..c42def9078759 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -14,6 +14,19 @@ finally end let + # these are intentionally triggered + allowed_errors = [ + "BoundsError: attempt to access 0-element Vector{Any} at index [1]", + "MethodError: no method matching f(::$Int, ::$Int)", + "Padding of type", # reinterpret docstring has ERROR examples + ] + function check_errors(out) + str = String(out) + if occursin("ERROR:", str) && !any(occursin(e, str) for e in allowed_errors) + @error "Unexpected error (Review REPL precompilation with debug_output on):\n$str" + exit(1) + end + end ## Debugging options # View the code sent to the repl by setting this to `stdout` debug_output = devnull # or stdout @@ -25,6 +38,8 @@ let DOWN_ARROW = "\e[B" repl_script = """ + import REPL + REPL.activate(REPL.Precompile; interactive_utils=false) # Main is closed so we can't evaluate in it 2+2 print("") printstyled("a", "b") @@ -47,6 +62,7 @@ let [][1] Base.Iterators.minimum cd("complete_path\t\t$CTRL_C + REPL.activate(; interactive_utils=false) println("done") """ @@ -113,10 +129,10 @@ let end schedule(repltask) # wait for the definitive prompt before start writing to the TTY - readuntil(output_copy, JULIA_PROMPT) + check_errors(readuntil(output_copy, JULIA_PROMPT)) write(debug_output, "\n#### REPL STARTED ####\n") sleep(0.1) - readavailable(output_copy) + check_errors(readavailable(output_copy)) # Input our script precompile_lines = split(repl_script::String, '\n'; keepempty=false) curr = 0 @@ -124,16 +140,16 @@ let sleep(0.1) curr += 1 # consume any other output - bytesavailable(output_copy) > 0 && readavailable(output_copy) + bytesavailable(output_copy) > 0 && check_errors(readavailable(output_copy)) # push our input write(debug_output, "\n#### inputting statement: ####\n$(repr(l))\n####\n") # If the line ends with a CTRL_C, don't write an extra newline, which would # cause a second empty prompt. Our code below expects one new prompt per # input line and can race out of sync with the unexpected second line. endswith(l, CTRL_C) ? write(ptm, l) : write(ptm, l, "\n") - readuntil(output_copy, "\n") + check_errors(readuntil(output_copy, "\n")) # wait for the next prompt-like to appear - readuntil(output_copy, "\n") + check_errors(readuntil(output_copy, "\n")) strbuf = "" while !eof(output_copy) strbuf *= String(readavailable(output_copy)) @@ -143,6 +159,7 @@ let occursin(HELP_PROMPT, strbuf) && break sleep(0.1) end + check_errors(strbuf) end write(debug_output, "\n#### COMPLETED - Closing REPL ####\n") write(ptm, "$CTRL_D") From 4633607ce9b9f077f32f89f09a136e04389bbac2 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 15 Sep 2024 07:11:22 -0500 Subject: [PATCH 218/548] Try to put back previously flakey addmul tests (#55775) Partial revert of #50071, inspired by conversation in https://github.com/JuliaLang/julia/issues/49966#issuecomment-2350935477 Ran the tests 100 times to make sure we're not putting back something that's still flaky. Closes #49966 --- stdlib/LinearAlgebra/test/addmul.jl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 3fff8289242f7..72fdf687bf5c3 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -164,8 +164,7 @@ end Bc = Matrix(B) returned_mat = mul!(C, A, B, α, β) @test returned_mat === C - # This test is skipped because it is flakey, but should be fixed and put back (see #49966) - @test_skip collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol + @test collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol y = C[:, 1] x = B[:, 1] @@ -190,8 +189,7 @@ end returned_mat = mul!(C, Af, Bf, α, β) @test returned_mat === C - # This test is skipped because it is flakey, but should be fixed and put back (see #49966) - @test_skip collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol + @test collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol end end end @@ -203,8 +201,7 @@ end Bc = Matrix(B) returned_mat = mul!(C, A, B, α, zero(eltype(C))) @test returned_mat === C - # This test is skipped because it is flakey, but should be fixed and put back (see #49966) - @test_skip collect(returned_mat) ≈ α * Ac * Bc rtol=rtol + @test collect(returned_mat) ≈ α * Ac * Bc rtol=rtol end end From a993cd8f81a6bc02a88ee5cf036f4e29c36d5580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sun, 15 Sep 2024 21:24:47 +0100 Subject: [PATCH 219/548] Print results of `runtests` with `printstyled` (#55780) This ensures escape characters are used only if `stdout` can accept them. --- test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index c46472ac93fa8..e48e896f4069e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -438,9 +438,9 @@ cd(@__DIR__) do # o_ts.verbose = true # set to true to show all timings when successful Test.print_test_results(o_ts, 1) if !o_ts.anynonpass - println(" \033[32;1mSUCCESS\033[0m") + printstyled(" SUCCESS\n"; bold=true, color=:green) else - println(" \033[31;1mFAILURE\033[0m\n") + printstyled(" FAILURE\n\n"; bold=true, color=:red) skipped > 0 && println("$skipped test", skipped > 1 ? "s were" : " was", " skipped due to failure.") println("The global RNG seed was 0x$(string(seed, base = 16)).\n") From 55c40ce52eb5c249efdff421101190b2a111d541 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 16 Sep 2024 13:25:20 -0400 Subject: [PATCH 220/548] move null check in `unsafe_convert` of RefValue (#55766) LLVM can optimize out this check but our optimizer can't, so this leads to smaller IR in most cases. --- base/refvalue.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/refvalue.jl b/base/refvalue.jl index 000088ff0ce76..7a0f2f84e2206 100644 --- a/base/refvalue.jl +++ b/base/refvalue.jl @@ -46,9 +46,9 @@ function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefValue{T}) # Instead, explicitly load the pointer from the `RefValue`, # which also ensures this returns same pointer as the one rooted in the `RefValue` object. p = atomic_pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), :monotonic) - end - if p == C_NULL - throw(UndefRefError()) + if p == C_NULL + throw(UndefRefError()) + end end return p end From 753296e89ddc484e54937ce7195a3f152fd5a14a Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 16 Sep 2024 15:58:40 -0300 Subject: [PATCH 221/548] Fix hang in tmerge_types_slow (#55757) Fixes https://github.com/JuliaLang/julia/issues/55751 Co-authored-by: Jameson Nash --- base/compiler/typelimits.jl | 1 + test/compiler/inference.jl | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 91a44d3b117ab..3d0e5f3d0877d 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -831,6 +831,7 @@ end typenames[i] = Any.name simplify[i] = false types[j] = widen + typenames[j] = ijname break end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9454c53a09fb7..d1382d3c84b82 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6141,3 +6141,14 @@ end == TypeError @test Base.infer_exception_type((Vector{Any},)) do args Core.throw_methoderror(args...) end == Union{MethodError,ArgumentError} + +# Issue https://github.com/JuliaLang/julia/issues/55751 + +abstract type AbstractGrid55751{T, N} <: AbstractArray{T, N} end +struct Grid55751{T, N, AT} <: AbstractGrid55751{T, N} + axes::AT +end + +t155751 = Union{AbstractArray{UInt8, 4}, Array{Float32, 4}, Grid55751{Float32, 3, _A} where _A} +t255751 = Array{Float32, 3} +@test Core.Compiler.tmerge_types_slow(t155751,t255751) == AbstractArray # shouldn't hang From 5aad7617c3ac49155d03748a451f56215b28dec4 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 16 Sep 2024 15:08:57 -0400 Subject: [PATCH 222/548] trace-compile: color recompilation yellow (#55763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marks recompilation of a method that produced a `precompile` statement as yellow, or if color isn't supported adds a trailing comment: `# recompilation`. The coloring matches the `@time_imports` coloring. i.e. an excerpt of ``` % ./julia --start=no --trace-compile=stderr --trace-compile-timing -e "using InteractiveUtils; @time @time_imports using Plots" ``` ![Screenshot 2024-09-13 at 5 04 24 PM](https://github.com/user-attachments/assets/85bd99e0-586e-4070-994f-2d845be0d9e7) --- NEWS.md | 1 + doc/man/julia.1 | 3 ++- doc/src/manual/command-line-interface.md | 2 +- src/gf.c | 21 ++++++++++++++++----- src/jloptions.c | 4 +++- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index c12cc3c64300c..9ecdd87f0c2bb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -56,6 +56,7 @@ variables. ([#53742]). * `--project=@temp` starts Julia with a temporary environment. * New `--trace-compile-timing` option to report how long each method reported by `--trace-compile` took to compile, in ms. ([#54662]) +* `--trace-compile` now prints recompiled methods in yellow or with a trailing comment if color is not supported ([#55763]) Multi-threading changes ----------------------- diff --git a/doc/man/julia.1 b/doc/man/julia.1 index ebac4362b39a6..536a23bd37894 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -283,7 +283,8 @@ Generate an incremental output file (rather than complete) .TP --trace-compile={stderr|name} -Print precompile statements for methods compiled during execution or save to a path +Print precompile statements for methods compiled during execution or save to stderr or a path. +Methods that were recompiled are printed in yellow or with a trailing comment if color is not supported .TP --trace-compile-timing= diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index d1ed576c42a4f..41c3eacd61d26 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -214,7 +214,7 @@ The following is a complete list of command-line switches available when launchi |`--output-bc ` |Generate LLVM bitcode (.bc)| |`--output-asm ` |Generate an assembly file (.s)| |`--output-incremental={yes\|no*}` |Generate an incremental output file (rather than complete)| -|`--trace-compile={stderr\|name}` |Print precompile statements for methods compiled during execution or save to a path| +|`--trace-compile={stderr\|name}` |Print precompile statements for methods compiled during execution or save to stderr or a path. Methods that were recompiled are printed in yellow or with a trailing comment if color is not supported| |`--trace-compile-timing` |If --trace-compile is enabled show how long each took to compile in ms| |`--image-codegen` |Force generate code in imaging mode| |`--permalloc-pkgimg={yes\|no*}` |Copy the data section of package images into memory| diff --git a/src/gf.c b/src/gf.c index 970cb62b8a862..e6f5b4ee007f7 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2514,7 +2514,7 @@ jl_code_instance_t *jl_method_inferred_with_abi(jl_method_instance_t *mi JL_PROP jl_mutex_t precomp_statement_out_lock; -static void record_precompile_statement(jl_method_instance_t *mi, double compilation_time) +static void record_precompile_statement(jl_method_instance_t *mi, double compilation_time, int is_recompile) { static ios_t f_precompile; static JL_STREAM* s_precompile = NULL; @@ -2539,11 +2539,22 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila } } if (!jl_has_free_typevars(mi->specTypes)) { + if (is_recompile && s_precompile == JL_STDERR && jl_options.color != JL_OPTIONS_COLOR_OFF) + jl_printf(s_precompile, "\e[33m"); if (jl_options.trace_compile_timing) jl_printf(s_precompile, "#= %6.1f ms =# ", compilation_time / 1e6); jl_printf(s_precompile, "precompile("); jl_static_show(s_precompile, mi->specTypes); - jl_printf(s_precompile, ")\n"); + jl_printf(s_precompile, ")"); + if (is_recompile) { + if (s_precompile == JL_STDERR && jl_options.color != JL_OPTIONS_COLOR_OFF) { + jl_printf(s_precompile, "\e[0m"); + } + else { + jl_printf(s_precompile, " # recompile"); + } + } + jl_printf(s_precompile, "\n"); if (s_precompile != JL_STDERR) ios_flush(&f_precompile); } @@ -2674,7 +2685,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t // unspec is probably not specsig, but might be using specptr jl_atomic_store_relaxed(&codeinst->specsigflags, specsigflags & ~0b1); // clear specsig flag jl_mi_cache_insert(mi, codeinst); - record_precompile_statement(mi, 0); + record_precompile_statement(mi, 0, 0); return codeinst; } } @@ -2691,7 +2702,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL); jl_atomic_store_release(&codeinst->invoke, jl_fptr_interpret_call); jl_mi_cache_insert(mi, codeinst); - record_precompile_statement(mi, 0); + record_precompile_statement(mi, 0, 0); return codeinst; } if (compile_option == JL_OPTIONS_COMPILE_OFF) { @@ -2740,7 +2751,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t codeinst = NULL; } else if (did_compile && codeinst->owner == jl_nothing) { - record_precompile_statement(mi, compile_time); + record_precompile_statement(mi, compile_time, is_recompile); } JL_GC_POP(); } diff --git a/src/jloptions.c b/src/jloptions.c index 4cdec2c7b367f..f63f4de020e26 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -258,7 +258,9 @@ static const char opts_hidden[] = " --output-incremental={yes|no*} Generate an incremental output file (rather than\n" " complete)\n" " --trace-compile={stderr|name} Print precompile statements for methods compiled\n" - " during execution or save to a path\n" + " during execution or save to stderr or a path. Methods that\n" + " were recompiled are printed in yellow or with a trailing\n" + " comment if color is not supported\n" " --trace-compile-timing If --trace-compile is enabled show how long each took to\n" " compile in ms\n" " --image-codegen Force generate code in imaging mode\n" From 02549d5c54ec8ac8c7e62ea470803350bb3d1899 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 17 Sep 2024 04:05:24 -0400 Subject: [PATCH 223/548] Use PrecompileTools mechanics to compile REPL (#55782) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/JuliaLang/julia/issues/55778 Based on discussion here https://github.com/JuliaLang/julia/issues/55778#issuecomment-2352428043 With this `?reinterpret` feels instant, with only these precompiles at the start. ![Screenshot 2024-09-16 at 9 49 39 AM](https://github.com/user-attachments/assets/20dc016d-c6f7-4870-acd7-0e795dcf541b) --- stdlib/REPL/src/precompile.jl | 39 ++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index c42def9078759..6bcec6415ba96 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -13,7 +13,7 @@ finally Base._track_dependencies[] = true end -let +function repl_workload() # these are intentionally triggered allowed_errors = [ "BoundsError: attempt to access 0-element Vector{Any} at index [1]", @@ -175,9 +175,38 @@ let nothing end -precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Any, Int}) -precompile(Tuple{typeof(Base.delete!), Base.Set{Any}, String}) -precompile(Tuple{typeof(Base.:(==)), Char, String}) -precompile(Tuple{typeof(Base.reseteof), Base.TTY}) +# Copied from PrecompileTools.jl +let + function check_edges(node) + parentmi = node.mi_info.mi + for child in node.children + childmi = child.mi_info.mi + if !(isdefined(childmi, :backedges) && parentmi ∈ childmi.backedges) + precompile(childmi.specTypes) + end + check_edges(child) + end + end + + if Base.generating_output() && Base.JLOptions().use_pkgimages != 0 + Core.Compiler.Timings.reset_timings() + Core.Compiler.__set_measure_typeinf(true) + try + repl_workload() + finally + Core.Compiler.__set_measure_typeinf(false) + Core.Compiler.Timings.close_current_timer() + end + roots = Core.Compiler.Timings._timings[1].children + for child in roots + precompile(child.mi_info.mi.specTypes) + check_edges(child) + end + precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Any, Int}) + precompile(Tuple{typeof(Base.delete!), Base.Set{Any}, String}) + precompile(Tuple{typeof(Base.:(==)), Char, String}) + precompile(Tuple{typeof(Base.reseteof), Base.TTY}) + end +end end # Precompile From f8086062906d80ef56c0dcd595a13438ef028293 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:41:52 +0900 Subject: [PATCH 224/548] use `inferencebarrier` instead of `invokelatest` for 1-arg `@assert` (#55783) This version would be better as per this comment: I confirmed this still allows us to avoid invalidations reported at JuliaLang/julia#55583. --- base/error.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/error.jl b/base/error.jl index ee533cee0b57d..c49ede624607d 100644 --- a/base/error.jl +++ b/base/error.jl @@ -232,12 +232,14 @@ macro assert(ex, msgs...) msg = msg # pass-through elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol)) # message is an expression needing evaluating - msg = :(Main.Base.invokelatest(Main.Base.string, $(esc(msg)))) + # N.B. To reduce the risk of invalidation caused by the complex callstack involved + # with `string`, use `inferencebarrier` here to hide this `string` from the compiler. + msg = :(Main.Base.inferencebarrier(Main.Base.string)($(esc(msg)))) elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg) msg = Main.Base.string(msg) else # string() might not be defined during bootstrap - msg = :(Main.Base.invokelatest(_assert_tostring, $(Expr(:quote,msg)))) + msg = :(Main.Base.inferencebarrier(_assert_tostring)($(Expr(:quote,msg)))) end return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end From 61c044ca4fe35ed357c77fc50d9a8cf70f6724b0 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 17 Sep 2024 13:18:44 -0300 Subject: [PATCH 225/548] Inline statically known method errors. (#54972) This replaces the `Expr(:call, ...)` with a call of a new builtin `Core.throw_methoderror` This is useful because it makes very clear if something is a static method error or a plain dynamic dispatch that always errors. Tools such as AllocCheck or juliac can notice that this is not a genuine dynamic dispatch, and prevent it from becoming a false positive compile-time error. Dependent on https://github.com/JuliaLang/julia/pull/55705 --------- Co-authored-by: Cody Tapscott --- base/compiler/abstractinterpretation.jl | 41 ++++++++------- base/compiler/ssair/inlining.jl | 52 +++++++++++-------- base/compiler/stmtinfo.jl | 17 ++++-- base/compiler/tfuncs.jl | 7 ++- base/compiler/types.jl | 6 +++ test/compiler/AbstractInterpreter.jl | 9 ++++ .../compiler/EscapeAnalysis/EscapeAnalysis.jl | 4 +- test/compiler/inline.jl | 39 +++++++++----- test/threads_exec.jl | 1 + 9 files changed, 111 insertions(+), 65 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index bb5f2dd1ad180..f126389c42d2d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -209,8 +209,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), rettype = exctype = Any all_effects = Effects() else - if (matches isa MethodMatches ? (!matches.fullmatch || any_ambig(matches)) : - (!all(matches.fullmatches) || any_ambig(matches))) + if !fully_covering(matches) || any_ambig(matches) # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. all_effects = Effects(all_effects; nothrow=false) exctype = exctype ⊔ₚ MethodError @@ -275,21 +274,23 @@ struct MethodMatches applicable::Vector{Any} info::MethodMatchInfo valid_worlds::WorldRange - mt::MethodTable - fullmatch::Bool end -any_ambig(info::MethodMatchInfo) = info.results.ambig +any_ambig(result::MethodLookupResult) = result.ambig +any_ambig(info::MethodMatchInfo) = any_ambig(info.results) any_ambig(m::MethodMatches) = any_ambig(m.info) +fully_covering(info::MethodMatchInfo) = info.fullmatch +fully_covering(m::MethodMatches) = fully_covering(m.info) struct UnionSplitMethodMatches applicable::Vector{Any} applicable_argtypes::Vector{Vector{Any}} info::UnionSplitInfo valid_worlds::WorldRange - mts::Vector{MethodTable} - fullmatches::Vector{Bool} end -any_ambig(m::UnionSplitMethodMatches) = any(any_ambig, m.info.matches) +any_ambig(info::UnionSplitInfo) = any(any_ambig, info.matches) +any_ambig(m::UnionSplitMethodMatches) = any_ambig(m.info) +fully_covering(info::UnionSplitInfo) = all(info.fullmatches) +fully_covering(m::UnionSplitMethodMatches) = fully_covering(m.info) function find_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype); max_union_splitting::Int = InferenceParams(interp).max_union_splitting, @@ -307,7 +308,7 @@ is_union_split_eligible(𝕃::AbstractLattice, argtypes::Vector{Any}, max_union_ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype), max_methods::Int) split_argtypes = switchtupleunion(typeinf_lattice(interp), argtypes) - infos = MethodMatchInfo[] + infos = MethodLookupResult[] applicable = Any[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match valid_worlds = WorldRange() @@ -323,29 +324,29 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: if matches === nothing return FailedMethodMatch("For one of the union split cases, too many methods matched") end - push!(infos, MethodMatchInfo(matches)) + push!(infos, matches) for m in matches push!(applicable, m) push!(applicable_argtypes, arg_n) end valid_worlds = intersect(valid_worlds, matches.valid_worlds) thisfullmatch = any(match::MethodMatch->match.fully_covers, matches) - found = false + mt_found = false for (i, mt′) in enumerate(mts) if mt′ === mt fullmatches[i] &= thisfullmatch - found = true + mt_found = true break end end - if !found + if !mt_found push!(mts, mt) push!(fullmatches, thisfullmatch) end end - info = UnionSplitInfo(infos) + info = UnionSplitInfo(infos, mts, fullmatches) return UnionSplitMethodMatches( - applicable, applicable_argtypes, info, valid_worlds, mts, fullmatches) + applicable, applicable_argtypes, info, valid_worlds) end function find_simple_method_matches(interp::AbstractInterpreter, @nospecialize(atype), max_methods::Int) @@ -360,10 +361,9 @@ function find_simple_method_matches(interp::AbstractInterpreter, @nospecialize(a # (assume this will always be true, so we don't compute / update valid age in this case) return FailedMethodMatch("Too many methods matched") end - info = MethodMatchInfo(matches) fullmatch = any(match::MethodMatch->match.fully_covers, matches) - return MethodMatches( - matches.matches, info, matches.valid_worlds, mt, fullmatch) + info = MethodMatchInfo(matches, mt, fullmatch) + return MethodMatches(matches.matches, info, matches.valid_worlds) end """ @@ -584,9 +584,10 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype) # also need an edge to the method table in case something gets # added that did not intersect with any existing method if isa(matches, MethodMatches) - matches.fullmatch || add_mt_backedge!(sv, matches.mt, atype) + fully_covering(matches) || add_mt_backedge!(sv, matches.info.mt, atype) else - for (thisfullmatch, mt) in zip(matches.fullmatches, matches.mts) + matches::UnionSplitMethodMatches + for (thisfullmatch, mt) in zip(matches.info.fullmatches, matches.info.mts) thisfullmatch || add_mt_backedge!(sv, mt, atype) end end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 70318b9e1a979..727e015b67062 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -50,12 +50,13 @@ struct InliningCase end struct UnionSplit - fully_covered::Bool + handled_all_cases::Bool # All possible dispatches are included in the cases + fully_covered::Bool # All handled cases are fully covering atype::DataType cases::Vector{InliningCase} bbs::Vector{Int} - UnionSplit(fully_covered::Bool, atype::DataType, cases::Vector{InliningCase}) = - new(fully_covered, atype, cases, Int[]) + UnionSplit(handled_all_cases::Bool, fully_covered::Bool, atype::DataType, cases::Vector{InliningCase}) = + new(handled_all_cases, fully_covered, atype, cases, Int[]) end struct InliningEdgeTracker @@ -215,7 +216,7 @@ end function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit, state::CFGInliningState, params::OptimizationParams) - (; fully_covered, #=atype,=# cases, bbs) = union_split + (; handled_all_cases, fully_covered, #=atype,=# cases, bbs) = union_split inline_into_block!(state, block_for_inst(ir, idx)) from_bbs = Int[] delete!(state.split_targets, length(state.new_cfg_blocks)) @@ -235,7 +236,7 @@ function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit, end end push!(from_bbs, length(state.new_cfg_blocks)) - if !(i == length(cases) && fully_covered) + if !(i == length(cases) && (handled_all_cases && fully_covered)) # This block will have the next condition or the final else case push!(state.new_cfg_blocks, BasicBlock(StmtRange(idx, idx))) push!(state.new_cfg_blocks[cond_bb].succs, length(state.new_cfg_blocks)) @@ -244,7 +245,10 @@ function cfg_inline_unionsplit!(ir::IRCode, idx::Int, union_split::UnionSplit, end end # The edge from the fallback block. - fully_covered || push!(from_bbs, length(state.new_cfg_blocks)) + # NOTE This edge is only required for `!handled_all_cases` and not `!fully_covered`, + # since in the latter case we inline `Core.throw_methoderror` into the fallback + # block, which is must-throw, making the subsequent code path unreachable. + !handled_all_cases && push!(from_bbs, length(state.new_cfg_blocks)) # This block will be the block everyone returns to push!(state.new_cfg_blocks, BasicBlock(StmtRange(idx, idx), from_bbs, orig_succs)) join_bb = length(state.new_cfg_blocks) @@ -523,7 +527,7 @@ assuming their order stays the same post-discovery in `ml_matches`. function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, union_split::UnionSplit, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int,Int}}, interp::AbstractInterpreter) - (; fully_covered, atype, cases, bbs) = union_split + (; handled_all_cases, fully_covered, atype, cases, bbs) = union_split stmt, typ, line = compact.result[idx][:stmt], compact.result[idx][:type], compact.result[idx][:line] join_bb = bbs[end] pn = PhiNode() @@ -538,7 +542,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs:: cond = true nparams = fieldcount(atype) @assert nparams == fieldcount(mtype) - if !(i == ncases && fully_covered) + if !(i == ncases && fully_covered && handled_all_cases) for i = 1:nparams aft, mft = fieldtype(atype, i), fieldtype(mtype, i) # If this is always true, we don't need to check for it @@ -597,14 +601,18 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs:: end bb += 1 # We're now in the fall through block, decide what to do - if !fully_covered + if !handled_all_cases ssa = insert_node_here!(compact, NewInstruction(stmt, typ, line)) push!(pn.edges, bb) push!(pn.values, ssa) insert_node_here!(compact, NewInstruction(GotoNode(join_bb), Any, line)) finish_current_bb!(compact, 0) + elseif !fully_covered + insert_node_here!(compact, NewInstruction(Expr(:call, GlobalRef(Core, :throw_methoderror), argexprs...), Union{}, line)) + insert_node_here!(compact, NewInstruction(ReturnNode(), Union{}, line)) + finish_current_bb!(compact, 0) + ncases == 0 && return insert_node_here!(compact, NewInstruction(nothing, Any, line)) end - # We're now in the join block. return insert_node_here!(compact, NewInstruction(pn, typ, line)) end @@ -1348,10 +1356,6 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig # Too many applicable methods # Or there is a (partial?) ambiguity return nothing - elseif length(meth) == 0 - # No applicable methods; try next union split - handled_all_cases = false - continue end local split_fully_covered = false for (j, match) in enumerate(meth) @@ -1392,12 +1396,16 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig handled_all_cases &= handle_any_const_result!(cases, result, match, argtypes, info, flag, state; allow_typevars=true) end + if !fully_covered + atype = argtypes_to_type(sig.argtypes) + # We will emit an inline MethodError so we need a backedge to the MethodTable + add_uncovered_edges!(state.edges, info, atype) + end elseif !isempty(cases) # if we've not seen all candidates, union split is valid only for dispatch tuples filter!(case::InliningCase->isdispatchtuple(case.sig), cases) end - - return cases, (handled_all_cases & fully_covered), joint_effects + return cases, handled_all_cases, fully_covered, joint_effects end function handle_call!(todo::Vector{Pair{Int,Any}}, @@ -1405,9 +1413,9 @@ function handle_call!(todo::Vector{Pair{Int,Any}}, state::InliningState) cases = compute_inlining_cases(info, flag, sig, state) cases === nothing && return nothing - cases, all_covered, joint_effects = cases + cases, handled_all_cases, fully_covered, joint_effects = cases atype = argtypes_to_type(sig.argtypes) - handle_cases!(todo, ir, idx, stmt, atype, cases, all_covered, joint_effects) + handle_cases!(todo, ir, idx, stmt, atype, cases, handled_all_cases, fully_covered, joint_effects) end function handle_match!(cases::Vector{InliningCase}, @@ -1496,19 +1504,19 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn end function handle_cases!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stmt::Expr, - @nospecialize(atype), cases::Vector{InliningCase}, all_covered::Bool, + @nospecialize(atype), cases::Vector{InliningCase}, handled_all_cases::Bool, fully_covered::Bool, joint_effects::Effects) # If we only have one case and that case is fully covered, we may either # be able to do the inlining now (for constant cases), or push it directly # onto the todo list - if all_covered && length(cases) == 1 + if fully_covered && handled_all_cases && length(cases) == 1 handle_single_case!(todo, ir, idx, stmt, cases[1].item) - elseif length(cases) > 0 + elseif length(cases) > 0 || handled_all_cases isa(atype, DataType) || return nothing for case in cases isa(case.sig, DataType) || return nothing end - push!(todo, idx=>UnionSplit(all_covered, atype, cases)) + push!(todo, idx=>UnionSplit(handled_all_cases, fully_covered, atype, cases)) else add_flag!(ir[SSAValue(idx)], flags_for_effects(joint_effects)) end diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 69d2ac7ae45a0..33fca90b6261e 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -33,10 +33,13 @@ not a call to a generic function. """ struct MethodMatchInfo <: CallInfo results::MethodLookupResult + mt::MethodTable + fullmatch::Bool end nsplit_impl(info::MethodMatchInfo) = 1 getsplit_impl(info::MethodMatchInfo, idx::Int) = (@assert idx == 1; info.results) getresult_impl(::MethodMatchInfo, ::Int) = nothing +add_uncovered_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, @nospecialize(atype)) = (!info.fullmatch && push!(edges, info.mt, atype); ) """ info::UnionSplitInfo <: CallInfo @@ -48,20 +51,27 @@ each partition (`info.matches::Vector{MethodMatchInfo}`). This info is illegal on any statement that is not a call to a generic function. """ struct UnionSplitInfo <: CallInfo - matches::Vector{MethodMatchInfo} + matches::Vector{MethodLookupResult} + mts::Vector{MethodTable} + fullmatches::Vector{Bool} end nmatches(info::MethodMatchInfo) = length(info.results) function nmatches(info::UnionSplitInfo) n = 0 for mminfo in info.matches - n += nmatches(mminfo) + n += length(mminfo) end return n end nsplit_impl(info::UnionSplitInfo) = length(info.matches) -getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit_impl(info.matches[idx], 1) +getsplit_impl(info::UnionSplitInfo, idx::Int) = info.matches[idx] getresult_impl(::UnionSplitInfo, ::Int) = nothing +function add_uncovered_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, @nospecialize(atype)) + for (mt, fullmatch) in zip(info.mts, info.fullmatches) + !fullmatch && push!(edges, mt, atype) + end +end abstract type ConstResult end @@ -105,6 +115,7 @@ end nsplit_impl(info::ConstCallInfo) = nsplit(info.call) getsplit_impl(info::ConstCallInfo, idx::Int) = getsplit(info.call, idx) getresult_impl(info::ConstCallInfo, idx::Int) = info.results[idx] +add_uncovered_edges_impl(edges::Vector{Any}, info::ConstCallInfo, @nospecialize(atype)) = add_uncovered_edges!(edges, info.call, atype) """ info::MethodResultPure <: CallInfo diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 64a93bd07c2fa..6bb73ded8660d 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2983,9 +2983,9 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, # also need an edge to the method table in case something gets # added that did not intersect with any existing method if isa(matches, MethodMatches) - matches.fullmatch || add_mt_backedge!(sv, matches.mt, atype) + fully_covering(matches) || add_mt_backedge!(sv, matches.info.mt, atype) else - for (thisfullmatch, mt) in zip(matches.fullmatches, matches.mts) + for (thisfullmatch, mt) in zip(matches.info.fullmatches, matches.info.mts) thisfullmatch || add_mt_backedge!(sv, mt, atype) end end @@ -3001,8 +3001,7 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, add_backedge!(sv, edge) end - if isa(matches, MethodMatches) ? (!matches.fullmatch || any_ambig(matches)) : - (!all(matches.fullmatches) || any_ambig(matches)) + if !fully_covering(matches) || any_ambig(matches) # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. rt = Bool end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index f315b7968fd9b..015b1dbc00a6f 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -450,10 +450,16 @@ abstract type CallInfo end nsplit(info::CallInfo) = nsplit_impl(info)::Union{Nothing,Int} getsplit(info::CallInfo, idx::Int) = getsplit_impl(info, idx)::MethodLookupResult +add_uncovered_edges!(edges::Vector{Any}, info::CallInfo, @nospecialize(atype)) = add_uncovered_edges_impl(edges, info, atype) + getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx) +# must implement `nsplit`, `getsplit`, and `add_uncovered_edges!` to opt in to inlining nsplit_impl(::CallInfo) = nothing getsplit_impl(::CallInfo, ::Int) = error("unexpected call into `getsplit`") +add_uncovered_edges_impl(edges::Vector{Any}, info::CallInfo, @nospecialize(atype)) = error("unexpected call into `add_uncovered_edges!`") + +# must implement `getresult` to opt in to extended lattice return information getresult_impl(::CallInfo, ::Int) = nothing @specialize diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index d95354cefa80c..e92b67f980942 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -409,6 +409,7 @@ end CC.nsplit_impl(info::NoinlineCallInfo) = CC.nsplit(info.info) CC.getsplit_impl(info::NoinlineCallInfo, idx::Int) = CC.getsplit(info.info, idx) CC.getresult_impl(info::NoinlineCallInfo, idx::Int) = CC.getresult(info.info, idx) +CC.add_uncovered_edges_impl(edges::Vector{Any}, info::NoinlineCallInfo, @nospecialize(atype)) = CC.add_uncovered_edges!(edges, info.info, atype) function CC.abstract_call(interp::NoinlineInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Int) @@ -431,6 +432,8 @@ end @inline function inlined_usually(x, y, z) return x * y + z end +foo_split(x::Float64) = 1 +foo_split(x::Int) = 2 # check if the inlining algorithm works as expected let src = code_typed1((Float64,Float64,Float64)) do x, y, z @@ -444,6 +447,7 @@ let NoinlineModule = Module() main_func(x, y, z) = inlined_usually(x, y, z) @eval NoinlineModule noinline_func(x, y, z) = $inlined_usually(x, y, z) @eval OtherModule other_func(x, y, z) = $inlined_usually(x, y, z) + @eval NoinlineModule bar_split_error() = $foo_split(Core.compilerbarrier(:type, nothing)) interp = NoinlineInterpreter(Set((NoinlineModule,))) @@ -473,6 +477,11 @@ let NoinlineModule = Module() @test count(isinvoke(:inlined_usually), src.code) == 0 @test count(iscall((src, inlined_usually)), src.code) == 0 end + + let src = code_typed1(NoinlineModule.bar_split_error) + @test count(iscall((src, foo_split)), src.code) == 0 + @test count(iscall((src, Core.throw_methoderror)), src.code) > 0 + end end # Make sure that Core.Compiler has enough NamedTuple infrastructure diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index 31c21f7228014..8c3e065818208 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -2240,13 +2240,13 @@ end # accounts for ThrownEscape via potential MethodError # no method error -@noinline identity_if_string(x::SafeRef) = (println("preventing inlining"); nothing) +@noinline identity_if_string(x::SafeRef{<:AbstractString}) = (println("preventing inlining"); nothing) let result = code_escapes((SafeRef{String},)) do x identity_if_string(x) end @test has_no_escape(ignore_argescape(result.state[Argument(2)])) end -let result = code_escapes((Union{SafeRef{String},Nothing},)) do x +let result = code_escapes((SafeRef,)) do x identity_if_string(x) end i = only(findall(iscall((result.ir, identity_if_string)), result.ir.stmts.stmt)) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index a8b5fd66dcd0d..80c8ddbb08c69 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -876,7 +876,7 @@ let src = code_typed1((Any,)) do x abstract_unionsplit_fallback(x) end @test count(isinvoke(:abstract_unionsplit_fallback), src.code) == 2 - @test count(iscall((src, abstract_unionsplit_fallback)), src.code) == 1 # fallback dispatch + @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end let src = code_typed1((Union{Type,Number},)) do x abstract_unionsplit_fallback(x) @@ -912,7 +912,7 @@ let src = code_typed1((Any,)) do x @test count(iscall((src, typeof)), src.code) == 2 @test count(isinvoke(:println), src.code) == 0 @test count(iscall((src, println)), src.code) == 0 - @test count(iscall((src, abstract_unionsplit_fallback)), src.code) == 1 # fallback dispatch + @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end let src = code_typed1((Union{Type,Number},)) do x abstract_unionsplit_fallback(false, x) @@ -960,8 +960,8 @@ let # aggressive inlining of single, abstract method match end |> only |> first # both callsites should be inlined @test count(isinvoke(:has_free_typevars), src.code) == 2 - # `isGoodType(y::Any)` isn't fully covered, thus a runtime type check and fallback dynamic dispatch should be inserted - @test count(iscall((src,isGoodType)), src.code) == 1 + # `isGoodType(y::Any)` isn't fully covered, so the fallback is a method error + @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end @inline isGoodType2(cnd, @nospecialize x::Type) = @@ -973,8 +973,8 @@ let # aggressive inlining of single, abstract method match (with constant-prop'e # both callsite should be inlined with constant-prop'ed result @test count(isinvoke(:isType), src.code) == 2 @test count(isinvoke(:has_free_typevars), src.code) == 0 - # `isGoodType(y::Any)` isn't fully covered, thus a runtime type check and fallback dynamic dispatch should be inserted - @test count(iscall((src,isGoodType2)), src.code) == 1 + # `isGoodType(y::Any)` isn't fully covered, thus a MethodError gets inserted + @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end @noinline function checkBadType!(@nospecialize x::Type) @@ -989,8 +989,8 @@ let # aggressive static dispatch of single, abstract method match end |> only |> first # both callsites should be resolved statically @test count(isinvoke(:checkBadType!), src.code) == 2 - # `checkBadType!(y::Any)` isn't fully covered, thus a runtime type check and fallback dynamic dispatch should be inserted - @test count(iscall((src,checkBadType!)), src.code) == 1 + # `checkBadType!(y::Any)` isn't fully covered, thus a MethodError gets inserted + @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end @testset "late_inline_special_case!" begin @@ -2004,7 +2004,7 @@ f48397(::Tuple{String,String}) = :ok let src = code_typed1((Union{Bool,Tuple{String,Any}},)) do x f48397(x) end - @test any(iscall((src, f48397)), src.code) + @test any(iscall((src, Core.throw_methoderror)), src.code) # fallback method error) end g48397::Union{Bool,Tuple{String,Any}} = ("48397", 48397) let res = @test_throws MethodError let @@ -2175,11 +2175,6 @@ let src = code_typed1() do @test count(isinvoke(:iterate), src.code) == 0 end -# JuliaLang/julia#53062: proper `joint_effects` for call with empty method matches -let ir = first(only(Base.code_ircode(setproperty!, (Base.RefValue{Int},Symbol,Base.RefValue{Int})))) - i = findfirst(iscall((ir, convert)), ir.stmts.stmt)::Int - @test iszero(ir.stmts.flag[i] & Core.Compiler.IR_FLAG_NOTHROW) -end function issue53062(cond) x = Ref{Int}(0) if cond @@ -2214,3 +2209,19 @@ let ir = Base.code_ircode((Issue52644,); optimize_until="Inlining") do t @test irfunc(Issue52644(Tuple{})) === :DataType @test_throws MethodError irfunc(Issue52644(Tuple{<:Integer})) end + +foo_split(x::Float64) = 1 +foo_split(x::Int) = 2 +bar_inline_error() = foo_split(nothing) +bar_split_error() = foo_split(Core.compilerbarrier(:type,nothing)) + +let src = code_typed1(bar_inline_error, Tuple{}) + # Should inline method errors + @test count(iscall((src, foo_split)), src.code) == 0 + @test count(iscall((src, Core.throw_methoderror)), src.code) > 0 +end +let src = code_typed1(bar_split_error, Tuple{}) + # Should inline method errors + @test count(iscall((src, foo_split)), src.code) == 0 + @test count(iscall((src, Core.throw_methoderror)), src.code) > 0 +end diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 595f8991d58d7..ac54dd009390c 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -1235,6 +1235,7 @@ end @testset "throw=true" begin tasks, event = create_tasks() push!(tasks, Threads.@spawn error("Error")) + wait(tasks[end]; throw=false) @test_throws CompositeException begin waitany(convert_tasks(tasks_type, tasks); throw=true) From 48ddd2dc2859f41fc1a82876cc5654fb31d7b2ba Mon Sep 17 00:00:00 2001 From: norci Date: Wed, 18 Sep 2024 01:14:35 +0800 Subject: [PATCH 226/548] Fix shell `cd` error when working dir has been deleted (#41244) root cause: if current dir has been deleted, then pwd() will throw an IOError: pwd(): no such file or directory (ENOENT) --------- Co-authored-by: Ian Butterworth --- base/client.jl | 13 +++++++++---- test/file.jl | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/base/client.jl b/base/client.jl index 2ca88c40aeb7e..a04556507d5dc 100644 --- a/base/client.jl +++ b/base/client.jl @@ -41,7 +41,6 @@ function repl_cmd(cmd, out) if isempty(cmd.exec) throw(ArgumentError("no cmd to execute")) elseif cmd.exec[1] == "cd" - new_oldpwd = pwd() if length(cmd.exec) > 2 throw(ArgumentError("cd method only takes one argument")) elseif length(cmd.exec) == 2 @@ -52,11 +51,17 @@ function repl_cmd(cmd, out) end dir = ENV["OLDPWD"] end - cd(dir) else - cd() + dir = homedir() end - ENV["OLDPWD"] = new_oldpwd + try + ENV["OLDPWD"] = pwd() + catch ex + ex isa IOError || rethrow() + # if current dir has been deleted, then pwd() will throw an IOError: pwd(): no such file or directory (ENOENT) + delete!(ENV, "OLDPWD") + end + cd(dir) println(out, pwd()) else @static if !Sys.iswindows() diff --git a/test/file.jl b/test/file.jl index de6d488056a02..de258c92e02bc 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1908,6 +1908,26 @@ end end end +@testset "pwd tests" begin + mktempdir() do dir + cd(dir) do + withenv("OLDPWD" => nothing) do + io = IOBuffer() + Base.repl_cmd(@cmd("cd"), io) + Base.repl_cmd(@cmd("cd -"), io) + @test realpath(pwd()) == realpath(dir) + if !Sys.iswindows() + # Delete the working directory and check we can cd out of it + # Cannot delete the working directory on Windows + rm(dir) + @test_throws Base._UVError("pwd()", Base.UV_ENOENT) pwd() + Base.repl_cmd(@cmd("cd \\~"), io) + end + end + end + end +end + @testset "readdir tests" begin ≛(a, b) = sort(a) == sort(b) mktempdir() do dir From 00739173311983479fee3f5f826e4b763872860a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 17 Sep 2024 19:47:48 -0400 Subject: [PATCH 227/548] codegen: fix bits compare for UnionAll (#55770) Fixes #55768 in two parts: one is making the type computation in emit_bits_compare agree with the parent function and two is not using the optimized egal code for UnionAll kinds, which is different from how the egal code itself works for kinds. --- src/codegen.cpp | 21 ++++++++++++--------- test/compiler/codegen.jl | 5 +++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 73a5f844b31da..6ae4f56a53ee2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3484,25 +3484,26 @@ static size_t emit_masked_bits_compare(callback &emit_desc, jl_datatype_t *aty, static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t arg2) { ++EmittedBitsCompares; + jl_value_t *argty = (arg1.constant ? jl_typeof(arg1.constant) : arg1.typ); bool isboxed; Type *at = julia_type_to_llvm(ctx, arg1.typ, &isboxed); - assert(jl_is_datatype(arg1.typ) && arg1.typ == arg2.typ && !isboxed); + assert(jl_is_datatype(arg1.typ) && arg1.typ == (arg2.constant ? jl_typeof(arg2.constant) : arg2.typ) && !isboxed); if (type_is_ghost(at)) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); if (at->isIntegerTy() || at->isPointerTy() || at->isFloatingPointTy()) { Type *at_int = INTT(at, ctx.emission_context.DL); - Value *varg1 = emit_unbox(ctx, at_int, arg1, arg1.typ); - Value *varg2 = emit_unbox(ctx, at_int, arg2, arg2.typ); + Value *varg1 = emit_unbox(ctx, at_int, arg1, argty); + Value *varg2 = emit_unbox(ctx, at_int, arg2, argty); return ctx.builder.CreateICmpEQ(varg1, varg2); } if (at->isVectorTy()) { - jl_svec_t *types = ((jl_datatype_t*)arg1.typ)->types; + jl_svec_t *types = ((jl_datatype_t*)argty)->types; Value *answer = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); - Value *varg1 = emit_unbox(ctx, at, arg1, arg1.typ); - Value *varg2 = emit_unbox(ctx, at, arg2, arg2.typ); + Value *varg1 = emit_unbox(ctx, at, arg1, argty); + Value *varg2 = emit_unbox(ctx, at, arg2, argty); for (size_t i = 0, l = jl_svec_len(types); i < l; i++) { jl_value_t *fldty = jl_svecref(types, i); Value *subAns, *fld1, *fld2; @@ -3517,7 +3518,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a } if (at->isAggregateType()) { // Struct or Array - jl_datatype_t *sty = (jl_datatype_t*)arg1.typ; + jl_datatype_t *sty = (jl_datatype_t*)argty; size_t sz = jl_datatype_size(sty); if (sz > 512 && !sty->layout->flags.haspadding && sty->layout->flags.isbitsegal) { Value *varg1 = arg1.ispointer() ? data_pointer(ctx, arg1) : @@ -3721,8 +3722,10 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva if (jl_type_intersection(rt1, rt2) == (jl_value_t*)jl_bottom_type) // types are disjoint (exhaustive test) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); - bool justbits1 = jl_is_concrete_immutable(rt1); - bool justbits2 = jl_is_concrete_immutable(rt2); + // can compare any concrete immutable by bits, except for UnionAll + // which has a special non-bits based egal + bool justbits1 = jl_is_concrete_immutable(rt1) && !jl_is_kind(rt1); + bool justbits2 = jl_is_concrete_immutable(rt2) && !jl_is_kind(rt2); if (justbits1 || justbits2) { // whether this type is unique'd by value return emit_nullcheck_guard2(ctx, nullcheck1, nullcheck2, [&] () -> Value* { jl_datatype_t *typ = (jl_datatype_t*)(justbits1 ? rt1 : rt2); diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 0260113044a3b..07308713bb789 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -999,3 +999,8 @@ for (T, StructName) in ((Int128, :Issue55558), (UInt128, :UIssue55558)) @test sizeof($(StructName)) == 48 broken=broken_i128 end end + +@noinline Base.@nospecializeinfer f55768(@nospecialize z::UnionAll) = z === Vector +@test f55768(Vector) +@test f55768(Vector{T} where T) +@test !f55768(Vector{S} where S) From 95a32db42c3cca841006836ddd88c18aac3afb5a Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:51:34 -0300 Subject: [PATCH 228/548] use libuv to measure maxrss (#55806) Libuv has a wrapper around rusage on Unix (and its equivalent on Windows). We should probably use it. --- src/sys.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/sys.c b/src/sys.c index 712d232da363a..b54edc32b32b6 100644 --- a/src/sys.c +++ b/src/sys.c @@ -772,26 +772,11 @@ JL_DLLEXPORT jl_sym_t *jl_get_ARCH(void) JL_NOTSAFEPOINT JL_DLLEXPORT size_t jl_maxrss(void) { -#if defined(_OS_WINDOWS_) - PROCESS_MEMORY_COUNTERS counter; - GetProcessMemoryInfo( GetCurrentProcess( ), &counter, sizeof(counter) ); - return (size_t)counter.PeakWorkingSetSize; - -// FIXME: `rusage` is available on OpenBSD, DragonFlyBSD and NetBSD as well. -// All of them return `ru_maxrss` in kilobytes. -#elif defined(_OS_LINUX_) || defined(_OS_DARWIN_) || defined (_OS_FREEBSD_) || defined (_OS_OPENBSD_) - struct rusage rusage; - getrusage( RUSAGE_SELF, &rusage ); - -#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || defined (_OS_OPENBSD_) - return (size_t)(rusage.ru_maxrss * 1024); -#else - return (size_t)rusage.ru_maxrss; -#endif - -#else - return (size_t)0; -#endif + uv_rusage_t rusage; + if (uv_getrusage(&rusage) == 0) { + return rusage.ru_maxrss * 1024; + } + return 0; } // Simple `rand()` like function, with global seed and added thread-safety From e4c8d4f7976162dcf5eebc15d93d2408cb6d0666 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 18 Sep 2024 17:49:15 -0400 Subject: [PATCH 229/548] REPL: use atreplinit to change the active module during precompilation (#55805) --- stdlib/REPL/src/precompile.jl | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 6bcec6415ba96..f7961a205e0b1 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -37,9 +37,21 @@ function repl_workload() UP_ARROW = "\e[A" DOWN_ARROW = "\e[B" + # This is notified as soon as the first prompt appears + repl_init_event = Base.Event() + + atreplinit() do repl + # Main is closed so we can't evaluate in it, but atreplinit runs at + # a time that repl.mistate === nothing so REPL.activate fails. So do + # it async and wait for the first prompt to know its ready. + t = @async begin + wait(repl_init_event) + REPL.activate(REPL.Precompile; interactive_utils=false) + end + Base.errormonitor(t) + end + repl_script = """ - import REPL - REPL.activate(REPL.Precompile; interactive_utils=false) # Main is closed so we can't evaluate in it 2+2 print("") printstyled("a", "b") @@ -62,7 +74,6 @@ function repl_workload() [][1] Base.Iterators.minimum cd("complete_path\t\t$CTRL_C - REPL.activate(; interactive_utils=false) println("done") """ @@ -159,6 +170,7 @@ function repl_workload() occursin(HELP_PROMPT, strbuf) && break sleep(0.1) end + notify(repl_init_event) check_errors(strbuf) end write(debug_output, "\n#### COMPLETED - Closing REPL ####\n") From 441bcd05feb3cbb325bc169f6e9e9d1a989f5f9f Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:23:39 -0400 Subject: [PATCH 230/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20299a35610=20to=20308f9d32f=20(#55808)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 | 1 - .../Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 | 1 - .../Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 | 1 + .../Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 create mode 100644 deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 diff --git a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 deleted file mode 100644 index 3c112b99f88d9..0000000000000 --- a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -791c9ca37077fdc36b959a17904dd935 diff --git a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 b/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 deleted file mode 100644 index c7c212047d2b0..0000000000000 --- a/deps/checksums/Pkg-299a356100f54215388502148979189aff760822.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -96520326931685d4300e825a302010f113e942aaa55aa4ff12caf3e9df314309df993c97753ae482c2198db67678423885bf5ea40c743c8e4b6ef96d7b8d4472 diff --git a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 new file mode 100644 index 0000000000000..b59e1d8427b8b --- /dev/null +++ b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 @@ -0,0 +1 @@ +b48c15e727d96a7525e0b800180d46f4 diff --git a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 new file mode 100644 index 0000000000000..4f4bce61f1f0f --- /dev/null +++ b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 @@ -0,0 +1 @@ +edc2c19bccf6b00e3ea7c4e0b1af36ca86c7e3f521d8c3c05a930ce3d961fb0259a98ae27be5c3e052418f9b4e7ca74cc4d3fee59dac12d47bd1ac5cd9e34fbe diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 3d4a627d6e472..602fbcc648e59 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 299a356100f54215388502148979189aff760822 +PKG_SHA1 = 308f9d32fcec769fbed8cf6c5a17d54753ca1f5b PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 86c567f6d9039ba58036031e7218dffe08b1cc16 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:04:04 -0400 Subject: [PATCH 231/548] Improve codegen for `Core.throw_methoderror` and `Core.current_scope` (#55803) This slightly improves our (LLVM) codegen for `Core.throw_methoderror` and `Core.current_scope` ```julia julia> foo() = Core.current_scope() julia> bar() = Core.throw_methoderror(+, nothing) ``` Before: ```llvm ; Function Signature: foo() define nonnull ptr @julia_foo_2488() #0 { top: %0 = call ptr @jl_get_builtin_fptr(ptr nonnull @"+Core.#current_scope#2491.jit") %Builtin_ret = call nonnull ptr %0(ptr nonnull @"jl_global#2492.jit", ptr null, i32 0) ret ptr %Builtin_ret } ; Function Signature: bar() define void @julia_bar_589() #0 { top: %jlcallframe1 = alloca [2 x ptr], align 8 %0 = call ptr @jl_get_builtin_fptr(ptr nonnull @"+Core.#throw_methoderror#591.jit") %jl_nothing = load ptr, ptr @jl_nothing, align 8 store ptr @"jl_global#593.jit", ptr %jlcallframe1, align 8 %1 = getelementptr inbounds ptr, ptr %jlcallframe1, i64 1 store ptr %jl_nothing, ptr %1, align 8 %Builtin_ret = call nonnull ptr %0(ptr nonnull @"jl_global#592.jit", ptr nonnull %jlcallframe1, i32 2) call void @llvm.trap() unreachable } ``` After: ```llvm ; Function Signature: foo() define nonnull ptr @julia_foo_713() #0 { top: %thread_ptr = call ptr asm "movq %fs:0, $0", "=r"() #5 %tls_ppgcstack = getelementptr inbounds i8, ptr %thread_ptr, i64 -8 %tls_pgcstack = load ptr, ptr %tls_ppgcstack, align 8 %current_scope = getelementptr inbounds i8, ptr %tls_pgcstack, i64 -72 %0 = load ptr, ptr %current_scope, align 8 ret ptr %0 } ; Function Signature: bar() define void @julia_bar_1581() #0 { top: %jlcallframe1 = alloca [2 x ptr], align 8 %jl_nothing = load ptr, ptr @jl_nothing, align 8 store ptr @"jl_global#1583.jit", ptr %jlcallframe1, align 8 %0 = getelementptr inbounds ptr, ptr %jlcallframe1, i64 1 store ptr %jl_nothing, ptr %0, align 8 %jl_f_throw_methoderror_ret = call nonnull ptr @jl_f_throw_methoderror(ptr null, ptr nonnull %jlcallframe1, i32 2) call void @llvm.trap() unreachable } ``` --- src/builtins.c | 2 +- src/codegen.cpp | 13 ++++++++++++- src/staticdata.c | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 75c4d02c898b2..96c4cec0f5087 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2444,7 +2444,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin_func("finalizer", jl_f_finalizer); add_builtin_func("_compute_sparams", jl_f__compute_sparams); add_builtin_func("_svec_ref", jl_f__svec_ref); - add_builtin_func("current_scope", jl_f_current_scope); + jl_builtin_current_scope = add_builtin_func("current_scope", jl_f_current_scope); add_builtin_func("throw_methoderror", jl_f_throw_methoderror); // builtin types diff --git a/src/codegen.cpp b/src/codegen.cpp index 6ae4f56a53ee2..6d4ecc63e5ca1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1619,6 +1619,7 @@ static const auto &builtin_func_map() { { jl_f__call_in_world_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, { jl_f__call_in_world_total_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world_total), get_func_sig, get_func_attrs} }, { jl_f_throw_addr, new JuliaFunction<>{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, + { jl_f_throw_methoderror_addr, new JuliaFunction<>{XSTR(jl_f_throw_methoderror), get_func_sig, get_func_attrs} }, { jl_f_tuple_addr, jltuple_func }, { jl_f_svec_addr, new JuliaFunction<>{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, { jl_f_applicable_addr, new JuliaFunction<>{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, @@ -1644,7 +1645,8 @@ static const auto &builtin_func_map() { { jl_f_donotdelete_addr, new JuliaFunction<>{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }, { jl_f_compilerbarrier_addr, new JuliaFunction<>{XSTR(jl_f_compilerbarrier), get_func_sig, get_func_attrs} }, { jl_f_finalizer_addr, new JuliaFunction<>{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }, - { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} } + { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} }, + { jl_f_current_scope_addr, new JuliaFunction<>{XSTR(jl_f_current_scope), get_func_sig, get_func_attrs} }, }; return builtins; } @@ -2117,6 +2119,7 @@ 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); static Value *get_current_task(jl_codectx_t &ctx); static Value *get_current_ptls(jl_codectx_t &ctx); +static Value *get_scope_field(jl_codectx_t &ctx); static Value *get_tls_world_age_field(jl_codectx_t &ctx); static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block = true); static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF, @@ -4944,6 +4947,14 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } + else if (f == jl_builtin_current_scope && (nargs == 0)) { + jl_aliasinfo_t scope_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + Instruction *v = scope_ai.decorateInst( + ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, get_scope_field(ctx), ctx.types().alignof_ptr)); + *ret = mark_julia_type(ctx, v, /*boxed*/ true, rt); + return true; + } + else if (f == jl_builtin_donotdelete) { // For now we emit this as a vararg call to the builtin // (which doesn't look at the arguments). In the future, diff --git a/src/staticdata.c b/src/staticdata.c index b991dfe8f37f3..363aa46b62221 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -100,7 +100,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 192 +#define NUM_TAGS 193 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -312,6 +312,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_builtin_modifyglobal); INSERT_TAG(jl_builtin_replaceglobal); INSERT_TAG(jl_builtin_setglobalonce); + INSERT_TAG(jl_builtin_current_scope); // n.b. must update NUM_TAGS when you add something here #undef INSERT_TAG assert(i == NUM_TAGS - 1); From 58b239c5b8eaffec3b9b99e6d7c37e8ae6129d6d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:08:34 +0900 Subject: [PATCH 232/548] a minor improvement for EA-based `:effect_free`-ness refinement (#55796) --- base/compiler/optimize.jl | 6 ++++-- base/compiler/types.jl | 2 +- test/compiler/effects.jl | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index fb712b1c71b12..6b0cf981930ad 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -702,6 +702,8 @@ function check_all_args_noescape!(sv::PostOptAnalysisState, ir::IRCode, @nospeci else return false end + has_no_escape(x::EscapeAnalysis.EscapeInfo) = + EscapeAnalysis.has_no_escape(EscapeAnalysis.ignore_argescape(x)) for i = startidx:length(stmt.args) arg = stmt.args[i] argt = argextype(arg, ir) @@ -710,7 +712,7 @@ function check_all_args_noescape!(sv::PostOptAnalysisState, ir::IRCode, @nospeci end # See if we can find the allocation if isa(arg, Argument) - if EscapeAnalysis.has_no_escape(EscapeAnalysis.ignore_argescape(estate[arg])) + if has_no_escape(estate[arg]) # Even if we prove everything else effect_free, the best we can # say is :effect_free_if_argmem_only if sv.effect_free_if_argmem_only === nothing @@ -721,7 +723,7 @@ function check_all_args_noescape!(sv::PostOptAnalysisState, ir::IRCode, @nospeci end return false elseif isa(arg, SSAValue) - EscapeAnalysis.has_no_escape(estate[arg]) || return false + has_no_escape(estate[arg]) || return false check_all_args_noescape!(sv, ir, ir[arg][:stmt], estate) || return false else return false diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 015b1dbc00a6f..b475e360dac02 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -457,7 +457,7 @@ getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx) # must implement `nsplit`, `getsplit`, and `add_uncovered_edges!` to opt in to inlining nsplit_impl(::CallInfo) = nothing getsplit_impl(::CallInfo, ::Int) = error("unexpected call into `getsplit`") -add_uncovered_edges_impl(edges::Vector{Any}, info::CallInfo, @nospecialize(atype)) = error("unexpected call into `add_uncovered_edges!`") +add_uncovered_edges_impl(::Vector{Any}, ::CallInfo, _) = error("unexpected call into `add_uncovered_edges!`") # must implement `getresult` to opt in to extended lattice return information getresult_impl(::CallInfo, ::Int) = nothing diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 11c30aad0b9a4..8bc5f27e31766 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1141,6 +1141,14 @@ end @test_broken Core.Compiler.is_effect_free(Base.infer_effects(set_arr_with_unused_arg_2, (Vector{Int},))) @test_broken Core.Compiler.is_effect_free_if_inaccessiblememonly(Base.infer_effects(set_arg_arr!, (Vector{Int},))) +# EA-based refinement of :effect_free +function f_EA_refine(ax, b) + bx = Ref{Any}() + @noinline bx[] = b + return ax[] + b +end +@test Core.Compiler.is_effect_free(Base.infer_effects(f_EA_refine, (Base.RefValue{Int},Int))) + function issue51837(; openquotechar::Char, newlinechar::Char) ncodeunits(openquotechar) == 1 || throw(ArgumentError("`openquotechar` must be a single-byte character")) if !isnothing(newlinechar) From 4045e7baa496b2f14dfe562dbe3700d0c91006ff Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 19 Sep 2024 07:37:10 -0400 Subject: [PATCH 233/548] fix #52986, regression in `@doc` of macro without REPL loaded (#55795) fix #52986 --- base/docs/Docs.jl | 4 ++++ test/docs.jl | 1 + 2 files changed, 5 insertions(+) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 1327a1f795d4f..1a2403bbb8644 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -577,6 +577,10 @@ function _doc(binding::Binding, sig::Type = Union{}) for msig in multidoc.order sig <: msig && return multidoc.docs[msig] end + # if no matching signatures, return first + if !isempty(multidoc.docs) + return first(values(multidoc.docs)) + end end end return nothing diff --git a/test/docs.jl b/test/docs.jl index f62f7f8b63b2c..a6ef6afec5807 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -4,6 +4,7 @@ import Base.Docs: meta, @var, DocStr, parsedoc # check that @doc can work before REPL is loaded @test !startswith(read(`$(Base.julia_cmd()) -E '@doc sin'`, String), "nothing") +@test !startswith(read(`$(Base.julia_cmd()) -E '@doc @time'`, String), "nothing") using Markdown using REPL From a73ba3bab7ddbf087bb64ef8d236923d8d7f0051 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 19 Sep 2024 19:38:28 +0800 Subject: [PATCH 234/548] Assume that docstring code with no lang is julia (#55465) --- stdlib/Markdown/src/render/terminal/render.jl | 2 +- stdlib/REPL/src/docview.jl | 24 ++++++++++++++++++- test/docs.jl | 4 ++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/stdlib/Markdown/src/render/terminal/render.jl b/stdlib/Markdown/src/render/terminal/render.jl index 619b2c8b8ef4a..a97d273131536 100644 --- a/stdlib/Markdown/src/render/terminal/render.jl +++ b/stdlib/Markdown/src/render/terminal/render.jl @@ -116,7 +116,7 @@ function term(io::AnnotIO, md::Header{l}, columns) where l end function term(io::IO, md::Code, columns) - code = if md.language ∈ ("", "julia") + code = if md.language == "julia" highlight(md.code) elseif md.language == "julia-repl" || Base.startswith(md.language, "jldoctest") hl = AnnotatedString(md.code) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 5086aa0c9485c..3c5e102bb657e 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -81,7 +81,8 @@ function formatdoc(d::DocStr) for part in d.text formatdoc(buffer, d, part) end - Markdown.MD(Any[Markdown.parse(seekstart(buffer))]) + md = Markdown.MD(Any[Markdown.parse(seekstart(buffer))]) + assume_julia_code!(md) end @noinline formatdoc(buffer, d, part) = print(buffer, part) @@ -95,6 +96,27 @@ function parsedoc(d::DocStr) d.object end +""" + assume_julia_code!(doc::Markdown.MD) -> doc + +Assume that code blocks with no language specified are Julia code. +""" +function assume_julia_code!(doc::Markdown.MD) + assume_julia_code!(doc.content) + doc +end + +function assume_julia_code!(blocks::Vector) + for (i, block) in enumerate(blocks) + if block isa Markdown.Code && block.language == "" + blocks[i] = Markdown.Code("julia", block.code) + elseif block isa Vector || block isa Markdown.MD + assume_julia_code!(block) + end + end + blocks +end + ## Trimming long help ("# Extended help") struct Message # For direct messages to the terminal diff --git a/test/docs.jl b/test/docs.jl index a6ef6afec5807..92d45fe05e397 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -575,8 +575,8 @@ end let T = meta(DocVars)[@var(DocVars.T)], S = meta(DocVars)[@var(DocVars.S)], - Tname = Markdown.parse("```\n$(curmod_prefix)DocVars.T\n```"), - Sname = Markdown.parse("```\n$(curmod_prefix)DocVars.S\n```") + Tname = Markdown.parse("```julia\n$(curmod_prefix)DocVars.T\n```"), + Sname = Markdown.parse("```julia\n$(curmod_prefix)DocVars.S\n```") # Splicing the expression directly doesn't work @test docstrings_equal(T.docs[Union{}], doc""" From b8093deefac19084646db9cfce0bf8ad7ea91ed8 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 19 Sep 2024 23:31:36 +0530 Subject: [PATCH 235/548] Broadcast binary ops involving strided triangular (#55798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we evaluate expressions like `(A::UpperTriangular) + (B::UpperTriangular)` using broadcasting if both `A` and `B` have strided parents, and forward the summation to the parents otherwise. This PR changes this to use broadcasting if either of the two has a strided parent. This avoids accessing the parent corresponding to the structural zero elements, as the index might not be initialized. Fixes https://github.com/JuliaLang/julia/issues/55590 This isn't a general fix, as we still sum the parents if neither is strided. However, it will address common cases. This also improves performance, as we only need to loop over one half: ```julia julia> using LinearAlgebra julia> U = UpperTriangular(zeros(100,100)); julia> B = Bidiagonal(zeros(100), zeros(99), :U); julia> @btime $U + $B; 35.530 μs (4 allocations: 78.22 KiB) # nightly 13.441 μs (4 allocations: 78.22 KiB) # This PR ``` --- stdlib/LinearAlgebra/src/symmetric.jl | 8 +-- stdlib/LinearAlgebra/src/triangular.jl | 91 ++++++++++++++++++-------- stdlib/LinearAlgebra/test/symmetric.jl | 25 +++++++ 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index ab7b5ee031260..a7739596a73bb 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -687,10 +687,10 @@ for f in (:+, :-) @eval begin $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo(B.uplo))) $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo(A.uplo)), B) - $f(A::SymTridiagonal, B::Symmetric) = Symmetric($f(A, B.data), sym_uplo(B.uplo)) - $f(A::Symmetric, B::SymTridiagonal) = Symmetric($f(A.data, B), sym_uplo(A.uplo)) - $f(A::SymTridiagonal{<:Real}, B::Hermitian) = Hermitian($f(A, B.data), sym_uplo(B.uplo)) - $f(A::Hermitian, B::SymTridiagonal{<:Real}) = Hermitian($f(A.data, B), sym_uplo(A.uplo)) + $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, sym_uplo(B.uplo)), B) + $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, sym_uplo(A.uplo))) + $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, sym_uplo(B.uplo)), B) + $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, sym_uplo(A.uplo))) end end diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index df0d0d4fd0d8b..74eab6a392723 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -850,35 +850,74 @@ fillstored!(A::UpperTriangular, x) = (fillband!(A.data, x, 0, size(A,2)-1); fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A) # Binary operations -+(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data) -+(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data) -+(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I) -+(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I) -+(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I) -+(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I) -+(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I) -+(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) +# use broadcasting if the parents are strided, where we loop only over the triangular part +function +(A::UpperTriangular, B::UpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + UpperTriangular(A.data + B.data) +end +function +(A::LowerTriangular, B::LowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + LowerTriangular(A.data + B.data) +end +function +(A::UpperTriangular, B::UnitUpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + UpperTriangular(A.data + triu(B.data, 1) + I) +end +function +(A::LowerTriangular, B::UnitLowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + LowerTriangular(A.data + tril(B.data, -1) + I) +end +function +(A::UnitUpperTriangular, B::UpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + UpperTriangular(triu(A.data, 1) + B.data + I) +end +function +(A::UnitLowerTriangular, B::LowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + LowerTriangular(tril(A.data, -1) + B.data + I) +end +function +(A::UnitUpperTriangular, B::UnitUpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I) +end +function +(A::UnitLowerTriangular, B::UnitLowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B + LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) +end +(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B) --(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data) --(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data) --(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I) --(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I) --(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I) --(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I) --(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1)) --(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) --(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) - -# use broadcasting if the parents are strided, where we loop only over the triangular part -for op in (:+, :-) - for TM1 in (:LowerTriangular, :UnitLowerTriangular), TM2 in (:LowerTriangular, :UnitLowerTriangular) - @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B) - end - for TM1 in (:UpperTriangular, :UnitUpperTriangular), TM2 in (:UpperTriangular, :UnitUpperTriangular) - @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B) - end +function -(A::UpperTriangular, B::UpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + UpperTriangular(A.data - B.data) +end +function -(A::LowerTriangular, B::LowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + LowerTriangular(A.data - B.data) +end +function -(A::UpperTriangular, B::UnitUpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + UpperTriangular(A.data - triu(B.data, 1) - I) +end +function -(A::LowerTriangular, B::UnitLowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + LowerTriangular(A.data - tril(B.data, -1) - I) end +function -(A::UnitUpperTriangular, B::UpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + UpperTriangular(triu(A.data, 1) - B.data + I) +end +function -(A::UnitLowerTriangular, B::LowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + LowerTriangular(tril(A.data, -1) - B.data + I) +end +function -(A::UnitUpperTriangular, B::UnitUpperTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + UpperTriangular(triu(A.data, 1) - triu(B.data, 1)) +end +function -(A::UnitLowerTriangular, B::UnitLowerTriangular) + (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B + LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) +end +-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) function kron(A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}) C = UpperTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B))) diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 939e677039dc7..7a51ab9d454af 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1135,4 +1135,29 @@ end end end +@testset "partly iniitalized matrices" begin + a = Matrix{BigFloat}(undef, 2,2) + a[1] = 1; a[3] = 1; a[4] = 1 + h = Hermitian(a) + s = Symmetric(a) + d = Diagonal([1,1]) + symT = SymTridiagonal([1 1;1 1]) + @test h+d == Array(h) + Array(d) + @test h+symT == Array(h) + Array(symT) + @test s+d == Array(s) + Array(d) + @test s+symT == Array(s) + Array(symT) + @test h-d == Array(h) - Array(d) + @test h-symT == Array(h) - Array(symT) + @test s-d == Array(s) - Array(d) + @test s-symT == Array(s) - Array(symT) + @test d+h == Array(d) + Array(h) + @test symT+h == Array(symT) + Array(h) + @test d+s == Array(d) + Array(s) + @test symT+s == Array(symT) + Array(s) + @test d-h == Array(d) - Array(h) + @test symT-h == Array(symT) - Array(h) + @test d-s == Array(d) - Array(s) + @test symT-s == Array(symT) - Array(s) +end + end # module TestSymmetric From a31a880d9b5da6fd55e9afba9fab3715d86379c6 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 19 Sep 2024 23:34:17 +0530 Subject: [PATCH 236/548] Reland " Avoid materializing arrays in bidiag matmul #55450" (#55777) This relands #55450 and adds tests for the failing case noted in https://github.com/JuliaLang/julia/issues/55727. The `addmul` tests that were failing earlier pass with this change. The issue in the earlier PR was that we were not exiting quickly for `iszero(alpha)` in `_bibimul!` for small matrices, and were computing the result as `C .= A * B * alpha + C * beta`. The problem with this is that if `A * B` contains `NaN`s, this propagates to `C` even if `alpha === 0.0`. This is fixed now, and the result is only computed if `!iszero(alpha)`. --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 4 +- stdlib/LinearAlgebra/src/bidiag.jl | 341 ++++++++++++++++++---- stdlib/LinearAlgebra/test/addmul.jl | 22 ++ stdlib/LinearAlgebra/test/bidiag.jl | 67 +++++ stdlib/LinearAlgebra/test/tridiag.jl | 71 +++++ 5 files changed, 453 insertions(+), 52 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 27d4255fb656b..17216845b350c 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -673,7 +673,9 @@ matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) - Tridiagonal(similar(A, TS, n-1), similar(A, TS, n), similar(A, TS, n-1)) + ev = similar(A, TS, max(0, n-1)) + dv = similar(A, TS, n) + Tridiagonal(ev, dv, similar(ev)) end # Special handling for adj/trans vec diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index d86bad7e41435..12d638f52add6 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -557,7 +557,8 @@ end # function to get the internally stored vectors for Bidiagonal and [Sym]Tridiagonal # to avoid allocations in _mul! below (#24324, #24578) _diag(A::Tridiagonal, k) = k == -1 ? A.dl : k == 0 ? A.d : A.du -_diag(A::SymTridiagonal, k) = k == 0 ? A.dv : A.ev +_diag(A::SymTridiagonal{<:Number}, k) = k == 0 ? A.dv : A.ev +_diag(A::SymTridiagonal, k) = k == 0 ? view(A, diagind(A, IndexStyle(A))) : view(A, diagind(A, 1, IndexStyle(A))) function _diag(A::Bidiagonal, k) if k == 0 return A.dv @@ -577,12 +578,45 @@ function _bibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the # off-diagonal elements for non-zero beta. _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C + if n <= 3 + # naive multiplication + for I in CartesianIndices(C) + C[I] += _add(sum(A[I[1], k] * B[k, I[2]] for k in axes(A,2))) + end + return C + end + @inbounds begin + # first column of C + C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2,1]) + C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) + C[3,1] += _add(A[3,2]*B[2,1]) + # second column of C + C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) + C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) + C[3,2] += _add(A[3,2]*B[2,2] + A[3,3]*B[3,2]) + C[4,2] += _add(A[4,3]*B[3,2]) + end # inbounds + # middle columns + __bibimul!(C, A, B, _add) + @inbounds begin + C[n-3,n-1] += _add(A[n-3,n-2]*B[n-2,n-1]) + C[n-2,n-1] += _add(A[n-2,n-2]*B[n-2,n-1] + A[n-2,n-1]*B[n-1,n-1]) + C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) + C[n, n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) + # last column of C + C[n-2, n] += _add(A[n-2,n-1]*B[n-1,n]) + C[n-1, n] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1,n]*B[n,n ]) + C[n, n] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) + end # inbounds + C +end +function __bibimul!(C, A, B, _add) + n = size(A,1) Al = _diag(A, -1) Ad = _diag(A, 0) Au = _diag(A, 1) @@ -590,44 +624,198 @@ function _bibimul!(C, A, B, _add) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin - # first row of C - C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2, 1]) - C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) - C[1,3] += _add(A[1,2]*B[2,3]) - # second row of C - C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) - C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) - C[2,3] += _add(A[2,2]*B[2,3] + A[2,3]*B[3,3]) - C[2,4] += _add(A[2,3]*B[3,4]) for j in 3:n-2 - Ajj₋1 = Al[j-1] - Ajj = Ad[j] + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] Ajj₊1 = Au[j] - Bj₋1j₋2 = Bl[j-2] - Bj₋1j₋1 = Bd[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] Bj₋1j = Bu[j-1] - Bjj₋1 = Bl[j-1] Bjj = Bd[j] - Bjj₊1 = Bu[j] Bj₊1j = Bl[j] - Bj₊1j₊1 = Bd[j+1] - Bj₊1j₊2 = Bu[j+1] - C[j,j-2] += _add( Ajj₋1*Bj₋1j₋2) - C[j, j-1] += _add(Ajj₋1*Bj₋1j₋1 + Ajj*Bjj₋1) - C[j, j ] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j, j+1] += _add(Ajj *Bjj₊1 + Ajj₊1*Bj₊1j₊1) - C[j, j+2] += _add(Ajj₊1*Bj₊1j₊2) + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) end - # row before last of C - C[n-1,n-3] += _add(A[n-1,n-2]*B[n-2,n-3]) - C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2] + A[n-1,n-2]*B[n-2,n-2]) - C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) - C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1, n]*B[n ,n ]) - # last row of C - C[n,n-2] += _add(A[n,n-1]*B[n-1,n-2]) - C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) - C[n,n ] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) - end # inbounds + end + C +end +function __bibimul!(C, A, B::Bidiagonal, _add) + n = size(A,1) + Al = _diag(A, -1) + Ad = _diag(A, 0) + Au = _diag(A, 1) + Bd = _diag(B, 0) + if B.uplo == 'U' + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj) + end + end + else # B.uplo == 'L' + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end + C +end +function __bibimul!(C, A::Bidiagonal, B, _add) + n = size(A,1) + Bl = _diag(B, -1) + Bd = _diag(B, 0) + Bu = _diag(B, 1) + Ad = _diag(A, 0) + if A.uplo == 'U' + Au = _diag(A, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) + end + end + else # A.uplo == 'L' + Al = _diag(A, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end + C +end +function __bibimul!(C, A::Bidiagonal, B::Bidiagonal, _add) + n = size(A,1) + Ad = _diag(A, 0) + Bd = _diag(B, 0) + if A.uplo == 'U' && B.uplo == 'U' + Au = _diag(A, 1) + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋2j₋1 = Au[j-2] + Aj₋1j = Au[j-1] + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj) + end + end + elseif A.uplo == 'U' && B.uplo == 'L' + Au = _diag(A, 1) + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Aj₋1j = Au[j-1] + Ajj₊1 = Au[j] + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j-1, j] += _add(Aj₋1j*Bjj) + C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) + C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) + end + end + elseif A.uplo == 'L' && B.uplo == 'U' + Al = _diag(A, -1) + Bu = _diag(B, 1) + @inbounds begin + for j in 3:n-2 + Aj₋1j₋1 = Ad[j-1] + Ajj = Ad[j] + Ajj₋1 = Al[j-1] + Aj₊1j = Al[j] + Bj₋1j = Bu[j-1] + Bjj = Bd[j] + + C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) + C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj) + end + end + else # A.uplo == 'L' && B.uplo == 'L' + Al = _diag(A, -1) + Bl = _diag(B, -1) + @inbounds begin + for j in 3:n-2 + Ajj = Ad[j] + Aj₊1j₊1 = Ad[j+1] + Aj₊1j = Al[j] + Aj₊2j₊1 = Al[j+1] + Bjj = Bd[j] + Bj₊1j = Bl[j] + + C[j, j] += _add(Ajj*Bjj) + C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) + C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) + end + end + end C end @@ -744,7 +932,52 @@ function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulA nB = size(B,2) (iszero(nA) || iszero(nB)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + if nA <= 3 + # naive multiplication + for I in CartesianIndices(C) + col = Base.tail(Tuple(I)) + _modify!(_add, sum(A[I[1], k] * B[k, col...] for k in axes(A,2)), C, I) + end + return C + end + _mul_bitrisym!(C, A, B, _add) +end +function _mul_bitrisym!(C::AbstractVecOrMat, A::Bidiagonal, B::AbstractVecOrMat, _add::MulAddMul) + nA = size(A,1) + nB = size(B,2) + d = A.dv + if A.uplo == 'U' + u = A.ev + @inbounds begin + for j = 1:nB + b₀, b₊ = B[1, j], B[2, j] + _modify!(_add, d[1]*b₀ + u[1]*b₊, C, (1, j)) + for i = 2:nA - 1 + b₀, b₊ = b₊, B[i + 1, j] + _modify!(_add, d[i]*b₀ + u[i]*b₊, C, (i, j)) + end + _modify!(_add, d[nA]*b₊, C, (nA, j)) + end + end + else + l = A.ev + @inbounds begin + for j = 1:nB + b₀, b₊ = B[1, j], B[2, j] + _modify!(_add, d[1]*b₀, C, (1, j)) + for i = 2:nA - 1 + b₋, b₀, b₊ = b₀, b₊, B[i + 1, j] + _modify!(_add, l[i - 1]*b₋ + d[i]*b₀, C, (i, j)) + end + _modify!(_add, l[nA - 1]*b₀ + d[nA]*b₊, C, (nA, j)) + end + end + end + C +end +function _mul_bitrisym!(C::AbstractVecOrMat, A::TriSym, B::AbstractVecOrMat, _add::MulAddMul) + nA = size(A,1) + nB = size(B,2) l = _diag(A, -1) d = _diag(A, 0) u = _diag(A, 1) @@ -767,10 +1000,10 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) m = size(B,2) - (iszero(m) || iszero(n)) && return C - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if n <= 3 || m <= 1 - return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) + (iszero(_add.alpha) || iszero(m)) && return _rmul_or_fill!(C, _add.beta) + if m == 1 + B11 = B[1,1] + return mul!(C, A, B11, _add.alpha, _add.beta) end Bl = _diag(B, -1) Bd = _diag(B, 0) @@ -804,21 +1037,18 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAdd m, n = size(A) (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if size(A, 1) <= 3 || size(B, 2) <= 1 - return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) - end @inbounds if B.uplo == 'U' + for j in n:-1:2, i in 1:m + _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) + end for i in 1:m - for j in n:-1:2 - _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) - end _modify!(_add, A[i,1] * B.dv[1], C, (i, 1)) end else # uplo == 'L' + for j in 1:n-1, i in 1:m + _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) + end for i in 1:m - for j in 1:n-1 - _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) - end _modify!(_add, A[i,n] * B.dv[n], C, (i, n)) end end @@ -834,9 +1064,17 @@ function _dibimul!(C, A, B, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) - _rmul_or_fill!(C, _add.beta) # see the same use above + # ensure that we fill off-band elements in the destination + _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C + if n <= 3 + # For simplicity, use a naive multiplication for small matrices + # that loops over all elements. + for I in CartesianIndices(C) + C[I] += _add(A.diag[I[1]] * B[I[1], I[2]]) + end + return C + end Ad = A.diag Bl = _diag(B, -1) Bd = _diag(B, 0) @@ -870,7 +1108,8 @@ function _dibimul!(C::AbstractMatrix, A::Diagonal, B::Bidiagonal, _add) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) iszero(n) && return C - _rmul_or_fill!(C, _add.beta) # see the same use above + # ensure that we fill off-band elements in the destination + _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C Ad = A.diag Bdv, Bev = B.dv, B.ev diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 72fdf687bf5c3..208fa930e8ee1 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -217,4 +217,26 @@ end end end +@testset "issue #55727" begin + C = zeros(1,1) + @testset "$(nameof(typeof(A)))" for A in Any[Diagonal([NaN]), + Bidiagonal([NaN], Float64[], :U), + Bidiagonal([NaN], Float64[], :L), + SymTridiagonal([NaN], Float64[]), + Tridiagonal(Float64[], [NaN], Float64[]), + ] + @testset "$(nameof(typeof(B)))" for B in Any[ + Diagonal([1.0]), + Bidiagonal([1.0], Float64[], :U), + Bidiagonal([1.0], Float64[], :L), + SymTridiagonal([1.0], Float64[]), + Tridiagonal(Float64[], [1.0], Float64[]), + ] + C .= 0 + @test mul!(C, A, B, 0.0, false)[] === 0.0 + @test mul!(C, B, A, 0.0, false)[] === 0.0 + end + end +end + end # module diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index ef50658a642fb..edad29d4ec180 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -1048,4 +1048,71 @@ end @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D end +@testset "mul for small matrices" begin + @testset for n in 0:6 + D = Diagonal(rand(n)) + v = rand(n) + @testset for uplo in (:L, :U) + B = Bidiagonal(rand(n), rand(max(n-1,0)), uplo) + M = Matrix(B) + + @test B * v ≈ M * v + @test mul!(similar(v), B, v) ≈ M * v + @test mul!(ones(size(v)), B, v, 2, 3) ≈ M * v * 2 .+ 3 + + @test B * B ≈ M * M + @test mul!(similar(B, size(B)), B, B) ≈ M * M + @test mul!(ones(size(B)), B, B, 2, 4) ≈ M * M * 2 .+ 4 + + for m in 0:6 + AL = rand(m,n) + AR = rand(n,m) + @test AL * B ≈ AL * M + @test B * AR ≈ M * AR + @test mul!(similar(AL), AL, B) ≈ AL * M + @test mul!(similar(AR), B, AR) ≈ M * AR + @test mul!(ones(size(AL)), AL, B, 2, 4) ≈ AL * M * 2 .+ 4 + @test mul!(ones(size(AR)), B, AR, 2, 4) ≈ M * AR * 2 .+ 4 + end + + @test B * D ≈ M * D + @test D * B ≈ D * M + @test mul!(similar(B), B, D) ≈ M * D + @test mul!(similar(B), B, D) ≈ M * D + @test mul!(similar(B, size(B)), D, B) ≈ D * M + @test mul!(similar(B, size(B)), B, D) ≈ M * D + @test mul!(ones(size(B)), D, B, 2, 4) ≈ D * M * 2 .+ 4 + @test mul!(ones(size(B)), B, D, 2, 4) ≈ M * D * 2 .+ 4 + end + BL = Bidiagonal(rand(n), rand(max(0, n-1)), :L) + ML = Matrix(BL) + BU = Bidiagonal(rand(n), rand(max(0, n-1)), :U) + MU = Matrix(BU) + T = Tridiagonal(zeros(max(0, n-1)), zeros(n), zeros(max(0, n-1))) + @test mul!(T, BL, BU) ≈ ML * MU + @test mul!(T, BU, BL) ≈ MU * ML + T = Tridiagonal(ones(max(0, n-1)), ones(n), ones(max(0, n-1))) + @test mul!(copy(T), BL, BU, 2, 3) ≈ ML * MU * 2 + T * 3 + @test mul!(copy(T), BU, BL, 2, 3) ≈ MU * ML * 2 + T * 3 + end + + n = 4 + arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) + for B in ( + Bidiagonal(fill(arr,n), fill(arr,n-1), :L), + Bidiagonal(fill(arr,n), fill(arr,n-1), :U), + ) + @test B * B ≈ Matrix(B) * Matrix(B) + BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) + BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) + @test BL * B ≈ Matrix(BL) * Matrix(B) + @test BU * B ≈ Matrix(BU) * Matrix(B) + @test B * BL ≈ Matrix(B) * Matrix(BL) + @test B * BU ≈ Matrix(B) * Matrix(BU) + D = Diagonal(fill(arr,n)) + @test D * B ≈ Matrix(D) * Matrix(B) + @test B * D ≈ Matrix(B) * Matrix(D) + end +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 3330fa682fe5e..15ac7f9f2147f 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -970,4 +970,75 @@ end @test sprint(show, S) == "SymTridiagonal($(repr(diag(S))), $(repr(diag(S,1))))" end +@testset "mul for small matrices" begin + @testset for n in 0:6 + for T in ( + Tridiagonal(rand(max(n-1,0)), rand(n), rand(max(n-1,0))), + SymTridiagonal(rand(n), rand(max(n-1,0))), + ) + M = Matrix(T) + @test T * T ≈ M * M + @test mul!(similar(T, size(T)), T, T) ≈ M * M + @test mul!(ones(size(T)), T, T, 2, 4) ≈ M * M * 2 .+ 4 + + for m in 0:6 + AR = rand(n,m) + AL = rand(m,n) + @test AL * T ≈ AL * M + @test T * AR ≈ M * AR + @test mul!(similar(AL), AL, T) ≈ AL * M + @test mul!(similar(AR), T, AR) ≈ M * AR + @test mul!(ones(size(AL)), AL, T, 2, 4) ≈ AL * M * 2 .+ 4 + @test mul!(ones(size(AR)), T, AR, 2, 4) ≈ M * AR * 2 .+ 4 + end + + v = rand(n) + @test T * v ≈ M * v + @test mul!(similar(v), T, v) ≈ M * v + + D = Diagonal(rand(n)) + @test T * D ≈ M * D + @test D * T ≈ D * M + @test mul!(Tridiagonal(similar(T)), D, T) ≈ D * M + @test mul!(Tridiagonal(similar(T)), T, D) ≈ M * D + @test mul!(similar(T, size(T)), D, T) ≈ D * M + @test mul!(similar(T, size(T)), T, D) ≈ M * D + @test mul!(ones(size(T)), D, T, 2, 4) ≈ D * M * 2 .+ 4 + @test mul!(ones(size(T)), T, D, 2, 4) ≈ M * D * 2 .+ 4 + + for uplo in (:U, :L) + B = Bidiagonal(rand(n), rand(max(0, n-1)), uplo) + @test T * B ≈ M * B + @test B * T ≈ B * M + if n <= 2 + @test mul!(Tridiagonal(similar(T)), B, T) ≈ B * M + @test mul!(Tridiagonal(similar(T)), T, B) ≈ M * B + end + @test mul!(similar(T, size(T)), B, T) ≈ B * M + @test mul!(similar(T, size(T)), T, B) ≈ M * B + @test mul!(ones(size(T)), B, T, 2, 4) ≈ B * M * 2 .+ 4 + @test mul!(ones(size(T)), T, B, 2, 4) ≈ M * B * 2 .+ 4 + end + end + end + + n = 4 + arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) + for T in ( + SymTridiagonal(fill(arr,n), fill(arr,n-1)), + Tridiagonal(fill(arr,n-1), fill(arr,n), fill(arr,n-1)), + ) + @test T * T ≈ Matrix(T) * Matrix(T) + BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) + BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) + @test BL * T ≈ Matrix(BL) * Matrix(T) + @test BU * T ≈ Matrix(BU) * Matrix(T) + @test T * BL ≈ Matrix(T) * Matrix(BL) + @test T * BU ≈ Matrix(T) * Matrix(BU) + D = Diagonal(fill(arr,n)) + @test D * T ≈ Matrix(D) * Matrix(T) + @test T * D ≈ Matrix(T) * Matrix(D) + end +end + end # module TestTridiagonal From 550f3215654906a0446f3a723abf09e85a298beb Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:59:38 +0900 Subject: [PATCH 237/548] move the test case added in #50174 to test/core.jl (#55811) Also renames the name of the test function to avoid name collision. --- test/compiler/AbstractInterpreter.jl | 5 ----- test/core.jl | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index e92b67f980942..bab4fe02a5168 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -484,11 +484,6 @@ let NoinlineModule = Module() end end -# Make sure that Core.Compiler has enough NamedTuple infrastructure -# to properly give error messages for basic kwargs... -Core.eval(Core.Compiler, quote f(;a=1) = a end) -@test_throws MethodError Core.Compiler.f(;b=2) - # custom inferred data # ==================== diff --git a/test/core.jl b/test/core.jl index 4db7f0e401fa0..d41a58a7ccb2e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8288,3 +8288,8 @@ end @test_broken (Tuple{Vararg{T}} where T) === Union{Tuple{T, T, Vararg{T}} where T, Tuple{}, Tuple{T} where T} @test sizeof(Pair{Union{typeof(Union{}),Nothing}, Union{Type{Union{}},Nothing}}(Union{}, Union{})) == 2 + +# Make sure that Core.Compiler has enough NamedTuple infrastructure +# to properly give error messages for basic kwargs... +Core.eval(Core.Compiler, quote issue50174(;a=1) = a end) +@test_throws MethodError Core.Compiler.issue50174(;b=2) From b30f80d99738161f0477832ab62da7da4d1bfe82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:18:53 +0100 Subject: [PATCH 238/548] [Random] Avoid conversion to `Float32` in `Float16` sampler (#55819) --- stdlib/Random/src/Xoshiro.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index 5569d6d5c1da5..1909effbbc9e6 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -294,7 +294,7 @@ rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt52{UInt64}}) = ran rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float16}}) = - Float16(Float32(rand(r, UInt16) >>> 5) * Float32(0x1.0p-11)) + Float16(rand(r, UInt16) >>> 5) * Float16(0x1.0p-11) rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float32}}) = Float32(rand(r, UInt32) >>> 8) * Float32(0x1.0p-24) From 7f7a472168f65043013b6b0692ac6b450ca07ae5 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:31:32 +0900 Subject: [PATCH 239/548] simplify the fields of `UnionSplitInfo` (#55815) xref: --- base/compiler/abstractinterpretation.jl | 68 +++++++++++++------------ base/compiler/stmtinfo.jl | 23 +++++---- base/compiler/tfuncs.jl | 32 ++++-------- 3 files changed, 60 insertions(+), 63 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f126389c42d2d..68b8394b72c3d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -280,6 +280,12 @@ any_ambig(info::MethodMatchInfo) = any_ambig(info.results) any_ambig(m::MethodMatches) = any_ambig(m.info) fully_covering(info::MethodMatchInfo) = info.fullmatch fully_covering(m::MethodMatches) = fully_covering(m.info) +function add_uncovered_edges!(sv::AbsIntState, info::MethodMatchInfo, @nospecialize(atype)) + fully_covering(info) || add_mt_backedge!(sv, info.mt, atype) + nothing +end +add_uncovered_edges!(sv::AbsIntState, matches::MethodMatches, @nospecialize(atype)) = + add_uncovered_edges!(sv, matches.info, atype) struct UnionSplitMethodMatches applicable::Vector{Any} @@ -287,10 +293,28 @@ struct UnionSplitMethodMatches info::UnionSplitInfo valid_worlds::WorldRange end -any_ambig(info::UnionSplitInfo) = any(any_ambig, info.matches) +any_ambig(info::UnionSplitInfo) = any(any_ambig, info.split) any_ambig(m::UnionSplitMethodMatches) = any_ambig(m.info) -fully_covering(info::UnionSplitInfo) = all(info.fullmatches) +fully_covering(info::UnionSplitInfo) = all(fully_covering, info.split) fully_covering(m::UnionSplitMethodMatches) = fully_covering(m.info) +function add_uncovered_edges!(sv::AbsIntState, info::UnionSplitInfo, @nospecialize(atype)) + all(fully_covering, info.split) && return nothing + # add mt backedges with removing duplications + for mt in uncovered_method_tables(info) + add_mt_backedge!(sv, mt, atype) + end +end +add_uncovered_edges!(sv::AbsIntState, matches::UnionSplitMethodMatches, @nospecialize(atype)) = + add_uncovered_edges!(sv, matches.info, atype) +function uncovered_method_tables(info::UnionSplitInfo) + mts = MethodTable[] + for mminfo in info.split + fully_covering(mminfo) && continue + any(mt′::MethodTable->mt′===mminfo.mt, mts) && continue + push!(mts, mminfo.mt) + end + return mts +end function find_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype); max_union_splitting::Int = InferenceParams(interp).max_union_splitting, @@ -308,43 +332,30 @@ is_union_split_eligible(𝕃::AbstractLattice, argtypes::Vector{Any}, max_union_ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype), max_methods::Int) split_argtypes = switchtupleunion(typeinf_lattice(interp), argtypes) - infos = MethodLookupResult[] + infos = MethodMatchInfo[] applicable = Any[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match valid_worlds = WorldRange() - mts = MethodTable[] - fullmatches = Bool[] for i in 1:length(split_argtypes) arg_n = split_argtypes[i]::Vector{Any} sig_n = argtypes_to_type(arg_n) mt = ccall(:jl_method_table_for, Any, (Any,), sig_n) mt === nothing && return FailedMethodMatch("Could not identify method table for call") mt = mt::MethodTable - matches = findall(sig_n, method_table(interp); limit = max_methods) - if matches === nothing + thismatches = findall(sig_n, method_table(interp); limit = max_methods) + if thismatches === nothing return FailedMethodMatch("For one of the union split cases, too many methods matched") end - push!(infos, matches) - for m in matches + for m in thismatches push!(applicable, m) push!(applicable_argtypes, arg_n) end - valid_worlds = intersect(valid_worlds, matches.valid_worlds) - thisfullmatch = any(match::MethodMatch->match.fully_covers, matches) - mt_found = false - for (i, mt′) in enumerate(mts) - if mt′ === mt - fullmatches[i] &= thisfullmatch - mt_found = true - break - end - end - if !mt_found - push!(mts, mt) - push!(fullmatches, thisfullmatch) - end + valid_worlds = intersect(valid_worlds, thismatches.valid_worlds) + thisfullmatch = any(match::MethodMatch->match.fully_covers, thismatches) + thisinfo = MethodMatchInfo(thismatches, mt, thisfullmatch) + push!(infos, thisinfo) end - info = UnionSplitInfo(infos, mts, fullmatches) + info = UnionSplitInfo(infos) return UnionSplitMethodMatches( applicable, applicable_argtypes, info, valid_worlds) end @@ -583,14 +594,7 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype) end # also need an edge to the method table in case something gets # added that did not intersect with any existing method - if isa(matches, MethodMatches) - fully_covering(matches) || add_mt_backedge!(sv, matches.info.mt, atype) - else - matches::UnionSplitMethodMatches - for (thisfullmatch, mt) in zip(matches.info.fullmatches, matches.info.mts) - thisfullmatch || add_mt_backedge!(sv, mt, atype) - end - end + add_uncovered_edges!(sv, matches, atype) return nothing end diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 33fca90b6261e..ac5ffbdd5d76d 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -39,7 +39,10 @@ end nsplit_impl(info::MethodMatchInfo) = 1 getsplit_impl(info::MethodMatchInfo, idx::Int) = (@assert idx == 1; info.results) getresult_impl(::MethodMatchInfo, ::Int) = nothing -add_uncovered_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, @nospecialize(atype)) = (!info.fullmatch && push!(edges, info.mt, atype); ) +function add_uncovered_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, @nospecialize(atype)) + fully_covering(info) || push!(edges, info.mt, atype) + nothing +end """ info::UnionSplitInfo <: CallInfo @@ -51,25 +54,25 @@ each partition (`info.matches::Vector{MethodMatchInfo}`). This info is illegal on any statement that is not a call to a generic function. """ struct UnionSplitInfo <: CallInfo - matches::Vector{MethodLookupResult} - mts::Vector{MethodTable} - fullmatches::Vector{Bool} + split::Vector{MethodMatchInfo} end nmatches(info::MethodMatchInfo) = length(info.results) function nmatches(info::UnionSplitInfo) n = 0 - for mminfo in info.matches - n += length(mminfo) + for mminfo in info.split + n += nmatches(mminfo) end return n end -nsplit_impl(info::UnionSplitInfo) = length(info.matches) -getsplit_impl(info::UnionSplitInfo, idx::Int) = info.matches[idx] +nsplit_impl(info::UnionSplitInfo) = length(info.split) +getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit(info.split[idx], 1) getresult_impl(::UnionSplitInfo, ::Int) = nothing function add_uncovered_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, @nospecialize(atype)) - for (mt, fullmatch) in zip(info.mts, info.fullmatches) - !fullmatch && push!(edges, mt, atype) + all(fully_covering, info.split) && return nothing + # add mt backedges with removing duplications + for mt in uncovered_method_tables(info) + push!(edges, mt, atype) end end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 6bb73ded8660d..ab3b50763deec 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2979,33 +2979,23 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, else (; valid_worlds, applicable) = matches update_valid_age!(sv, valid_worlds) - - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - if isa(matches, MethodMatches) - fully_covering(matches) || add_mt_backedge!(sv, matches.info.mt, atype) - else - for (thisfullmatch, mt) in zip(matches.info.fullmatches, matches.info.mts) - thisfullmatch || add_mt_backedge!(sv, mt, atype) - end - end - napplicable = length(applicable) if napplicable == 0 rt = Const(false) # never any matches + elseif !fully_covering(matches) || any_ambig(matches) + # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + rt = Bool else rt = Const(true) # has applicable matches - for i in 1:napplicable - match = applicable[i]::MethodMatch - edge = specialize_method(match)::MethodInstance - add_backedge!(sv, edge) - end - - if !fully_covering(matches) || any_ambig(matches) - # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - rt = Bool - end end + for i in 1:napplicable + match = applicable[i]::MethodMatch + edge = specialize_method(match)::MethodInstance + add_backedge!(sv, edge) + end + # also need an edge to the method table in case something gets + # added that did not intersect with any existing method + add_uncovered_edges!(sv, matches, atype) end return CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo()) end From 220742d6194acba995eda822c82fdf647d6896ee Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Fri, 20 Sep 2024 08:45:54 -0400 Subject: [PATCH 240/548] Add errorhint for nonexisting fields and properties (#55165) I played a bit with error hints and crafted this: ```julia julia> (1+2im).real ERROR: FieldError: type Complex has no field real, available fields: `re`, `im` julia> nothing.xy ERROR: FieldError: type Nothing has no field xy; Nothing has no fields at all. julia> svd(rand(2,2)).VV ERROR: FieldError: type SVD has no field VV, available fields: `U`, `S`, `Vt` Available properties: `V` ``` --------- Co-authored-by: Lilith Orion Hafner --- base/docs/basedocs.jl | 2 +- base/errorshow.jl | 31 ++++++++++++++++++++++++++++--- base/reflection.jl | 2 +- test/errorshow.jl | 14 +++++++++----- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 0fc253bd73d1c..e28b3a21659a8 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1694,7 +1694,7 @@ julia> ab = AB(1, 3) AB(1.0f0, 3.0) julia> ab.c # field `c` doesn't exist -ERROR: FieldError: type AB has no field c +ERROR: FieldError: type AB has no field `c`, available fields: `a`, `b` Stacktrace: [...] ``` diff --git a/base/errorshow.jl b/base/errorshow.jl index d805cb64fb81e..9c8aad8b6ee2c 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -378,7 +378,7 @@ end function showerror(io::IO, exc::FieldError) @nospecialize - print(io, "FieldError: type $(exc.type |> nameof) has no field $(exc.field)") + print(io, "FieldError: type $(exc.type |> nameof) has no field `$(exc.field)`") Base.Experimental.show_error_hints(io, exc) end @@ -1102,7 +1102,7 @@ end Experimental.register_error_hint(methods_on_iterable, MethodError) # Display a hint in case the user tries to access non-member fields of container type datastructures -function fielderror_hint_handler(io, exc) +function fielderror_dict_hint_handler(io, exc) @nospecialize field = exc.field type = exc.type @@ -1113,7 +1113,32 @@ function fielderror_hint_handler(io, exc) end end -Experimental.register_error_hint(fielderror_hint_handler, FieldError) +Experimental.register_error_hint(fielderror_dict_hint_handler, FieldError) + +function fielderror_listfields_hint_handler(io, exc) + fields = fieldnames(exc.type) + if isempty(fields) + print(io, "; $(nameof(exc.type)) has no fields at all.") + else + print(io, ", available fields: $(join(map(k -> "`$k`", fields), ", "))") + end + props = _propertynames_bytype(exc.type) + isnothing(props) && return + props = setdiff(props, fields) + isempty(props) && return + print(io, "\nAvailable properties: $(join(map(k -> "`$k`", props), ", "))") +end + +function _propertynames_bytype(T::Type) + which(propertynames, (T,)) === which(propertynames, (Any,)) && return nothing + inferred_names = promote_op(Val∘propertynames, T) + inferred_names isa DataType && inferred_names <: Val || return nothing + inferred_names = inferred_names.parameters[1] + inferred_names isa NTuple{<:Any, Symbol} || return nothing + return Symbol[inferred_names[i] for i in 1:length(inferred_names)] +end + +Experimental.register_error_hint(fielderror_listfields_hint_handler, FieldError) # ExceptionStack implementation size(s::ExceptionStack) = size(s.stack) diff --git a/base/reflection.jl b/base/reflection.jl index 2ddd34b0f73c1..5b395efc58190 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1017,7 +1017,7 @@ julia> struct Foo end julia> Base.fieldindex(Foo, :z) -ERROR: FieldError: type Foo has no field z +ERROR: FieldError: type Foo has no field `z`, available fields: `x`, `y` Stacktrace: [...] diff --git a/test/errorshow.jl b/test/errorshow.jl index a82ab7743dc5a..3ede370553212 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -10,7 +10,8 @@ Base.Experimental.register_error_hint(Base.noncallable_number_hint_handler, Meth Base.Experimental.register_error_hint(Base.string_concatenation_hint_handler, MethodError) Base.Experimental.register_error_hint(Base.methods_on_iterable, MethodError) Base.Experimental.register_error_hint(Base.nonsetable_type_hint_handler, MethodError) -Base.Experimental.register_error_hint(Base.fielderror_hint_handler, FieldError) +Base.Experimental.register_error_hint(Base.fielderror_listfields_hint_handler, FieldError) +Base.Experimental.register_error_hint(Base.fielderror_dict_hint_handler, FieldError) @testset "SystemError" begin err = try; systemerror("reason", Cint(0)); false; catch ex; ex; end::SystemError @@ -808,12 +809,13 @@ end @test_throws ArgumentError("invalid index: \"foo\" of type String") [1]["foo"] @test_throws ArgumentError("invalid index: nothing of type Nothing") [1][nothing] -# issue #53618 -@testset "FieldErrorHint" begin +# issue #53618, pr #55165 +@testset "FieldErrorHints" begin struct FieldFoo a::Float32 b::Int end + Base.propertynames(foo::FieldFoo) = (:a, :x, :y) s = FieldFoo(1, 2) @@ -823,7 +825,9 @@ end # Check error message first errorMsg = sprint(Base.showerror, ex) - @test occursin("FieldError: type FieldFoo has no field c", errorMsg) + @test occursin("FieldError: type FieldFoo has no field `c`", errorMsg) + @test occursin("available fields: `a`, `b`", errorMsg) + @test occursin("Available properties: `x`, `y`", errorMsg) d = Dict(s => 1) @@ -840,7 +844,7 @@ end ex = test.value::FieldError errorMsg = sprint(Base.showerror, ex) - @test occursin("FieldError: type Dict has no field c", errorMsg) + @test occursin("FieldError: type Dict has no field `c`", errorMsg) # Check hint message hintExpected = "Did you mean to access dict values using key: `:c` ? Consider using indexing syntax dict[:c]\n" @test occursin(hintExpected, errorMsg) From 44bef0df7a115334c10abac88aeba333b12cce2d Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:59:39 +0200 Subject: [PATCH 241/548] Improve printing of several arguments (#55754) Following a discussion on [Discourse](https://discourse.julialang.org/t/string-optimisation-in-julia/119301/10?u=gdalle), this PR tries to improve `print` (and variants) for more than one argument. The idea is that `for` is type-unstable over the tuple `args`, while `foreach` unrolls. --------- Co-authored-by: Steven G. Johnson --- base/strings/io.jl | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index 754e058cd2f54..c78e3e2e043b6 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -42,9 +42,7 @@ end function print(io::IO, xs...) lock(io) try - for x in xs - print(io, x) - end + foreach(Fix1(print, io), xs) finally unlock(io) end @@ -138,15 +136,9 @@ function print_to_string(xs...) if isempty(xs) return "" end - siz::Int = 0 - for x in xs - siz += _str_sizehint(x) - end - # specialized for performance reasons + siz = sum(_str_sizehint, xs; init = 0) s = IOBuffer(sizehint=siz) - for x in xs - print(s, x) - end + print(s, xs...) String(_unsafe_take!(s)) end @@ -154,16 +146,10 @@ function string_with_env(env, xs...) if isempty(xs) return "" end - siz::Int = 0 - for x in xs - siz += _str_sizehint(x) - end - # specialized for performance reasons + siz = sum(_str_sizehint, xs; init = 0) s = IOBuffer(sizehint=siz) env_io = IOContext(s, env) - for x in xs - print(env_io, x) - end + print(env_io, xs...) String(_unsafe_take!(s)) end From bce8f0441edae17d31abc7e7e7541659d4658704 Mon Sep 17 00:00:00 2001 From: Denis Barucic Date: Fri, 20 Sep 2024 22:23:06 +0200 Subject: [PATCH 242/548] Markdown: support `parse(::AbstractString)` (#55747) `Markdown.parse` is documented to accept `AbstractString` but it was implemented by calling `IOBuffer` on the string argument. `IOBuffer`, however, is documented only for `String` arguments. This commit changes the current `parse(::AbstractString)` to `parse(::String)` and implements `parse(::AbstractString)` by converting the argument to `String`. Now, even `LazyString`s can be parsed to Markdown representation. Fixes #55732 --- stdlib/Markdown/src/Markdown.jl | 3 ++- stdlib/Markdown/test/runtests.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/Markdown/src/Markdown.jl b/stdlib/Markdown/src/Markdown.jl index 1832e3a6a6956..0d45d9e534df2 100644 --- a/stdlib/Markdown/src/Markdown.jl +++ b/stdlib/Markdown/src/Markdown.jl @@ -56,7 +56,8 @@ const MARKDOWN_FACES = [ __init__() = foreach(addface!, MARKDOWN_FACES) -parse(markdown::AbstractString; flavor = julia) = parse(IOBuffer(markdown), flavor = flavor) +parse(markdown::String; flavor = julia) = parse(IOBuffer(markdown), flavor = flavor) +parse(markdown::AbstractString; flavor = julia) = parse(String(markdown), flavor = flavor) parse_file(file::AbstractString; flavor = julia) = parse(read(file, String), flavor = flavor) function mdexpr(s, flavor = :julia) diff --git a/stdlib/Markdown/test/runtests.jl b/stdlib/Markdown/test/runtests.jl index ffdb735f3b7cd..35608f75b2426 100644 --- a/stdlib/Markdown/test/runtests.jl +++ b/stdlib/Markdown/test/runtests.jl @@ -1308,3 +1308,7 @@ end # https://github.com/JuliaLang/julia/issues/37757 @test insert_hlines(nothing) === nothing end + +@testset "Lazy Strings" begin + @test Markdown.parse(lazy"foo") == Markdown.parse("foo") +end From 911e02558d0c145a192facd28808b68e157aa5af Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Sat, 21 Sep 2024 09:58:15 +0200 Subject: [PATCH 243/548] better error for esc outside of macro expansion (#55797) fixes #55788 --------- Co-authored-by: Jeff Bezanson --- src/julia-syntax.scm | 4 ++++ test/syntax.jl | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index d6bc03091f37b..f1acb9c3250e1 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -5001,6 +5001,10 @@ f(x) = yt(x) ((≔ ⩴ ≕ :=) (error (string "unsupported assignment operator \"" (deparse (car e)) "\""))) + ;; bare :escape + ((escape) + (error (string "\"esc(...)\" used outside of macro expansion"))) + ((error) (error (cadr e))) (else diff --git a/test/syntax.jl b/test/syntax.jl index 1b630a56f84f8..c19721b5c54b3 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3985,3 +3985,5 @@ begin end end @test f45494() === (0,) + +@test_throws "\"esc(...)\" used outside of macro expansion" eval(esc(:(const x=1))) From d9555c6e7286d121b8625013548f945bc0bffc58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateus=20Ara=C3=BAjo?= Date: Sun, 22 Sep 2024 08:33:25 +0200 Subject: [PATCH 244/548] allow kronecker product between recursive triangular matrices (#55527) Using the recently introduced recursive `zero` I can remove the specialization to `<:Number` as @dkarrasch wanted to do in #54413. --------- Co-authored-by: Jishnu Bhattacharya --- stdlib/LinearAlgebra/src/triangular.jl | 16 ++++++++-------- stdlib/LinearAlgebra/test/triangular.jl | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 74eab6a392723..03634aa7d68e1 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -919,21 +919,21 @@ function -(A::UnitLowerTriangular, B::UnitLowerTriangular) end -(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) -function kron(A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}) - C = UpperTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B))) +function kron(A::UpperTriangular{T,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{S,<:StridedMaybeAdjOrTransMat}) where {T,S} + C = UpperTriangular(Matrix{promote_op(*, T, S)}(undef, _kronsize(A, B))) return kron!(C, A, B) end -function kron(A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}) - C = LowerTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B))) +function kron(A::LowerTriangular{T,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{S,<:StridedMaybeAdjOrTransMat}) where {T,S} + C = LowerTriangular(Matrix{promote_op(*, T, S)}(undef, _kronsize(A, B))) return kron!(C, A, B) end -function kron!(C::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}) +function kron!(C::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, A::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) _triukron!(C.data, A.data, B.data) return C end -function kron!(C::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}) +function kron!(C::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, A::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) _trilkron!(C.data, A.data, B.data) return C @@ -952,7 +952,7 @@ function _triukron!(C, A, B) C[inB+k, jnB+l] = Aij * B[k, l] end for k = 1:(l-1) - C[inB+l, jnB+k] = zero(eltype(C)) + C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) end end end @@ -984,7 +984,7 @@ function _trilkron!(C, A, B) C[inB+k, jnB+l] = Aij * B[k, l] end for k = (l+1):n_B - C[inB+l, jnB+k] = zero(eltype(C)) + C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) end end end diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index a09d0092e9f39..42c5494f73e6f 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1048,6 +1048,9 @@ end @test 2\L == 2\B @test real(L) == real(B) @test imag(L) == imag(B) + if MT == LowerTriangular + @test isa(kron(L,L), MT) + end @test kron(L,L) == kron(B,B) @test transpose!(MT(copy(A))) == transpose(L) broken=!(A isa Matrix) @test adjoint!(MT(copy(A))) == adjoint(L) broken=!(A isa Matrix) @@ -1070,6 +1073,9 @@ end @test 2\U == 2\B @test real(U) == real(B) @test imag(U) == imag(B) + if MT == UpperTriangular + @test isa(kron(U,U), MT) + end @test kron(U,U) == kron(B,B) @test transpose!(MT(copy(A))) == transpose(U) broken=!(A isa Matrix) @test adjoint!(MT(copy(A))) == adjoint(U) broken=!(A isa Matrix) @@ -1081,10 +1087,20 @@ end for T in (UpperTriangular, LowerTriangular) t = T(fill(ones(2,2), 2, 2)) m = Matrix(t) + @test isa(kron(t,t), T) @test kron(t, t) ≈ kron(m, m) end end +@testset "kron with triangular matrices of mixed eltypes" begin + for T in (UpperTriangular, LowerTriangular) + U = T(Matrix{Union{Missing,Int}}(fill(2, 2, 2))) + U[1, 1] = missing + @test kron(U, U)[2, 3] == 0 + @test kron(U, U)[3, 2] == 0 + end +end + @testset "copyto! tests" begin @testset "copyto! with aliasing (#39460)" begin M = Matrix(reshape(1:36, 6, 6)) From d6fa66ff6a983c08fce478346241879c7db31dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:08:59 +0100 Subject: [PATCH 245/548] [Dates] Make test more robust against non-UTC timezones (#55829) `%M` is the format specifier for the minutes, not the month (which should be `%m`), and it was used twice. Also, on macOS `Libc.strptime` internally calls `mktime` which depends on the local timezone. We now temporarily set `TZ=UTC` to avoid depending on the local timezone. Fix #55827. --- stdlib/Dates/test/types.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index f5284b376ca4a..29395ccf3a271 100644 --- a/stdlib/Dates/test/types.jl +++ b/stdlib/Dates/test/types.jl @@ -263,7 +263,11 @@ end end @testset "issue #31524" begin - dt1 = Libc.strptime("%Y-%M-%dT%H:%M:%SZ", "2018-11-16T10:26:14Z") + # Ensure the result doesn't depend on local timezone, especially on macOS + # where an extra internal call to `mktime` is affected by timezone settings. + dt1 = withenv("TZ" => "UTC") do + Libc.strptime("%Y-%m-%dT%H:%M:%SZ", "2018-11-16T10:26:14Z") + end dt2 = Libc.TmStruct(14, 30, 5, 10, 1, 99, 3, 40, 0) time = Time(dt1) From 4964c9789c571d86884078de81040847e1e3d21d Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 22 Sep 2024 10:59:09 -0400 Subject: [PATCH 246/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20308f9d32f=20to=20ef9f76c17=20(#55838)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 | 1 - .../Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 | 1 - .../Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 | 1 + .../Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 create mode 100644 deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 diff --git a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 deleted file mode 100644 index b59e1d8427b8b..0000000000000 --- a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b48c15e727d96a7525e0b800180d46f4 diff --git a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 b/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 deleted file mode 100644 index 4f4bce61f1f0f..0000000000000 --- a/deps/checksums/Pkg-308f9d32fcec769fbed8cf6c5a17d54753ca1f5b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -edc2c19bccf6b00e3ea7c4e0b1af36ca86c7e3f521d8c3c05a930ce3d961fb0259a98ae27be5c3e052418f9b4e7ca74cc4d3fee59dac12d47bd1ac5cd9e34fbe diff --git a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 new file mode 100644 index 0000000000000..39dbb56dbaf53 --- /dev/null +++ b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 @@ -0,0 +1 @@ +080b5cb82d208245cba014f1dfcb8033 diff --git a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 new file mode 100644 index 0000000000000..2f95d4a0e28da --- /dev/null +++ b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 @@ -0,0 +1 @@ +1b91505c78d2608afa89ceea16f645bb41c0737815aec1853ad72c9751e7299b264135c9a40a6319f68b973073a151619b925d7a9655c46526bccf501b116113 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 602fbcc648e59..f5ca169a775c6 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 308f9d32fcec769fbed8cf6c5a17d54753ca1f5b +PKG_SHA1 = ef9f76c175872bab6803da4a5fa3fd99bce3d03a PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 9136bddb6c36050e03529e2db456e6ea2e380557 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 22 Sep 2024 22:10:08 +0530 Subject: [PATCH 247/548] lmul!/rmul! for banded matrices (#55823) This adds fast methods for `lmul!` and `rmul!` between banded matrices and numbers. Performance impact: ```julia julia> T = Tridiagonal(rand(999), rand(1000), rand(999)); julia> @btime rmul!($T, 0.2); 4.686 ms (0 allocations: 0 bytes) # nightly v"1.12.0-DEV.1225" 669.355 ns (0 allocations: 0 bytes) # this PR ``` --- stdlib/LinearAlgebra/src/bidiag.jl | 26 +++++++++++++++ stdlib/LinearAlgebra/src/diagonal.jl | 20 ++++++++++++ stdlib/LinearAlgebra/src/tridiag.jl | 47 +++++++++++++++++++++++++++ stdlib/LinearAlgebra/test/bidiag.jl | 13 ++++++++ stdlib/LinearAlgebra/test/diagonal.jl | 11 +++++++ stdlib/LinearAlgebra/test/tridiag.jl | 13 ++++++++ 6 files changed, 130 insertions(+) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 12d638f52add6..0aab9ceeca6b9 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -441,6 +441,32 @@ end -(A::Bidiagonal)=Bidiagonal(-A.dv,-A.ev,A.uplo) *(A::Bidiagonal, B::Number) = Bidiagonal(A.dv*B, A.ev*B, A.uplo) *(B::Number, A::Bidiagonal) = Bidiagonal(B*A.dv, B*A.ev, A.uplo) +function rmul!(B::Bidiagonal, x::Number) + if size(B,1) > 1 + isupper = B.uplo == 'U' + row, col = 1 + isupper, 1 + !isupper + # ensure that zeros are preserved on scaling + y = B[row,col] * x + iszero(y) || throw(ArgumentError(LazyString(lazy"cannot set index ($row, $col) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. B.dv *= x + @. B.ev *= x + return B +end +function lmul!(x::Number, B::Bidiagonal) + if size(B,1) > 1 + isupper = B.uplo == 'U' + row, col = 1 + isupper, 1 + !isupper + # ensure that zeros are preserved on scaling + y = x * B[row,col] + iszero(y) || throw(ArgumentError(LazyString(lazy"cannot set index ($row, $col) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. B.dv = x * B.dv + @. B.ev = x * B.ev + return B +end /(A::Bidiagonal, B::Number) = Bidiagonal(A.dv/B, A.ev/B, A.uplo) \(B::Number, A::Bidiagonal) = Bidiagonal(B\A.dv, B\A.ev, A.uplo) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 23d2422d13654..d762549a2b228 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -274,6 +274,26 @@ end (*)(x::Number, D::Diagonal) = Diagonal(x * D.diag) (*)(D::Diagonal, x::Number) = Diagonal(D.diag * x) +function lmul!(x::Number, D::Diagonal) + if size(D,1) > 1 + # ensure that zeros are preserved on scaling + y = D[2,1] * x + iszero(y) || throw(ArgumentError(LazyString("cannot set index (2, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. D.diag = x * D.diag + return D +end +function rmul!(D::Diagonal, x::Number) + if size(D,1) > 1 + # ensure that zeros are preserved on scaling + y = x * D[2,1] + iszero(y) || throw(ArgumentError(LazyString("cannot set index (2, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. D.diag *= x + return D +end (/)(D::Diagonal, x::Number) = Diagonal(D.diag / x) (\)(x::Number, D::Diagonal) = Diagonal(x \ D.diag) (^)(D::Diagonal, a::Number) = Diagonal(D.diag .^ a) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 84c79f57debc7..e755ce63e9b2a 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -228,6 +228,29 @@ end -(A::SymTridiagonal) = SymTridiagonal(-A.dv, -A.ev) *(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv*B, A.ev*B) *(B::Number, A::SymTridiagonal) = SymTridiagonal(B*A.dv, B*A.ev) +function rmul!(A::SymTridiagonal, x::Number) + if size(A,1) > 2 + # ensure that zeros are preserved on scaling + y = A[3,1] * x + iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + A.dv .*= x + _evview(A) .*= x + return A +end +function lmul!(x::Number, B::SymTridiagonal) + if size(B,1) > 2 + # ensure that zeros are preserved on scaling + y = x * B[3,1] + iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. B.dv = x * B.dv + ev = _evview(B) + @. ev = x * ev + return B +end /(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv/B, A.ev/B) \(B::Number, A::SymTridiagonal) = SymTridiagonal(B\A.dv, B\A.ev) ==(A::SymTridiagonal{<:Number}, B::SymTridiagonal{<:Number}) = @@ -836,6 +859,30 @@ tr(M::Tridiagonal) = sum(M.d) -(A::Tridiagonal) = Tridiagonal(-A.dl, -A.d, -A.du) *(A::Tridiagonal, B::Number) = Tridiagonal(A.dl*B, A.d*B, A.du*B) *(B::Number, A::Tridiagonal) = Tridiagonal(B*A.dl, B*A.d, B*A.du) +function rmul!(T::Tridiagonal, x::Number) + if size(T,1) > 2 + # ensure that zeros are preserved on scaling + y = T[3,1] * x + iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + T.dl .*= x + T.d .*= x + T.du .*= x + return T +end +function lmul!(x::Number, T::Tridiagonal) + if size(T,1) > 2 + # ensure that zeros are preserved on scaling + y = x * T[3,1] + iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", + lazy"the tridiagonal band to a nonzero value ($y)"))) + end + @. T.dl = x * T.dl + @. T.d = x * T.d + @. T.du = x * T.du + return T +end /(A::Tridiagonal, B::Number) = Tridiagonal(A.dl/B, A.d/B, A.du/B) \(B::Number, A::Tridiagonal) = Tridiagonal(B\A.dl, B\A.d, B\A.du) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index edad29d4ec180..d633a99a2390e 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -969,6 +969,19 @@ end end end +@testset "rmul!/lmul! with numbers" begin + for T in (Bidiagonal(rand(4), rand(3), :U), Bidiagonal(rand(4), rand(3), :L)) + @test rmul!(copy(T), 0.2) ≈ rmul!(Array(T), 0.2) + @test lmul!(0.2, copy(T)) ≈ lmul!(0.2, Array(T)) + @test_throws ArgumentError rmul!(T, NaN) + @test_throws ArgumentError lmul!(NaN, T) + end + for T in (Bidiagonal(rand(1), rand(0), :U), Bidiagonal(rand(1), rand(0), :L)) + @test all(isnan, rmul!(copy(T), NaN)) + @test all(isnan, lmul!(NaN, copy(T))) + end +end + @testset "mul with Diagonal" begin for n in 0:4 dv, ev = rand(n), rand(max(n-1,0)) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 83d5e4fcdf170..dfb901908ba69 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1345,6 +1345,17 @@ end end end +@testset "rmul!/lmul! with numbers" begin + D = Diagonal(rand(4)) + @test rmul!(copy(D), 0.2) ≈ rmul!(Array(D), 0.2) + @test lmul!(0.2, copy(D)) ≈ lmul!(0.2, Array(D)) + @test_throws ArgumentError rmul!(D, NaN) + @test_throws ArgumentError lmul!(NaN, D) + D = Diagonal(rand(1)) + @test all(isnan, rmul!(copy(D), NaN)) + @test all(isnan, lmul!(NaN, copy(D))) +end + @testset "+/- with block Symmetric/Hermitian" begin for p in ([1 2; 3 4], [1 2+im; 2-im 4+2im]) m = SizedArrays.SizedArray{(2,2)}(p) diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 15ac7f9f2147f..826a6e62355d0 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -935,6 +935,19 @@ end end end +@testset "rmul!/lmul! with numbers" begin + for T in (SymTridiagonal(rand(4), rand(3)), Tridiagonal(rand(3), rand(4), rand(3))) + @test rmul!(copy(T), 0.2) ≈ rmul!(Array(T), 0.2) + @test lmul!(0.2, copy(T)) ≈ lmul!(0.2, Array(T)) + @test_throws ArgumentError rmul!(T, NaN) + @test_throws ArgumentError lmul!(NaN, T) + end + for T in (SymTridiagonal(rand(2), rand(1)), Tridiagonal(rand(1), rand(2), rand(1))) + @test all(isnan, rmul!(copy(T), NaN)) + @test all(isnan, lmul!(NaN, copy(T))) + end +end + @testset "mul with empty arrays" begin A = zeros(5,0) T = Tridiagonal(zeros(0), zeros(0), zeros(0)) From f62a380368484913dd022c99055056a027268134 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 23 Sep 2024 18:32:04 +0530 Subject: [PATCH 248/548] Specialize indexing triangular matrices with BandIndex (#55644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this, certain indexing operations involving a `BandIndex` may be evaluated as constants. This isn't used directly presently, but might allow for more performant broadcasting in the future. With this, ```julia julia> n = 3; T = Tridiagonal(rand(n-1), rand(n), rand(n-1)); julia> @code_warntype ((T,j) -> UpperTriangular(T)[LinearAlgebra.BandIndex(2,j)])(T, 1) MethodInstance for (::var"#17#18")(::Tridiagonal{Float64, Vector{Float64}}, ::Int64) from (::var"#17#18")(T, j) @ Main REPL[12]:1 Arguments #self#::Core.Const(var"#17#18"()) T::Tridiagonal{Float64, Vector{Float64}} j::Int64 Body::Float64 1 ─ %1 = Main.UpperTriangular(T)::UpperTriangular{Float64, Tridiagonal{Float64, Vector{Float64}}} │ %2 = LinearAlgebra.BandIndex::Core.Const(LinearAlgebra.BandIndex) │ %3 = (%2)(2, j)::Core.PartialStruct(LinearAlgebra.BandIndex, Any[Core.Const(2), Int64]) │ %4 = Base.getindex(%1, %3)::Core.Const(0.0) └── return %4 ``` The indexing operation may be evaluated at compile-time, as the band index is constant-propagated. --- stdlib/LinearAlgebra/src/bidiag.jl | 5 ++-- stdlib/LinearAlgebra/src/dense.jl | 2 +- stdlib/LinearAlgebra/src/triangular.jl | 14 +++++++++ stdlib/LinearAlgebra/test/triangular.jl | 38 ++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 0aab9ceeca6b9..e5482cbba5595 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -166,10 +166,11 @@ end end @inline function getindex(A::Bidiagonal{T}, b::BandIndex) where T - @boundscheck checkbounds(A, _cartinds(b)) + @boundscheck checkbounds(A, b) if b.band == 0 return @inbounds A.dv[b.index] - elseif b.band == _offdiagind(A.uplo) + elseif b.band ∈ (-1,1) && b.band == _offdiagind(A.uplo) + # we explicitly compare the possible bands as b.band may be constant-propagated return @inbounds A.ev[b.index] else return bidiagzero(A, Tuple(_cartinds(b))...) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 62096cbb172f2..aacc5479bfa9d 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -110,7 +110,7 @@ norm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} = # Conservative assessment of types that have zero(T) defined for themselves haszero(::Type) = false haszero(::Type{T}) where {T<:Number} = isconcretetype(T) -@propagate_inbounds _zero(M::AbstractArray{T}, i, j) where {T} = haszero(T) ? zero(T) : zero(M[i,j]) +@propagate_inbounds _zero(M::AbstractArray{T}, inds...) where {T} = haszero(T) ? zero(T) : zero(M[inds...]) """ triu!(M, k::Integer) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 03634aa7d68e1..e1d61e4035966 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -236,6 +236,20 @@ Base.isstored(A::UpperTriangular, i::Int, j::Int) = @propagate_inbounds getindex(A::UpperTriangular, i::Int, j::Int) = i <= j ? A.data[i,j] : _zero(A.data,j,i) +# these specialized getindex methods enable constant-propagation of the band +Base.@constprop :aggressive @propagate_inbounds function getindex(A::UnitLowerTriangular{T}, b::BandIndex) where {T} + b.band < 0 ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) +end +Base.@constprop :aggressive @propagate_inbounds function getindex(A::LowerTriangular, b::BandIndex) + b.band <= 0 ? A.data[b] : _zero(A.data, b) +end +Base.@constprop :aggressive @propagate_inbounds function getindex(A::UnitUpperTriangular{T}, b::BandIndex) where {T} + b.band > 0 ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) +end +Base.@constprop :aggressive @propagate_inbounds function getindex(A::UpperTriangular, b::BandIndex) + b.band >= 0 ? A.data[b] : _zero(A.data, b) +end + _zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" _zero_triangular_half_str(::Type{<:LowerOrUnitLowerTriangular}) = "upper" diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 42c5494f73e6f..ec9a3079e2643 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -6,7 +6,7 @@ debug = false using Test, LinearAlgebra, Random using LinearAlgebra: BlasFloat, errorbounds, full!, transpose!, UnitUpperTriangular, UnitLowerTriangular, - mul!, rdiv!, rmul!, lmul! + mul!, rdiv!, rmul!, lmul!, BandIndex const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") @@ -1286,4 +1286,40 @@ end end end +@testset "indexing with a BandIndex" begin + # these tests should succeed even if the linear index along + # the band isn't a constant, or type-inferred at all + M = rand(Int,2,2) + f(A,j, v::Val{n}) where {n} = Val(A[BandIndex(n,j)]) + function common_tests(M, ind) + j = ind[] + @test @inferred(f(UpperTriangular(M), j, Val(-1))) == Val(0) + @test @inferred(f(UnitUpperTriangular(M), j, Val(-1))) == Val(0) + @test @inferred(f(UnitUpperTriangular(M), j, Val(0))) == Val(1) + @test @inferred(f(LowerTriangular(M), j, Val(1))) == Val(0) + @test @inferred(f(UnitLowerTriangular(M), j, Val(1))) == Val(0) + @test @inferred(f(UnitLowerTriangular(M), j, Val(0))) == Val(1) + end + common_tests(M, Any[1]) + + M = Diagonal([1,2]) + common_tests(M, Any[1]) + # extra tests for banded structure of the parent + for T in (UpperTriangular, UnitUpperTriangular) + @test @inferred(f(T(M), 1, Val(1))) == Val(0) + end + for T in (LowerTriangular, UnitLowerTriangular) + @test @inferred(f(T(M), 1, Val(-1))) == Val(0) + end + + M = Tridiagonal([1,2], [1,2,3], [1,2]) + common_tests(M, Any[1]) + for T in (UpperTriangular, UnitUpperTriangular) + @test @inferred(f(T(M), 1, Val(2))) == Val(0) + end + for T in (LowerTriangular, UnitLowerTriangular) + @test @inferred(f(T(M), 1, Val(-2))) == Val(0) + end +end + end # module TestTriangular From 0fade450a183470b01c656a9001512ef2f1aae47 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 23 Sep 2024 11:39:00 -0400 Subject: [PATCH 249/548] Replace regex package module checks with actual code checks (#55824) Fixes https://github.com/JuliaLang/julia/issues/55792 Replaces https://github.com/JuliaLang/julia/pull/55822 Improves what https://github.com/JuliaLang/julia/pull/51635 was trying to do i.e. ``` ERROR: LoadError: `using/import Printf` outside of a Module detected. Importing a package outside of a module is not allowed during package precompilation. ``` --- base/loading.jl | 49 ++++++++------------- test/loading.jl | 106 --------------------------------------------- test/precompile.jl | 74 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 137 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 8d180845f942f..2c4a7a16ec7c0 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1524,6 +1524,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} end end +precompiling_package::Bool = false loading_extension::Bool = false precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) @@ -2215,6 +2216,11 @@ For more details regarding code loading, see the manual sections on [modules](@r [parallel computing](@ref code-availability). """ function require(into::Module, mod::Symbol) + if into === Base.__toplevel__ && precompiling_package + # this error type needs to match the error type compilecache throws for non-125 errors. + error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ + is not allowed during package precompilation.") + end if _require_world_age[] != typemax(UInt) Base.invoke_in_world(_require_world_age[], __require, into, mod) else @@ -2792,41 +2798,10 @@ function load_path_setup_code(load_path::Bool=true) return code end -""" - check_src_module_wrap(srcpath::String) - -Checks that a package entry file `srcpath` has a module declaration, and that it is before any using/import statements. -""" -function check_src_module_wrap(pkg::PkgId, srcpath::String) - module_rgx = r"^(|end |\"\"\" )\s*(?:@)*(?:bare)?module\s" - load_rgx = r"\b(?:using|import)\s" - load_seen = false - inside_string = false - for s in eachline(srcpath) - if count("\"\"\"", s) == 1 - # ignore module docstrings - inside_string = !inside_string - end - inside_string && continue - if contains(s, module_rgx) - if load_seen - throw(ErrorException("Package $(repr("text/plain", pkg)) source file $srcpath has a using/import before a module declaration.")) - end - return true - end - if startswith(s, load_rgx) - load_seen = true - end - end - throw(ErrorException("Package $(repr("text/plain", pkg)) source file $srcpath does not contain a module declaration.")) -end - # this is called in the external process that generates precompiled package files function include_package_for_output(pkg::PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::typeof(_concrete_dependencies), source::Union{Nothing,String}) - check_src_module_wrap(pkg, input) - append!(empty!(Base.DEPOT_PATH), depot_path) append!(empty!(Base.DL_LOAD_PATH), dl_load_path) append!(empty!(Base.LOAD_PATH), load_path) @@ -2853,6 +2828,17 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto finally Core.Compiler.track_newly_inferred.x = false end + # check that the package defined the expected module so we can give a nice error message if not + Base.check_package_module_loaded(pkg) +end + +function check_package_module_loaded(pkg::PkgId) + if !haskey(Base.loaded_modules, pkg) + # match compilecache error type for non-125 errors + error("$(repr("text/plain", pkg)) did not define the expected module `$(pkg.name)`, \ + check for typos in package module name") + end + return nothing end const PRECOMPILE_TRACE_COMPILE = Ref{String}() @@ -2927,6 +2913,7 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated Base.track_nested_precomp($precomp_stack) Base.precompiling_extension = $(loading_extension) + Base.precompiling_package = true Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) """) diff --git a/test/loading.jl b/test/loading.jl index 8db8405ef2a83..fb200bf7a0a93 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -855,22 +855,6 @@ end end end -@testset "error message loading pkg bad module name" begin - mktempdir() do tmp - old_loadpath = copy(LOAD_PATH) - try - push!(LOAD_PATH, tmp) - write(joinpath(tmp, "BadCase.jl"), "module badcase end") - @test_logs (:warn, r"The call to compilecache failed.*") match_mode=:any begin - @test_throws ErrorException("package `BadCase` did not define the expected module `BadCase`, \ - check for typos in package module name") (@eval using BadCase) - end - finally - copy!(LOAD_PATH, old_loadpath) - end - end -end - @testset "Preferences loading" begin mktempdir() do dir this_uuid = uuid4() @@ -1268,96 +1252,6 @@ end @test success(`$(Base.julia_cmd()) --startup-file=no -e 'using Statistics'`) end -@testset "checking srcpath modules" begin - p = Base.PkgId("Dummy") - fpath, _ = mktemp() - @testset "valid" begin - write(fpath, """ - module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - baremodule Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - \"\"\" - Foo - using Foo - \"\"\" - module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - \"\"\" Foo \"\"\" - module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - \"\"\" - Foo - \"\"\" module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - @doc let x = 1 - x - end module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - # using foo - module Foo - using Bar - end - """) - @test Base.check_src_module_wrap(p, fpath) - end - @testset "invalid" begin - write(fpath, """ - # module Foo - using Bar - # end - """) - @test_throws ErrorException Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - using Bar - module Foo - end - """) - @test_throws ErrorException Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - using Bar - """) - @test_throws ErrorException Base.check_src_module_wrap(p, fpath) - - write(fpath, """ - x = 1 - """) - @test_throws ErrorException Base.check_src_module_wrap(p, fpath) - end -end - @testset "relocatable upgrades #51989" begin mktempdir() do depot # realpath is needed because Pkg is used for one of the precompile paths below, and Pkg calls realpath on the diff --git a/test/precompile.jl b/test/precompile.jl index bc738e557bb51..7a6e41061f9b1 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2093,4 +2093,78 @@ precompile_test_harness("Binding Unique") do load_path @test UniqueBinding2.thebinding2 === ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), UniqueBinding2, :thebinding, true) end +precompile_test_harness("Detecting importing outside of a package module") do load_path + io = IOBuffer() + write(joinpath(load_path, "ImportBeforeMod.jl"), + """ + import Printf + module ImportBeforeMod + end #module + """) + @test_throws r"Failed to precompile ImportBeforeMod" Base.compilecache(Base.identify_package("ImportBeforeMod"), io, io) + @test occursin( + "`using/import Printf` outside of a Module detected. Importing a package outside of a module is not allowed during package precompilation.", + String(take!(io))) + + + write(joinpath(load_path, "HarmlessComments.jl"), + """ + # import Printf + #= + import Printf + =# + module HarmlessComments + end #module + # import Printf + #= + import Printf + =# + """) + Base.compilecache(Base.identify_package("HarmlessComments")) + + + write(joinpath(load_path, "ImportAfterMod.jl"), """ + module ImportAfterMod + end #module + import Printf + """) + @test_throws r"Failed to precompile ImportAfterMod" Base.compilecache(Base.identify_package("ImportAfterMod"), io, io) + @test occursin( + "`using/import Printf` outside of a Module detected. Importing a package outside of a module is not allowed during package precompilation.", + String(take!(io))) +end + +precompile_test_harness("No package module") do load_path + io = IOBuffer() + write(joinpath(load_path, "NoModule.jl"), + """ + 1 + """) + @test_throws r"Failed to precompile NoModule" Base.compilecache(Base.identify_package("NoModule"), io, io) + @test occursin( + "NoModule [top-level] did not define the expected module `NoModule`, check for typos in package module name", + String(take!(io))) + + + write(joinpath(load_path, "WrongModuleName.jl"), + """ + module DifferentName + x = 1 + end #module + """) + @test_throws r"Failed to precompile WrongModuleName" Base.compilecache(Base.identify_package("WrongModuleName"), io, io) + @test occursin( + "WrongModuleName [top-level] did not define the expected module `WrongModuleName`, check for typos in package module name", + String(take!(io))) + + + write(joinpath(load_path, "NoModuleWithImport.jl"), """ + import Printf + """) + @test_throws r"Failed to precompile NoModuleWithImport" Base.compilecache(Base.identify_package("NoModuleWithImport"), io, io) + @test occursin( + "`using/import Printf` outside of a Module detected. Importing a package outside of a module is not allowed during package precompilation.", + String(take!(io))) +end + finish_precompile_test!() From fc9f1470458ad6bdeb1f56c1150d651814a0a164 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 23 Sep 2024 18:48:18 -0400 Subject: [PATCH 250/548] fall back to slower stat filesize if optimized filesize fails (#55641) --- base/iostream.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/iostream.jl b/base/iostream.jl index 762f881cfbecb..74908344e078e 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -230,8 +230,8 @@ end function filesize(s::IOStream) sz = @_lock_ios s ccall(:ios_filesize, Int64, (Ptr{Cvoid},), s.ios) if sz == -1 - err = Libc.errno() - throw(IOError(string("filesize: ", Libc.strerror(err), " for ", s.name), err)) + # if `s` is not seekable `ios_filesize` can fail, so fall back to slower stat method + sz = filesize(stat(s)) end return sz end From 9f1c0686405173a0ebd065e3322e44a3e7ec0a26 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 23 Sep 2024 21:53:46 -0500 Subject: [PATCH 251/548] Use "index" instead of "subscript" to refer to indexing in performance tips (#55846) --- doc/src/manual/performance-tips.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 436d58f54754a..417d5ac7a4ca1 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1537,7 +1537,7 @@ be modified as suggested by the warnings. Sometimes you can enable better optimization by promising certain program properties. * Use [`@inbounds`](@ref) to eliminate array bounds checking within expressions. Be certain before doing - this. If the subscripts are ever out of bounds, you may suffer crashes or silent corruption. + this. If the indices are ever out of bounds, you may suffer crashes or silent corruption. * Use [`@fastmath`](@ref) to allow floating point optimizations that are correct for real numbers, but lead to differences for IEEE numbers. Be careful when doing this, as this may change numerical results. This corresponds to the `-ffast-math` option of clang. From db6d277f6226daa5739940a4642277cfe0a884ea Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 24 Sep 2024 10:02:47 +0200 Subject: [PATCH 252/548] privatize annotated string API, take two (#55845) https://github.com/JuliaLang/julia/pull/55453 is stuck on StyledStrings and Base documentation being entangled and there isn't a good way to have the documentation of Base types / methods live in an stdlib. This is a stop gap solution to finally be able to move forwards with 1.11. --- base/public.jl | 7 ------- base/strings/annotated.jl | 14 -------------- doc/src/base/strings.md | 19 ++++++++++++++----- doc/src/manual/strings.md | 4 ++++ 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/base/public.jl b/base/public.jl index 803766a0cec1b..2e8e777d2f91d 100644 --- a/base/public.jl +++ b/base/public.jl @@ -21,15 +21,8 @@ public ImmutableDict, OneTo, LogRange, - AnnotatedString, - AnnotatedChar, UUID, -# Annotated strings - annotatedstring, - annotate!, - annotations, - # Semaphores Semaphore, acquire, diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index be4c6887d4a6d..9a0b4b2825436 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -39,13 +39,6 @@ the combined range. See also [`AnnotatedChar`](@ref), [`annotatedstring`](@ref), [`annotations`](@ref), and [`annotate!`](@ref). -!!! warning - While the constructors are part of the Base public API, the fields - of `AnnotatedString` are not. This is to allow for potential future - changes in the implementation of this type. Instead use the - [`annotations`](@ref), and [`annotate!`](@ref) getter/setter - functions. - # Constructors ```julia @@ -81,13 +74,6 @@ More specifically, this is a simple wrapper around any other See also: [`AnnotatedString`](@ref), [`annotatedstring`](@ref), `annotations`, and `annotate!`. -!!! warning - While the constructors are part of the Base public API, the fields - of `AnnotatedChar` are not. This it to allow for potential future - changes in the implementation of this type. Instead use the - [`annotations`](@ref), and [`annotate!`](@ref) getter/setter - functions. - # Constructors ```julia diff --git a/doc/src/base/strings.md b/doc/src/base/strings.md index b7d16ffc7d487..a9637a1a7be3a 100644 --- a/doc/src/base/strings.md +++ b/doc/src/base/strings.md @@ -17,11 +17,6 @@ Core.String(::AbstractString) Base.SubString Base.LazyString Base.@lazy_str -Base.AnnotatedString -Base.AnnotatedChar -Base.annotatedstring -Base.annotations -Base.annotate! Base.transcode Base.unsafe_string Base.ncodeunits(::AbstractString) @@ -101,3 +96,17 @@ Base.escape_string Base.escape_raw_string Base.unescape_string ``` + +## `AnnotatedString`s + +!!! note + The API for AnnotatedStrings is considered experimental and is subject to change between + Julia versions. + +```@docs +Base.AnnotatedString +Base.AnnotatedChar +Base.annotatedstring +Base.annotations +Base.annotate! +``` diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 5ba27b3921cec..c04e5e6d6760e 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -1207,6 +1207,10 @@ last backslash escapes a quote, since these backslashes appear before a quote. ## [Annotated Strings](@id man-annotated-strings) +!!! note + The API for AnnotatedStrings is considered experimental and is subject to change between + Julia versions. + It is sometimes useful to be able to hold metadata relating to regions of a string. A [`AnnotatedString`](@ref Base.AnnotatedString) wraps another string and allows for regions of it to be annotated with labelled values (`:label => value`). From c3af4fc24564c3ecda59a26648abe919f090929a Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 24 Sep 2024 05:48:28 -0400 Subject: [PATCH 253/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Downloads=20stdlib=20from=201061ecc=20to=2089d3c7d=20(#55854)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Downloads URL: https://github.com/JuliaLang/Downloads.jl.git Stdlib branch: master Julia branch: master Old commit: 1061ecc New commit: 89d3c7d Julia version: 1.12.0-DEV Downloads version: 1.6.0(It's okay that it doesn't match) Bump invoked by: @KristofferC Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Downloads.jl/compare/1061ecc377a053fce0df94e1a19e5260f7c030f5...89d3c7dded535a77551e763a437a6d31e4d9bf84 ``` $ git log --oneline 1061ecc..89d3c7d 89d3c7d fix cancelling upload requests (#259) df33406 gracefully cancel a request (#256) ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/Downloads.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 delete mode 100644 deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 create mode 100644 deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 create mode 100644 deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 deleted file mode 100644 index f42bbedb6d415..0000000000000 --- a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -70878dd96911d6960537dfee2a820d98 diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 deleted file mode 100644 index 83164cad9a89d..0000000000000 --- a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -87d2bdc6c85cbbce5302aab8ffe92fc542c9c71a396844fcc04c0416be059b00298b4816ab5e5491dbf865660a3a6152f1c245875a1ec75fb49b4c7ba0d303d8 diff --git a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 new file mode 100644 index 0000000000000..611f3dd448d98 --- /dev/null +++ b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 @@ -0,0 +1 @@ +2472bd6434d21c4b3e3199437e6fdcf7 diff --git a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 new file mode 100644 index 0000000000000..6937982e838f3 --- /dev/null +++ b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 @@ -0,0 +1 @@ +0a3fa9a09de81aa9676dbc7448408c7503f45e42519a2667540ad890316c7da089c95de5464a2032171f963c6f3cba73d6b3c246f1c7ac6ede283fc8132d5209 diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index cb041d86d7f66..b539771fbdb47 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 1061ecc377a053fce0df94e1a19e5260f7c030f5 +DOWNLOADS_SHA1 = 89d3c7dded535a77551e763a437a6d31e4d9bf84 DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 From 2943833bea8a8c05f47de1edc154bbb6888547a1 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Wed, 25 Sep 2024 00:00:13 +1200 Subject: [PATCH 254/548] docs: Small edits to noteworthy differences (#55852) - The first line edit changes it so that the Julia example goes first, not the Python example, keeping with the general flow of the lines above. - The second adds a "the" that is missing. --- doc/src/manual/noteworthy-differences.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index 181fe0a30eb38..33285bde8a066 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -220,8 +220,8 @@ For users coming to Julia from R, these are some noteworthy differences: * Unlike Python, Julia allows [AbstractArrays with arbitrary indexes](https://julialang.org/blog/2017/04/offset-arrays/). Python's special interpretation of negative indexing, `a[-1]` and `a[-2]`, should be written `a[end]` and `a[end-1]` in Julia. - * Julia requires `end` for indexing until the last element. `x[1:]` in Python is equivalent to `x[2:end]` in Julia. - * In Julia, `:` before any object creates a [`Symbol`](@ref) or *quotes* an expression; so, `x[:5]` is same as `x[5]`. If you want to get the first `n` elements of an array, then use range indexing. + * Julia requires `end` for indexing until the last element. `x[2:end]` in Julia is equivalent to `x[1:]` in Python. + * In Julia, `:` before any object creates a [`Symbol`](@ref) or *quotes* an expression; so, `x[:5]` is the same as `x[5]`. If you want to get the first `n` elements of an array, then use range indexing. * Julia's range indexing has the format of `x[start:step:stop]`, whereas Python's format is `x[start:(stop+1):step]`. Hence, `x[0:10:2]` in Python is equivalent to `x[1:2:10]` in Julia. Similarly, `x[::-1]` in Python, which refers to the reversed array, is equivalent to `x[end:-1:1]` in Julia. * In Julia, ranges can be constructed independently as `start:step:stop`, the same syntax it uses in array-indexing. The `range` function is also supported. From a06a80162bb9bdf6f7e91dc18e7ccf5c12673ca4 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 25 Sep 2024 03:15:48 +0800 Subject: [PATCH 255/548] Add filesystem func to transform a path to a URI (#55454) In a few places across Base and the stdlib, we emit paths that we like people to be able to click on in their terminal and editor. Up to this point, we have relied on auto-filepath detection, but this does not allow for alternative link text, such as contracted paths. Doing so (via OSC 8 terminal links for example) requires filepath URI encoding. This functionality was previously part of a PR modifying stacktrace printing (#51816), but after that became held up for unrelated reasons and another PR appeared that would benefit from this utility (#55335), I've split out this functionality so it can be used before the stacktrace printing PR is resolved. --- base/path.jl | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/path.jl | 13 ++++++++++++ 2 files changed, 69 insertions(+) diff --git a/base/path.jl b/base/path.jl index 3b8124f34f174..f6d3266d9738c 100644 --- a/base/path.jl +++ b/base/path.jl @@ -613,3 +613,59 @@ relpath(path::AbstractString, startpath::AbstractString) = for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath) @eval $f(path::AbstractString) = $f(String(path)) end + +""" + uripath(path::AbstractString) + +Encode `path` as a URI as per [RFC8089: The "file" URI +Scheme](https://www.rfc-editor.org/rfc/rfc8089), [RFC3986: Uniform Resource +Identifier (URI): Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986), and +the [Freedesktop File URI spec](https://www.freedesktop.org/wiki/Specifications/file-uri-spec/). + +## Examples + +```julia-repl +julia> uripath("/home/user/example file.jl") # On a unix machine +"file:///home/user/example%20file.jl" + +juila> uripath("C:\\Users\\user\\example file.jl") # On a windows machine +"file:///C:/Users/user/example%20file.jl" +``` +""" +function uripath end + +@static if Sys.iswindows() + function uripath(path::String) + percent_escape(s) = # RFC3986 Section 2.1 + '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%') + encode_uri_component(s) = # RFC3986 Section 2.3 + replace(s, r"[^A-Za-z0-9\-_.~/]+" => percent_escape) + path = abspath(path) + if startswith(path, "\\\\") # UNC path, RFC8089 Appendix E.3 + unixpath = join(eachsplit(path, path_separator_re, keepempty=false), '/') + string("file://", encode_uri_component(unixpath)) # RFC8089 Section 2 + else + drive, localpath = splitdrive(path) # Assuming that non-UNC absolute paths on Windows always have a drive component + unixpath = join(eachsplit(localpath, path_separator_re, keepempty=false), '/') + encdrive = replace(encode_uri_component(drive), "%3A" => ':', "%7C" => '|') # RFC8089 Appendices D.2, E.2.1, and E.2.2 + string("file:///", encdrive, '/', encode_uri_component(unixpath)) # RFC8089 Section 2 + end + end +else + function uripath(path::String) + percent_escape(s) = # RFC3986 Section 2.1 + '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%') + encode_uri_component(s) = # RFC3986 Section 2.3 + replace(s, r"[^A-Za-z0-9\-_.~/]+" => percent_escape) + localpath = join(eachsplit(abspath(path), path_separator_re, keepempty=false), '/') + host = if ispath("/proc/sys/fs/binfmt_misc/WSLInterop") # WSL sigil + distro = get(ENV, "WSL_DISTRO_NAME", "") # See + "wsl\$/$distro" # See and + else + gethostname() # Freedesktop File URI Spec, Hostnames section + end + string("file://", encode_uri_component(host), '/', encode_uri_component(localpath)) # RFC8089 Section 2 + end +end + +uripath(path::AbstractString) = uripath(String(path)) diff --git a/test/path.jl b/test/path.jl index 2f4f2d0983a58..4c2c7034577d5 100644 --- a/test/path.jl +++ b/test/path.jl @@ -311,6 +311,19 @@ test_relpath() end + @testset "uripath" begin + host = if Sys.iswindows() "" else gethostname() end + sysdrive, uridrive = if Sys.iswindows() "C:\\", "C:/" else "/", "" end + @test Base.Filesystem.uripath("$(sysdrive)some$(sep)file.txt") == "file://$host/$(uridrive)some/file.txt" + @test Base.Filesystem.uripath("$(sysdrive)another$(sep)$(sep)folder$(sep)file.md") == "file://$host/$(uridrive)another/folder/file.md" + @test Base.Filesystem.uripath("$(sysdrive)some file with ^odd% chars") == "file://$host/$(uridrive)some%20file%20with%20%5Eodd%25%20chars" + @test Base.Filesystem.uripath("$(sysdrive)weird chars like @#&()[]{}") == "file://$host/$(uridrive)weird%20chars%20like%20%40%23%26%28%29%5B%5D%7B%7D" + @test Base.Filesystem.uripath("$sysdrive") == "file://$host/$uridrive" + @test Base.Filesystem.uripath(".") == Base.Filesystem.uripath(pwd()) + @test Base.Filesystem.uripath("$(sysdrive)unicode$(sep)Δεδομένα") == "file://$host/$(uridrive)unicode/%CE%94%CE%B5%CE%B4%CE%BF%CE%BC%CE%AD%CE%BD%CE%B1" + @test Base.Filesystem.uripath("$(sysdrive)unicode$(sep)🧮🐛🔨") == "file://$host/$(uridrive)unicode/%F0%9F%A7%AE%F0%9F%90%9B%F0%9F%94%A8" + end + if Sys.iswindows() @testset "issue #23646" begin @test lowercase(relpath("E:\\a\\b", "C:\\c")) == "e:\\a\\b" From 060035d1eaec95a8c3f138896a5e42dc871381fe Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Tue, 24 Sep 2024 22:22:13 +0200 Subject: [PATCH 256/548] constrain the path argument of `include` functions to `AbstractString` (#55466) Each `Module` defined with `module` automatically gets an `include` function with two methods. Each of those two methods takes a file path as its last argument. Even though the path argument is unconstrained by dispatch, it's documented as constrained with `::AbstractString`: https://docs.julialang.org/en/v1.11-dev/base/base/#include Furthermore, I think that any invocation of `include` with a non-`AbstractString` path will necessarily throw a `MethodError` eventually. Thus this change should be harmless. Adding the type constraint to the path argument is an improvement because any possible exception would be thrown earlier than before. Apart from modules defined with `module`, the same issue is present with the anonymous modules created by `evalfile`, which is also addressed. Sidenote: `evalfile` seems to be completely untested apart from the test added here. Co-authored-by: Florian --- base/loading.jl | 4 ++-- src/jlfrontend.scm | 4 ++-- test/loading.jl | 11 +++++++++++ test/testhelpers/just_module.jl | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 test/testhelpers/just_module.jl diff --git a/base/loading.jl b/base/loading.jl index 2c4a7a16ec7c0..cf7e41a0b5b2b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2773,8 +2773,8 @@ function evalfile(path::AbstractString, args::Vector{String}=String[]) Expr(:toplevel, :(const ARGS = $args), :(eval(x) = $(Expr(:core, :eval))(__anon__, x)), - :(include(x) = $(Expr(:top, :include))(__anon__, x)), - :(include(mapexpr::Function, x) = $(Expr(:top, :include))(mapexpr, __anon__, x)), + :(include(x::AbstractString) = $(Expr(:top, :include))(__anon__, x)), + :(include(mapexpr::Function, x::AbstractString) = $(Expr(:top, :include))(mapexpr, __anon__, x)), :(include($path)))) end evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...]) diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 2c5f42eda5ce8..463e39c41d00a 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -211,11 +211,11 @@ (block ,@loc (call (core eval) ,name ,x))) - (= (call include ,x) + (= (call include (:: ,x (top AbstractString))) (block ,@loc (call (core _call_latest) (top include) ,name ,x))) - (= (call include (:: ,mex (top Function)) ,x) + (= (call include (:: ,mex (top Function)) (:: ,x (top AbstractString))) (block ,@loc (call (core _call_latest) (top include) ,mex ,name ,x))))) diff --git a/test/loading.jl b/test/loading.jl index fb200bf7a0a93..bdaca7f9dc69e 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -793,6 +793,17 @@ import .Foo28190.Libdl; import Libdl end end +@testset "`::AbstractString` constraint on the path argument to `include`" begin + for m ∈ (NotPkgModule, evalfile("testhelpers/just_module.jl")) + let i = m.include + @test !applicable(i, (nothing,)) + @test !applicable(i, (identity, nothing,)) + @test !hasmethod(i, Tuple{Nothing}) + @test !hasmethod(i, Tuple{Function,Nothing}) + end + end +end + @testset "`Base.project_names` and friends" begin # Some functions in Pkg assumes that these tuples have the same length n = length(Base.project_names) diff --git a/test/testhelpers/just_module.jl b/test/testhelpers/just_module.jl new file mode 100644 index 0000000000000..71bd87e660eae --- /dev/null +++ b/test/testhelpers/just_module.jl @@ -0,0 +1 @@ +@__MODULE__ From b0db75d7be83aa1a019e0801b30bb647d1d3e01e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 24 Sep 2024 16:42:36 -0400 Subject: [PATCH 257/548] Mmap: fix grow! for non file IOs (#55849) Fixes https://github.com/JuliaLang/julia/issues/54203 Requires #55641 Based on https://github.com/JuliaLang/julia/pull/55641#issuecomment-2334162489 cc. @JakeZw @ronisbr --------- Co-authored-by: Jameson Nash --- stdlib/Mmap/src/Mmap.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/Mmap/src/Mmap.jl b/stdlib/Mmap/src/Mmap.jl index e6987582bf511..df2f4f1a19991 100644 --- a/stdlib/Mmap/src/Mmap.jl +++ b/stdlib/Mmap/src/Mmap.jl @@ -86,6 +86,8 @@ grow!(::Anonymous,o::Integer,l::Integer) = return function grow!(io::IO, offset::Integer, len::Integer) pos = position(io) filelen = filesize(io) + # If non-regular file skip trying to grow since we know that will fail the ftruncate syscall + filelen == 0 && !isfile(io) && return if filelen < offset + len failure = ccall(:jl_ftruncate, Cint, (Cint, Int64), fd(io), offset+len) Base.systemerror(:ftruncate, failure != 0) @@ -218,7 +220,7 @@ function mmap(io::IO, # platform-specific mmapping @static if Sys.isunix() prot, flags, iswrite = settings(file_desc, shared) - if requestedSizeLarger + if requestedSizeLarger && isfile(io) # add a condition to this line to ensure it only checks files if iswrite if grow grow!(io, offset, len) From 25cbe006f3a610c204d8f2f67f1200a13a8ce349 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 24 Sep 2024 18:11:43 -0400 Subject: [PATCH 258/548] codegen: split gc roots from other bits on stack (#55767) In order to help avoid memory provenance issues, and better utilize stack space (somewhat), and use FCA less, change the preferred representation of an immutable object to be a pair of `` values. This packing requires some care at the boundaries and if the expected field alignment exceeds that of a pointer. The change is expected to eventually make codegen more flexible at representing unions of values with both bits and pointer regions. Eventually we can also have someone improve the late-gc-lowering pass to take advantage of this increased information accuracy, but currently it will not be any better than before at laying out the frame. --- src/ccall.cpp | 39 +- src/cgutils.cpp | 718 ++++++++++++++++++++------- src/codegen.cpp | 877 +++++++++++++++++++-------------- src/intrinsics.cpp | 54 +- src/llvm-codegen-shared.h | 1 - src/llvm-final-gc-lowering.cpp | 2 +- src/llvm-gc-interface-passes.h | 9 +- src/llvm-late-gc-lowering.cpp | 84 +++- test/compiler/codegen.jl | 5 +- 9 files changed, 1146 insertions(+), 643 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index e336de8e3574f..2de5be6906e7c 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -441,22 +441,13 @@ static Value *llvm_type_rewrite( // we need to use this alloca copy trick instead // On ARM and AArch64, the ABI requires casting through memory to different // sizes. - Value *from; - Value *to; const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); Align align = std::max(DL.getPrefTypeAlign(target_type), DL.getPrefTypeAlign(from_type)); - if (DL.getTypeAllocSize(target_type) >= DL.getTypeAllocSize(from_type)) { - to = emit_static_alloca(ctx, target_type, align); - setName(ctx.emission_context, to, "type_rewrite_buffer"); - from = to; - } - else { - from = emit_static_alloca(ctx, from_type, align); - setName(ctx.emission_context, from, "type_rewrite_buffer"); - to = from; - } - ctx.builder.CreateAlignedStore(v, from, align); - auto pun = ctx.builder.CreateAlignedLoad(target_type, to, align); + size_t nb = std::max(DL.getTypeAllocSize(target_type), DL.getTypeAllocSize(from_type)); + AllocaInst *cast = emit_static_alloca(ctx, nb, align); + setName(ctx.emission_context, cast, "type_rewrite_buffer"); + ctx.builder.CreateAlignedStore(v, cast, align); + auto pun = ctx.builder.CreateAlignedLoad(target_type, cast, align); setName(ctx.emission_context, pun, "type_rewrite"); return pun; } @@ -494,7 +485,7 @@ static const std::string make_errmsg(const char *fname, int n, const char *err) return msg.str(); } -static void typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_value_t *jlto, jl_unionall_t *jlto_env, int argn) +static jl_cgval_t typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_value_t *jlto, jl_unionall_t *jlto_env, int argn) { if (jlto != (jl_value_t*)jl_any_type && !jl_subtype(jvinfo.typ, jlto)) { if (jlto == (jl_value_t*)jl_voidpointer_type) { @@ -502,6 +493,7 @@ static void typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_val if (!jl_is_cpointer_type(jvinfo.typ)) { // emit a typecheck, if not statically known to be correct emit_cpointercheck(ctx, jvinfo, make_errmsg("ccall", argn + 1, "")); + return update_julia_type(ctx, jvinfo, (jl_value_t*)jl_pointer_type); } } else { @@ -526,8 +518,10 @@ static void typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_val ctx.builder.CreateUnreachable(); ctx.builder.SetInsertPoint(passBB); } + return update_julia_type(ctx, jvinfo, jlto); } } + return jvinfo; } // Emit code to convert argument to form expected by C ABI @@ -537,7 +531,7 @@ static void typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_val static Value *julia_to_native( jl_codectx_t &ctx, Type *to, bool toboxed, jl_value_t *jlto, jl_unionall_t *jlto_env, - const jl_cgval_t &jvinfo, + jl_cgval_t jvinfo, bool byRef, int argn) { // We're passing Any @@ -547,7 +541,7 @@ static Value *julia_to_native( } assert(jl_is_datatype(jlto) && jl_struct_try_layout((jl_datatype_t*)jlto)); - typeassert_input(ctx, jvinfo, jlto, jlto_env, argn); + jvinfo = typeassert_input(ctx, jvinfo, jlto, jlto_env, argn); if (!byRef) return emit_unbox(ctx, to, jvinfo, jlto); @@ -556,14 +550,7 @@ static Value *julia_to_native( Align align(julia_alignment(jlto)); Value *slot = emit_static_alloca(ctx, to, align); setName(ctx.emission_context, slot, "native_convert_buffer"); - if (!jvinfo.ispointer()) { - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); - ai.decorateInst(ctx.builder.CreateStore(emit_unbox(ctx, to, jvinfo, jlto), slot)); - } - else { - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); - emit_memcpy(ctx, slot, ai, jvinfo, jl_datatype_size(jlto), align, align); - } + emit_unbox_store(ctx, jvinfo, slot, ctx.tbaa().tbaa_stack, align); return slot; } @@ -1991,7 +1978,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) // If the value is not boxed, try to compute the object id without // reboxing it. auto T_p_derived = PointerType::get(ctx.builder.getContext(), AddressSpace::Derived); - if (!val.isghost && !val.ispointer()) + if (!val.isghost) val = value_to_pointer(ctx, val); Value *args[] = { emit_typeof(ctx, val, false, true), diff --git a/src/cgutils.cpp b/src/cgutils.cpp index bf5c67ae9f849..9124638ce7446 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -323,6 +323,8 @@ static bool type_is_permalloc(jl_value_t *typ) } +// find the offset of pointer fields which never need a write barrier since their type-analysis +// shows they are permanently rooted static void find_perm_offsets(jl_datatype_t *typ, SmallVectorImpl &res, unsigned offset) { // This is a inlined field at `offset`. @@ -346,14 +348,37 @@ static void find_perm_offsets(jl_datatype_t *typ, SmallVectorImpl &res } } -static llvm::SmallVector get_gc_roots_for(jl_codectx_t &ctx, const jl_cgval_t &x) +// load a pointer to N inlined_roots into registers (as a SmallVector) +static llvm::SmallVector load_gc_roots(jl_codectx_t &ctx, Value *inline_roots_ptr, size_t npointers, bool isVolatile=false) +{ + SmallVector gcroots(npointers); + Type *T_prjlvalue = ctx.types().T_prjlvalue; + auto roots_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + for (size_t i = 0; i < npointers; i++) { + auto *ptr = ctx.builder.CreateAlignedLoad(T_prjlvalue, emit_ptrgep(ctx, inline_roots_ptr, i * sizeof(jl_value_t*)), Align(sizeof(void*)), isVolatile); + roots_ai.decorateInst(ptr); + gcroots[i] = ptr; + } + return gcroots; +} + +// inlined bool indicates whether this must return the inlined roots inside x separately, or whether x itself may be used as the root (if x is already isboxed) +static llvm::SmallVector get_gc_roots_for(jl_codectx_t &ctx, const jl_cgval_t &x, bool inlined=false) { if (x.constant || x.typ == jl_bottom_type) return {}; - if (x.Vboxed) // superset of x.isboxed + if (!inlined && x.Vboxed) // superset of x.isboxed return {x.Vboxed}; - assert(!x.isboxed); - if (x.ispointer()) { + assert(!x.isboxed || !inlined); + if (!x.inline_roots.empty()) { + // if (!inlined) { // TODO: implement this filter operation + // SmallVector perm_offsets; + // find_perm_offsets(typ, perm_offsets, 0); + // return filter(!in(perm_offsets), x.inline_roots) + // } + return x.inline_roots; + } + if (!inlined && x.ispointer()) { assert(x.V); assert(x.V->getType()->getPointerAddressSpace() != AddressSpace::Tracked); return {x.V}; @@ -363,8 +388,7 @@ static llvm::SmallVector get_gc_roots_for(jl_codectx_t &ctx, co Type *T = julia_type_to_llvm(ctx, jltype); Value *agg = emit_unbox(ctx, T, x, jltype); SmallVector perm_offsets; - if (jltype && jl_is_datatype(jltype) && ((jl_datatype_t*)jltype)->layout) - find_perm_offsets((jl_datatype_t*)jltype, perm_offsets, 0); + find_perm_offsets((jl_datatype_t*)jltype, perm_offsets, 0); return ExtractTrackedValues(agg, agg->getType(), false, ctx.builder, perm_offsets); } // nothing here to root, move along @@ -1078,6 +1102,247 @@ static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst emit_memcpy_llvm(ctx, dst, dst_ai, data_pointer(ctx, src), src_ai, sz, align_dst, align_src, is_volatile); } +static bool allpointers(jl_datatype_t *typ) +{ + return jl_datatype_size(typ) == typ->layout->npointers * sizeof(void*); +} + +// compute the space required by split_value_into, by simulating it +// returns (sizeof(split_value), n_pointers) +static std::pair split_value_size(jl_datatype_t *typ) +{ + assert(jl_is_datatype(typ)); + size_t dst_off = 0; + bool hasptr = typ->layout->first_ptr >= 0; + size_t npointers = hasptr ? typ->layout->npointers : 0; + // drop the data pointer if the entire structure is just pointers + // TODO: eventually we could drop the slots for the pointers from inside the + // types to pack it together, but this can change the alignment of the bits + // in the fields inside, even if those bits have no pointers themselves. So + // we would actually need to compute, for each pointer, whether any + // subsequent field needed the extra alignment (for example, we can + // drop space for any runs of two/four pointer). Some of these + // functions are already written in a way to support that, but not + // fully implemented yet. + bool nodata = allpointers(typ); + if (nodata) + dst_off = 0; + else + dst_off = jl_datatype_size(typ); + return std::make_pair(dst_off, npointers); +} + +// take a value `x` and split its bits into dst and the roots into inline_roots +static void split_value_into(jl_codectx_t &ctx, const jl_cgval_t &x, Align align_src, Value *dst, Align align_dst, jl_aliasinfo_t const &dst_ai, Value *inline_roots_ptr, jl_aliasinfo_t const &roots_ai, bool isVolatileStore=false) +{ + jl_datatype_t *typ = (jl_datatype_t*)x.typ; + assert(jl_is_concrete_type(x.typ)); + auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); + Type *T_prjlvalue = ctx.types().T_prjlvalue; + if (!x.inline_roots.empty()) { + auto sizes = split_value_size(typ); + if (sizes.first > 0) + emit_memcpy(ctx, dst, dst_ai, x.V, src_ai, sizes.first, align_dst, align_src, isVolatileStore); + for (size_t i = 0; i < sizes.second; i++) { + Value *unbox = x.inline_roots[i]; + roots_ai.decorateInst(ctx.builder.CreateAlignedStore(unbox, emit_ptrgep(ctx, inline_roots_ptr, i * sizeof(void*)), Align(sizeof(void*)), isVolatileStore)); + } + return; + } + if (inline_roots_ptr == nullptr) { + emit_unbox_store(ctx, x, dst, ctx.tbaa().tbaa_stack, align_dst, isVolatileStore); + return; + } + Value *src = data_pointer(ctx, value_to_pointer(ctx, x)); + bool isstack = isa(src->stripInBoundsOffsets()) || src_ai.tbaa == ctx.tbaa().tbaa_stack; + size_t dst_off = 0; + size_t src_off = 0; + bool hasptr = typ->layout->first_ptr >= 0; + size_t npointers = hasptr ? typ->layout->npointers : 0; + bool nodata = allpointers(typ); + for (size_t i = 0; true; i++) { + bool last = i == npointers; + size_t ptr = last ? jl_datatype_size(typ) : (jl_ptr_offset(typ, i) * sizeof(void*)); + if (ptr > src_off) { + emit_memcpy(ctx, + emit_ptrgep(ctx, dst, dst_off), + dst_ai, + emit_ptrgep(ctx, src, src_off), + src_ai, + ptr - src_off, + align_dst, + align_src, + isVolatileStore); + dst_off += ptr - src_off; + } + if (last) + break; + auto *load = ctx.builder.CreateAlignedLoad(T_prjlvalue, emit_ptrgep(ctx, src, ptr), Align(sizeof(void*))); + if (!isstack) + load->setOrdering(AtomicOrdering::Unordered); + src_ai.decorateInst(load); + roots_ai.decorateInst(ctx.builder.CreateAlignedStore(load, emit_ptrgep(ctx, inline_roots_ptr, i * sizeof(void*)), Align(sizeof(void*)), isVolatileStore)); + align_src = align_dst = Align(sizeof(void*)); + src_off = ptr + sizeof(void*); + if (!nodata) { + // store an undef pointer here, to make sure nobody looks at this + dst_ai.decorateInst(ctx.builder.CreateAlignedStore( + ctx.builder.getIntN(sizeof(void*) * 8, (uint64_t)-1), + emit_ptrgep(ctx, dst, dst_off), + align_src, + isVolatileStore)); + dst_off += sizeof(void*); + assert(dst_off == src_off); + } + } +} + +static void split_value_into(jl_codectx_t &ctx, const jl_cgval_t &x, Align align_src, Value *dst, Align align_dst, jl_aliasinfo_t const &dst_ai, MutableArrayRef inline_roots) +{ + jl_datatype_t *typ = (jl_datatype_t*)x.typ; + assert(jl_is_concrete_type(x.typ)); + auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); + Type *T_prjlvalue = ctx.types().T_prjlvalue; + if (!x.inline_roots.empty()) { + auto sizes = split_value_size(typ); + if (sizes.first > 0) + emit_memcpy(ctx, dst, dst_ai, x.V, src_ai, sizes.first, align_dst, align_src); + for (size_t i = 0; i < sizes.second; i++) + inline_roots[i] = x.inline_roots[i]; + return; + } + if (inline_roots.empty()) { + emit_unbox_store(ctx, x, dst, ctx.tbaa().tbaa_stack, align_dst); + return; + } + Value *src = data_pointer(ctx, value_to_pointer(ctx, x)); + bool isstack = isa(src->stripInBoundsOffsets()) || src_ai.tbaa == ctx.tbaa().tbaa_stack; + size_t dst_off = 0; + size_t src_off = 0; + bool hasptr = typ->layout->first_ptr >= 0; + size_t npointers = hasptr ? typ->layout->npointers : 0; + bool nodata = allpointers(typ); + for (size_t i = 0; true; i++) { + bool last = i == npointers; + size_t ptr = last ? jl_datatype_size(typ) : (jl_ptr_offset(typ, i) * sizeof(void*)); + if (ptr > src_off) { + emit_memcpy(ctx, + emit_ptrgep(ctx, dst, dst_off), + dst_ai, + emit_ptrgep(ctx, src, src_off), + src_ai, + ptr - src_off, + align_dst, + align_src); + dst_off += ptr - src_off; + } + if (last) + break; + auto *load = ctx.builder.CreateAlignedLoad(T_prjlvalue, emit_ptrgep(ctx, src, ptr), Align(sizeof(void*))); + if (!isstack) + load->setOrdering(AtomicOrdering::Unordered); + src_ai.decorateInst(load); + inline_roots[i] = load; + align_src = align_dst = Align(sizeof(void*)); + src_off = ptr + sizeof(void*); + if (!nodata) { + // store an undef pointer here, to make sure nobody looks at this + dst_ai.decorateInst(ctx.builder.CreateAlignedStore( + ctx.builder.getIntN(sizeof(void*) * 8, (uint64_t)-1), + emit_ptrgep(ctx, dst, dst_off), + align_src)); + dst_off += sizeof(void*); + assert(dst_off == src_off); + } + } +} + +static std::pair> split_value(jl_codectx_t &ctx, const jl_cgval_t &x, Align x_alignment) +{ + jl_datatype_t *typ = (jl_datatype_t*)x.typ; + auto sizes = split_value_size(typ); + Align align_dst(julia_alignment((jl_value_t*)typ)); + AllocaInst *bits = sizes.first > 0 ? emit_static_alloca(ctx, sizes.first, align_dst) : nullptr; + SmallVector roots(sizes.second); + auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); + split_value_into(ctx, x, x_alignment, bits, align_dst, stack_ai, MutableArrayRef(roots)); + return std::make_pair(bits, roots); +} + +// Return the offset values corresponding to jl_field_offset, but into the two buffers for a split value (or -1) +static std::pair split_value_field(jl_datatype_t *typ, unsigned idx) +{ + size_t fldoff = jl_field_offset(typ, idx); + size_t src_off = 0; + size_t dst_off = 0; + assert(typ->layout->first_ptr >= 0); + size_t npointers = typ->layout->npointers; + bool nodata = allpointers(typ); + for (size_t i = 0; i < npointers; i++) { + size_t ptr = jl_ptr_offset(typ, i) * sizeof(void*); + if (ptr >= fldoff) { + if (ptr >= fldoff + jl_field_size(typ, idx)) + break; + bool onlyptr = jl_field_isptr(typ, idx) || allpointers((jl_datatype_t*)jl_field_type(typ, idx)); + return std::make_pair(onlyptr ? -1 : dst_off + fldoff - src_off, i); + } + dst_off += ptr - src_off; + src_off = ptr + sizeof(void*); + if (!nodata) { + assert(dst_off + sizeof(void*) == src_off); + dst_off = src_off; + } + } + return std::make_pair(dst_off + fldoff - src_off, -1); +} + +// Copy `x` to `dst`, where `x` was a split value and dst needs to have a native layout, copying any inlined roots back into their native location. +// This does not respect roots, so you must call emit_write_multibarrier afterwards. +static void recombine_value(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dst, jl_aliasinfo_t const &dst_ai, Align alignment, bool isVolatileStore) +{ + jl_datatype_t *typ = (jl_datatype_t*)x.typ; + assert(jl_is_concrete_type(x.typ)); + assert(typ->layout->first_ptr >= 0 && !x.inline_roots.empty()); + Align align_dst = alignment; + Align align_src(julia_alignment(x.typ)); + Value *src = x.V; + auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); + size_t dst_off = 0; + size_t src_off = 0; + size_t npointers = typ->layout->npointers; + bool nodata = allpointers(typ); + bool isstack = isa(dst->stripInBoundsOffsets()) || dst_ai.tbaa == ctx.tbaa().tbaa_stack; + for (size_t i = 0; true; i++) { + bool last = i == npointers; + size_t ptr = last ? jl_datatype_size(typ) : (jl_ptr_offset(typ, i) * sizeof(void*)); + if (ptr > dst_off) { + emit_memcpy(ctx, + emit_ptrgep(ctx, dst, dst_off), + dst_ai, + emit_ptrgep(ctx, src, src_off), + src_ai, + ptr - dst_off, + align_dst, + align_src, + isVolatileStore); + src_off += ptr - dst_off; + } + if (last) + break; + auto *root = x.inline_roots[i]; + auto *store = ctx.builder.CreateAlignedStore(root, emit_ptrgep(ctx, dst, ptr), Align(sizeof(void*)), isVolatileStore); + if (!isstack) + store->setOrdering(AtomicOrdering::Unordered); + dst_ai.decorateInst(store); + align_dst = align_src = Align(sizeof(void*)); + dst_off = ptr + sizeof(void*); + if (!nodata) { + assert(src_off + sizeof(void*) == dst_off); + src_off = dst_off; + } + } +} + static Value *emit_tagfrom(jl_codectx_t &ctx, jl_datatype_t *dt) { if (dt->smalltag) @@ -1421,15 +1686,23 @@ static void null_load_check(jl_codectx_t &ctx, Value *v, jl_module_t *scope, jl_ } template -static Value *emit_guarded_test(jl_codectx_t &ctx, Value *ifnot, Value *defval, Func &&func) +static void emit_guarded_test(jl_codectx_t &ctx, Value *ifnot, MutableArrayRef defval, Func &&func) { - if (!ifnot) { - return func(); + if (ifnot == nullptr) { + auto res = func(); + assert(res.size() == defval.size()); + for (size_t i = 0; i < defval.size(); i++) + defval[i] = res[i]; + return; } if (auto Cond = dyn_cast(ifnot)) { if (Cond->isZero()) - return defval; - return func(); + return; + auto res = func(); + assert(res.size() == defval.size()); + for (size_t i = 0; i < defval.size(); i++) + defval[i] = res[i]; + return; } ++EmittedGuards; BasicBlock *currBB = ctx.builder.GetInsertBlock(); @@ -1438,16 +1711,33 @@ static Value *emit_guarded_test(jl_codectx_t &ctx, Value *ifnot, Value *defval, ctx.builder.CreateCondBr(ifnot, passBB, exitBB); ctx.builder.SetInsertPoint(passBB); auto res = func(); + assert(res.size() == defval.size()); passBB = ctx.builder.GetInsertBlock(); ctx.builder.CreateBr(exitBB); ctx.builder.SetInsertPoint(exitBB); - if (defval == nullptr) + for (size_t i = 0; i < defval.size(); i++) { + PHINode *phi = ctx.builder.CreatePHI(defval[i]->getType(), 2); + phi->addIncoming(defval[i], currBB); + phi->addIncoming(res[i], passBB); + setName(ctx.emission_context, phi, "guard_res"); + defval[i] = phi; + } +} + +template +static Value *emit_guarded_test(jl_codectx_t &ctx, Value *ifnot, Value *defval, Func &&func) +{ + MutableArrayRef res(&defval, defval == nullptr ? 0 : 1); + auto funcwrap = [&func] () -> SmallVector { + auto res = func(); + if (res == nullptr) + return {}; + return {res}; + }; + emit_guarded_test(ctx, ifnot, res, funcwrap); + if (res.empty()) return nullptr; - PHINode *phi = ctx.builder.CreatePHI(defval->getType(), 2); - phi->addIncoming(defval, currBB); - phi->addIncoming(res, passBB); - setName(ctx.emission_context, phi, "guard_res"); - return phi; + return res[0]; } template @@ -1755,7 +2045,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, // declare that the pointer is legal (for zero bytes) even though it might be undef. static Value *emit_isa_and_defined(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) { - return emit_nullcheck_guard(ctx, val.ispointer() ? val.V : nullptr, [&] { + return emit_nullcheck_guard(ctx, val.inline_roots.empty() && val.ispointer() ? val.V : nullptr, [&] { return emit_isa(ctx, val, typ, Twine()).first; }); } @@ -1838,6 +2128,9 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v if (ainfo.isghost) { a = Constant::getNullValue(getPointerTy(ctx.builder.getContext())); } + else if (!ainfo.inline_roots.empty()) { + a = value_to_pointer(ctx, ainfo).V; + } else if (!ainfo.ispointer()) { // CreateAlloca is OK here since we are on an error branch Value *tempSpace = ctx.builder.CreateAlloca(a->getType()); @@ -1869,6 +2162,7 @@ static Value *CreateSimplifiedExtractValue(jl_codectx_t &ctx, Value *Agg, ArrayR static void emit_write_barrier(jl_codectx_t&, Value*, ArrayRef); static void emit_write_barrier(jl_codectx_t&, Value*, Value*); static void emit_write_multibarrier(jl_codectx_t&, Value*, Value*, jl_value_t*); +static void emit_write_multibarrier(jl_codectx_t &ctx, Value *parent, const jl_cgval_t &x); SmallVector first_ptr(Type *T) { @@ -1930,7 +2224,6 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j bool maybe_null_if_boxed = true, unsigned alignment = 0, Value **nullcheck = nullptr) { - // TODO: we should use unordered loads for anything with CountTrackedPointers(elty).count > 0 (if not otherwise locked) Type *elty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jltype); if (type_is_ghost(elty)) { if (isStrongerThanMonotonic(Order)) @@ -1941,74 +2234,71 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j alignment = sizeof(void*); else if (!alignment) alignment = julia_alignment(jltype); + if (idx_0based) + ptr = ctx.builder.CreateInBoundsGEP(elty, ptr, idx_0based); unsigned nb = isboxed ? sizeof(void*) : jl_datatype_size(jltype); // note that nb == jl_Module->getDataLayout().getTypeAllocSize(elty) or getTypeStoreSize, depending on whether it is a struct or primitive type AllocaInst *intcast = NULL; - if (Order == AtomicOrdering::NotAtomic) { - if (!isboxed && !aliasscope && elty->isAggregateType() && !CountTrackedPointers(elty).count) { - intcast = emit_static_alloca(ctx, elty, Align(alignment)); - setName(ctx.emission_context, intcast, "aggregate_load_box"); + if (Order == AtomicOrdering::NotAtomic && !isboxed && !aliasscope && elty->isAggregateType() && !jl_is_genericmemoryref_type(jltype)) { + // use split_value to do this load + auto src = mark_julia_slot(ptr, jltype, NULL, tbaa); + auto copy = split_value(ctx, src, Align(alignment)); + if (maybe_null_if_boxed && !copy.second.empty()) { + null_pointer_check(ctx, copy.second[0], nullcheck); } + return mark_julia_slot(copy.first, jltype, NULL, ctx.tbaa().tbaa_stack, copy.second); } - else { + Type *realelty = elty; + if (Order != AtomicOrdering::NotAtomic) { if (!isboxed && !elty->isIntOrPtrTy()) { intcast = emit_static_alloca(ctx, elty, Align(alignment)); setName(ctx.emission_context, intcast, "atomic_load_box"); - elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); + realelty = elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb); + } + if (isa(elty)) { + unsigned nb2 = PowerOf2Ceil(nb); + if (nb != nb2) + elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb2); } } - Type *realelty = elty; - if (Order != AtomicOrdering::NotAtomic && isa(elty)) { - unsigned nb2 = PowerOf2Ceil(nb); - if (nb != nb2) - elty = Type::getIntNTy(ctx.builder.getContext(), 8 * nb2); - } - Value *data = ptr; - if (idx_0based) - data = ctx.builder.CreateInBoundsGEP(elty, data, idx_0based); Value *instr = nullptr; - if (intcast && Order == AtomicOrdering::NotAtomic) { - emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, Align(alignment), intcast->getAlign()); + if (!isboxed && jl_is_genericmemoryref_type(jltype)) { + // load these FCA as individual fields, so LLVM does not need to split them later + Value *fld0 = ctx.builder.CreateStructGEP(elty, ptr, 0); + LoadInst *load0 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(0), fld0, Align(alignment), false); + load0->setOrdering(Order); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); + ai.scope = MDNode::concatenate(aliasscope, ai.scope); + ai.decorateInst(load0); + Value *fld1 = ctx.builder.CreateStructGEP(elty, ptr, 1); + LoadInst *load1 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(1), fld1, Align(alignment), false); + static_assert(offsetof(jl_genericmemoryref_t, ptr_or_offset) == 0, "wrong field order"); + maybe_mark_load_dereferenceable(load1, true, sizeof(void*)*2, alignof(void*)); + load1->setOrdering(Order); + ai.decorateInst(load1); + instr = Constant::getNullValue(elty); + instr = ctx.builder.CreateInsertValue(instr, load0, 0); + instr = ctx.builder.CreateInsertValue(instr, load1, 1); } else { - if (!isboxed && jl_is_genericmemoryref_type(jltype)) { - // load these FCA as individual fields, so LLVM does not need to split them later - Value *fld0 = ctx.builder.CreateStructGEP(elty, data, 0); - LoadInst *load0 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(0), fld0, Align(alignment), false); - load0->setOrdering(Order); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - ai.scope = MDNode::concatenate(aliasscope, ai.scope); - ai.decorateInst(load0); - Value *fld1 = ctx.builder.CreateStructGEP(elty, data, 1); - LoadInst *load1 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(1), fld1, Align(alignment), false); - static_assert(offsetof(jl_genericmemoryref_t, ptr_or_offset) == 0, "wrong field order"); - maybe_mark_load_dereferenceable(load1, true, sizeof(void*)*2, alignof(void*)); - load1->setOrdering(Order); - ai.decorateInst(load1); - instr = Constant::getNullValue(elty); - instr = ctx.builder.CreateInsertValue(instr, load0, 0); - instr = ctx.builder.CreateInsertValue(instr, load1, 1); - } - else { - LoadInst *load = ctx.builder.CreateAlignedLoad(elty, data, Align(alignment), false); - load->setOrdering(Order); - if (isboxed) - maybe_mark_load_dereferenceable(load, true, jltype); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - ai.scope = MDNode::concatenate(aliasscope, ai.scope); - ai.decorateInst(load); - instr = load; - } - if (elty != realelty) - instr = ctx.builder.CreateTrunc(instr, realelty); - if (intcast) { - ctx.builder.CreateStore(instr, intcast); - instr = nullptr; - } + LoadInst *load = ctx.builder.CreateAlignedLoad(elty, ptr, Align(alignment), false); + load->setOrdering(Order); + if (isboxed) + maybe_mark_load_dereferenceable(load, true, jltype); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); + ai.scope = MDNode::concatenate(aliasscope, ai.scope); + ai.decorateInst(load); + instr = load; + } + if (elty != realelty) + instr = ctx.builder.CreateTrunc(instr, realelty); + if (intcast) { + ctx.builder.CreateAlignedStore(instr, intcast, Align(alignment)); + instr = nullptr; } if (maybe_null_if_boxed) { if (intcast) - instr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + instr = ctx.builder.CreateAlignedLoad(intcast->getAllocatedType(), intcast, Align(alignment)); Value *first_ptr = isboxed ? instr : extract_first_ptr(ctx, instr); if (first_ptr) null_pointer_check(ctx, first_ptr, nullcheck); @@ -2021,7 +2311,7 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j // ConstantAsMetadata::get(ConstantInt::get(T_int8, 0)), // ConstantAsMetadata::get(ConstantInt::get(T_int8, 2)) })); if (intcast) - instr = ctx.builder.CreateLoad(intcast->getAllocatedType(), intcast); + instr = ctx.builder.CreateAlignedLoad(intcast->getAllocatedType(), intcast, Align(alignment)); instr = ctx.builder.CreateTrunc(instr, getInt1Ty(ctx.builder.getContext())); } if (instr) @@ -2119,7 +2409,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, emit_unbox_store(ctx, rhs, intcast, ctx.tbaa().tbaa_stack, intcast->getAlign()); r = ctx.builder.CreateLoad(realelty, intcast); } - else if (aliasscope || Order != AtomicOrdering::NotAtomic || tracked_pointers) { + else if (aliasscope || Order != AtomicOrdering::NotAtomic || (tracked_pointers && rhs.inline_roots.empty())) { r = emit_unbox(ctx, realelty, rhs, jltype); } if (realelty != elty) @@ -2279,8 +2569,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, if (!tracked_pointers) // oldval is a slot, so put the oldval back ctx.builder.CreateStore(realCompare, intcast); } - else if (Order != AtomicOrdering::NotAtomic) { - assert(!tracked_pointers); + else if (Order != AtomicOrdering::NotAtomic || (tracked_pointers && rhs.inline_roots.empty())) { r = emit_unbox(ctx, realelty, rhs, jltype); } if (realelty != elty) @@ -2393,23 +2682,30 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ctx.builder.SetInsertPoint(DoneBB); if (needlock) emit_lockstate_value(ctx, needlock, false); - if (parent != NULL && r && tracked_pointers && (!isboxed || !type_is_permalloc(rhs.typ))) { + if (parent != NULL && tracked_pointers && (!isboxed || !type_is_permalloc(rhs.typ))) { if (isreplacefield || issetfieldonce) { BasicBlock *BB = BasicBlock::Create(ctx.builder.getContext(), "xchg_wb", ctx.f); DoneBB = BasicBlock::Create(ctx.builder.getContext(), "done_xchg_wb", ctx.f); ctx.builder.CreateCondBr(Success, BB, DoneBB); ctx.builder.SetInsertPoint(BB); } - if (realelty != elty) - r = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, r, realelty)); - if (intcast) { - ctx.builder.CreateStore(r, intcast); - r = ctx.builder.CreateLoad(intcast_eltyp, intcast); + if (r) { + if (realelty != elty) + r = ctx.builder.Insert(CastInst::Create(Instruction::Trunc, r, realelty)); + if (intcast) { + ctx.builder.CreateStore(r, intcast); + r = ctx.builder.CreateLoad(intcast_eltyp, intcast); + } + if (!isboxed) + emit_write_multibarrier(ctx, parent, r, rhs.typ); + else + emit_write_barrier(ctx, parent, r); + } + else { + assert(!isboxed); + assert(!rhs.inline_roots.empty()); + emit_write_multibarrier(ctx, parent, rhs); } - if (!isboxed) - emit_write_multibarrier(ctx, parent, r, rhs.typ); - else if (!type_is_permalloc(rhs.typ)) - emit_write_barrier(ctx, parent, r); if (isreplacefield || issetfieldonce) { ctx.builder.CreateBr(DoneBB); ctx.builder.SetInsertPoint(DoneBB); @@ -2524,7 +2820,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, } assert(!jl_is_vecelement_type((jl_value_t*)stt)); - if (!strct.ispointer()) { // unboxed + if (strct.inline_roots.empty() && !strct.ispointer()) { // unboxed assert(jl_is_concrete_immutable((jl_value_t*)stt)); bool isboxed = is_datatype_all_pointers(stt); jl_svec_t *types = stt->types; @@ -2580,7 +2876,8 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, } bool maybeatomic = stt->name->atomicfields != NULL; - if (strct.ispointer() && !maybeatomic) { // boxed or stack + if ((strct.inline_roots.empty() && strct.ispointer()) && !maybeatomic) { // boxed or stack + // COMBAK: inline_roots support could be implemented for this if (order != jl_memory_order_notatomic && order != jl_memory_order_unspecified) { emit_atomic_error(ctx, "getfield: non-atomic field cannot be accessed atomically"); *ret = jl_cgval_t(); // unreachable @@ -2656,8 +2953,7 @@ static jl_cgval_t emit_unionload(jl_codectx_t &ctx, Value *addr, Value *ptindex, Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 1), tindex0); if (fsz > 0 && mutabl) { // move value to an immutable stack slot (excluding tindex) - Type *AT = ArrayType::get(IntegerType::get(ctx.builder.getContext(), 8 * al), (fsz + al - 1) / al); - AllocaInst *lv = emit_static_alloca(ctx, AT, Align(al)); + AllocaInst *lv = emit_static_alloca(ctx, fsz, Align(al)); setName(ctx.emission_context, lv, "immutable_union"); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); emit_memcpy(ctx, lv, ai, addr, ai, fsz, Align(al), Align(al)); @@ -2825,7 +3121,41 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st } bool maybe_null = field_may_be_null(strct, jt, idx); size_t byte_offset = jl_field_offset(jt, idx); - if (strct.ispointer()) { + if (!strct.inline_roots.empty()) { + assert(!isatomic && !needlock); + auto tbaa = best_field_tbaa(ctx, strct, jt, idx, byte_offset); + auto offsets = split_value_field(jt, idx); + bool hasptr = offsets.second >= 0; + assert(hasptr == jl_field_isptr(jt, idx) || jl_type_hasptr(jfty)); + ArrayRef roots; + if (hasptr) { + roots = ArrayRef(strct.inline_roots).slice(offsets.second, jl_field_isptr(jt, idx) ? 1 : ((jl_datatype_t*)jfty)->layout->npointers); + if (maybe_null) + null_pointer_check(ctx, roots[0], nullcheck); + } + if (jl_field_isptr(jt, idx)) { + return mark_julia_type(ctx, roots[0], true, jfty); + } + Value *addr = offsets.first < 0 ? nullptr : offsets.first == 0 ? strct.V : emit_ptrgep(ctx, strct.V, offsets.first); + if (jl_is_uniontype(jfty)) { + size_t fsz = 0, al = 0; + int union_max = jl_islayout_inline(jfty, &fsz, &al); + size_t fsz1 = jl_field_size(jt, idx) - 1; + bool isptr = (union_max == 0); + assert(!isptr && fsz < jl_field_size(jt, idx)); (void)isptr; + Value *ptindex = emit_ptrgep(ctx, addr, fsz1); + return emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, false, union_max, strct.tbaa); + } + else if (jfty == (jl_value_t*)jl_bool_type) { + unsigned align = jl_field_align(jt, idx); + return typed_load(ctx, addr, NULL, jfty, tbaa, nullptr, false, + AtomicOrdering::NotAtomic, maybe_null, align, nullcheck); + } + else { + return mark_julia_slot(addr, jfty, nullptr, tbaa, roots); + } + } + else if (strct.ispointer()) { auto tbaa = best_field_tbaa(ctx, strct, jt, idx, byte_offset); Value *staddr = data_pointer(ctx, strct); Value *addr; @@ -2901,8 +3231,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st unsigned st_idx = convert_struct_offset(ctx, T, byte_offset); IntegerType *ET = cast(T->getStructElementType(st_idx)); unsigned align = (ET->getBitWidth() + 7) / 8; - lv = emit_static_alloca(ctx, ET, Align(align)); - lv->setOperand(0, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), (fsz + align - 1) / align)); + lv = emit_static_alloca(ctx, fsz, Align(align)); // emit all of the align-sized words unsigned i = 0; for (; i < fsz / align; i++) { @@ -3079,16 +3408,12 @@ static void init_bits_value(jl_codectx_t &ctx, Value *newv, Value *v, MDNode *tb ai.decorateInst(ctx.builder.CreateAlignedStore(v, newv, alignment)); } -static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, MDNode *tbaa) +static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t &v) { - // newv should already be tagged - if (v.ispointer()) { - unsigned align = std::max(julia_alignment(v.typ), (unsigned)sizeof(void*)); - emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), Align(align), Align(julia_alignment(v.typ))); - } - else { - init_bits_value(ctx, newv, v.V, tbaa); - } + MDNode *tbaa = jl_is_mutable(v.typ) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; + Align newv_align{std::max(julia_alignment(v.typ), (unsigned)sizeof(void*))}; + newv = maybe_decay_tracked(ctx, newv); + emit_unbox_store(ctx, v, newv, tbaa, newv_align); } static jl_value_t *static_constant_instance(const llvm::DataLayout &DL, Constant *constant, jl_value_t *jt) @@ -3205,7 +3530,7 @@ static Value *_boxed_special(jl_codectx_t &ctx, const jl_cgval_t &vinfo, Type *t if (t == getInt1Ty(ctx.builder.getContext())) return track_pjlvalue(ctx, julia_bool(ctx, as_value(ctx, t, vinfo))); - if (ctx.linfo && jl_is_method(ctx.linfo->def.method) && !vinfo.ispointer()) { // don't bother codegen pre-boxing for toplevel + if (ctx.linfo && jl_is_method(ctx.linfo->def.method) && vinfo.inline_roots.empty() && !vinfo.ispointer()) { // don't bother codegen pre-boxing for toplevel if (Constant *c = dyn_cast(vinfo.V)) { jl_value_t *s = static_constant_instance(jl_Module->getDataLayout(), c, jt); if (s) { @@ -3320,9 +3645,8 @@ static AllocaInst *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, union_alloca_type(ut, allunbox, nbytes, align, min_align); if (nbytes > 0) { // at least some of the values can live on the stack - // try to pick an Integer type size such that SROA will emit reasonable code - Type *AT = ArrayType::get(IntegerType::get(ctx.builder.getContext(), 8 * min_align), (nbytes + min_align - 1) / min_align); - AllocaInst *lv = emit_static_alloca(ctx, AT, Align(align)); + assert(align % min_align == 0); + AllocaInst *lv = emit_static_alloca(ctx, nbytes, Align(align)); setName(ctx.emission_context, lv, "unionalloca"); return lv; } @@ -3379,7 +3703,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB if (!box) { box = emit_allocobj(ctx, jt, true); setName(ctx.emission_context, box, "unionbox"); - init_bits_cgval(ctx, box, vinfo_r, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); + init_bits_cgval(ctx, box, vinfo_r); } } tempBB = ctx.builder.GetInsertBlock(); // could have changed @@ -3502,14 +3826,14 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab box = box_union(ctx, vinfo, skip_none); } else { - assert(vinfo.V && "Missing data for unboxed value."); + assert((vinfo.V || !vinfo.inline_roots.empty()) && "Missing data for unboxed value."); assert(jl_is_concrete_immutable(jt) && "This type shouldn't have been unboxed."); Type *t = julia_type_to_llvm(ctx, jt); assert(!type_is_ghost(t)); // ghost values should have been handled by vinfo.constant above! box = _boxed_special(ctx, vinfo, t); if (!box) { bool do_promote = vinfo.promotion_point; - if (do_promote && is_promotable) { + if (do_promote && is_promotable && vinfo.inline_roots.empty()) { auto IP = ctx.builder.saveIP(); ctx.builder.SetInsertPoint(vinfo.promotion_point); box = emit_allocobj(ctx, (jl_datatype_t*)jt, true); @@ -3523,13 +3847,14 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab // end illegal IR originalAlloca->eraseFromParent(); ctx.builder.restoreIP(IP); - } else { + } + else { auto arg_typename = [&] JL_NOTSAFEPOINT { return "box::" + std::string(jl_symbol_name(((jl_datatype_t*)(jt))->name->name)); }; box = emit_allocobj(ctx, (jl_datatype_t*)jt, true); setName(ctx.emission_context, box, arg_typename); - init_bits_cgval(ctx, box, vinfo, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); + init_bits_cgval(ctx, box, vinfo); } } } @@ -3542,30 +3867,25 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con if (AllocaInst *ai = dyn_cast(dest)) // TODO: make this a lifetime_end & dereferenceable annotation? ctx.builder.CreateAlignedStore(UndefValue::get(ai->getAllocatedType()), ai, ai->getAlign()); - if (jl_is_concrete_type(src.typ) || src.constant) { - jl_value_t *typ = src.constant ? jl_typeof(src.constant) : src.typ; + if (src.constant) { + jl_value_t *typ = jl_typeof(src.constant); assert(skip || jl_is_pointerfree(typ)); if (jl_is_pointerfree(typ)) { - unsigned alignment = julia_alignment(typ); - if (!src.ispointer() || src.constant) { + emit_guarded_test(ctx, skip, nullptr, [&] { + unsigned alignment = julia_alignment(typ); + emit_unbox_store(ctx, mark_julia_const(ctx, src.constant), dest, tbaa_dst, Align(alignment), isVolatile); + return nullptr; + }); + } + } + else if (jl_is_concrete_type(src.typ)) { + assert(skip || jl_is_pointerfree(src.typ)); + if (jl_is_pointerfree(src.typ)) { + emit_guarded_test(ctx, skip, nullptr, [&] { + unsigned alignment = julia_alignment(src.typ); emit_unbox_store(ctx, src, dest, tbaa_dst, Align(alignment), isVolatile); - } - else { - Value *src_ptr = data_pointer(ctx, src); - unsigned nb = jl_datatype_size(typ); - // TODO: this branch may be bad for performance, but is necessary to work around LLVM bugs with the undef option that we want to use: - // select copy dest -> dest to simulate an undef value / conditional copy - // if (skip) src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); - auto f = [&] { - (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, Align(alignment), Align(alignment), isVolatile); - return nullptr; - }; - if (skip) - emit_guarded_test(ctx, skip, nullptr, f); - else - f(); - } + return nullptr; + }); } } else if (src.TIndex) { @@ -3615,17 +3935,13 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con } else { assert(src.isboxed && "expected boxed value for sizeof/alignment computation"); - auto f = [&] { + emit_guarded_test(ctx, skip, nullptr, [&] { Value *datatype = emit_typeof(ctx, src, false, false); Value *copy_bytes = emit_datatype_size(ctx, datatype); - (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), data_pointer(ctx, src), - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, Align(1), Align(1), isVolatile); + emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), data_pointer(ctx, src), + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, Align(1), Align(1), isVolatile); return nullptr; - }; - if (skip) - emit_guarded_test(ctx, skip, nullptr, f); - else - f(); + }); } } @@ -3714,6 +4030,12 @@ static void emit_write_multibarrier(jl_codectx_t &ctx, Value *parent, Value *agg emit_write_barrier(ctx, parent, ptrs); } +static void emit_write_multibarrier(jl_codectx_t &ctx, Value *parent, const jl_cgval_t &x) +{ + auto ptrs = get_gc_roots_for(ctx, x, true); + emit_write_barrier(ctx, parent, ptrs); +} + static jl_cgval_t union_store(jl_codectx_t &ctx, Value *ptr, Value *ptindex, jl_cgval_t rhs, jl_cgval_t cmp, jl_value_t *jltype, MDNode *tbaa, MDNode *tbaa_tindex, @@ -3854,25 +4176,24 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg Type *lt = julia_type_to_llvm(ctx, ty); unsigned na = nargs < nf ? nargs : nf; - // whether we should perform the initialization with the struct as a IR value - // or instead initialize the stack buffer with stores - auto tracked = CountTrackedPointers(lt); + // choose whether we should perform the initialization with the struct as a IR value + // or instead initialize the stack buffer with stores (the later is nearly always better) + auto tracked = split_value_size(sty); + assert(CountTrackedPointers(lt).count == tracked.second); bool init_as_value = false; if (lt->isVectorTy() || jl_is_vecelement_type(ty)) { // maybe also check the size ? init_as_value = true; } - else if (tracked.count) { - init_as_value = true; - } Instruction *promotion_point = nullptr; ssize_t promotion_ssa = -1; Value *strct; + SmallVector inline_roots; if (type_is_ghost(lt)) { - strct = NULL; + strct = nullptr; } else if (init_as_value) { - if (tracked.count) { + if (tracked.second) { strct = Constant::getNullValue(lt); } else { @@ -3881,11 +4202,19 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg strct = ctx.builder.CreateFreeze(strct); } } + else if (tracked.second) { + inline_roots.resize(tracked.second, Constant::getNullValue(ctx.types().T_prjlvalue)); + strct = nullptr; + if (tracked.first) { + AllocaInst *bits = emit_static_alloca(ctx, tracked.first, Align(julia_alignment(ty))); + strct = bits; + setName(ctx.emission_context, bits, arg_typename); + is_promotable = false; // wrong layout for promotion + } + } else { strct = emit_static_alloca(ctx, lt, Align(julia_alignment(ty))); setName(ctx.emission_context, strct, arg_typename); - if (tracked.count) - undef_derived_strct(ctx, strct, sty, ctx.tbaa().tbaa_stack); } for (unsigned i = 0; i < na; i++) { @@ -3897,25 +4226,32 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg fval_info = update_julia_type(ctx, fval_info, jtype); if (fval_info.typ == jl_bottom_type) return jl_cgval_t(); + if (type_is_ghost(lt)) + continue; + Type *fty = julia_type_to_llvm(ctx, jtype); + if (type_is_ghost(fty)) + continue; + Instruction *dest = nullptr; + MutableArrayRef roots; + ssize_t offs = jl_field_offset(sty, i); + ssize_t ptrsoffs = -1; + if (!inline_roots.empty()) + std::tie(offs, ptrsoffs) = split_value_field(sty, i); + unsigned llvm_idx = init_as_value ? ((i > 0 && isa(lt)) ? convert_struct_offset(ctx, lt, offs) : i) : -1u; // TODO: Use (post-)domination instead. bool field_promotable = !jl_is_uniontype(jtype) && !init_as_value && fval_info.promotion_ssa != -1 && + fval_info.inline_roots.empty() && inline_roots.empty() && // these need to be compatible, if they were to be implemented fval_info.promotion_point && fval_info.promotion_point->getParent() == ctx.builder.GetInsertBlock(); if (field_promotable) { savedIP = ctx.builder.saveIP(); ctx.builder.SetInsertPoint(fval_info.promotion_point); } - if (type_is_ghost(lt)) - continue; - Type *fty = julia_type_to_llvm(ctx, jtype); - if (type_is_ghost(fty)) - continue; - Value *dest = NULL; - unsigned offs = jl_field_offset(sty, i); - unsigned llvm_idx = (i > 0 && isa(lt)) ? convert_struct_offset(ctx, lt, offs) : i; if (!init_as_value) { // avoid unboxing the argument explicitly // and use memcpy instead - Instruction *inst = cast(emit_ptrgep(ctx, strct, offs)); + Instruction *inst = strct && offs >= 0 ? cast(emit_ptrgep(ctx, strct, offs)) : nullptr; + if (!inline_roots.empty() && ptrsoffs >= 0) + roots = MutableArrayRef(inline_roots).slice(ptrsoffs, jl_field_isptr(sty, i) ? 1 : ((jl_datatype_t*)jtype)->layout->npointers); dest = inst; // Our promotion point needs to come before // A) All of our arguments' promotion points @@ -3936,10 +4272,13 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (jl_field_isptr(sty, i)) { fval = boxed(ctx, fval_info, field_promotable); if (!init_as_value) { - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); - StoreInst *SI = cast(ai.decorateInst( - ctx.builder.CreateAlignedStore(fval, dest, Align(jl_field_align(sty, i))))); - SI->setOrdering(AtomicOrdering::Unordered); + if (dest) { + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); + ai.decorateInst(ctx.builder.CreateAlignedStore(fval, dest, Align(jl_field_align(sty, i)))); + } + else { + roots[0] = fval; + } } } else if (jl_is_uniontype(jtype)) { @@ -3962,9 +4301,8 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (fsz1 > 0 && !fval_info.isghost) { Type *ET = IntegerType::get(ctx.builder.getContext(), 8 * al); assert(lt->getStructElementType(llvm_idx) == ET); - AllocaInst *lv = emit_static_alloca(ctx, ET, Align(al)); + AllocaInst *lv = emit_static_alloca(ctx, fsz1, Align(al)); setName(ctx.emission_context, lv, "unioninit"); - lv->setOperand(0, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), (fsz1 + al - 1) / al)); emit_unionmove(ctx, lv, ctx.tbaa().tbaa_stack, fval_info, nullptr); // emit all of the align-sized words unsigned i = 0; @@ -4002,9 +4340,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (field_promotable) { fval_info.V->replaceAllUsesWith(dest); cast(fval_info.V)->eraseFromParent(); - } else if (init_as_value) { + } + else if (init_as_value) { fval = emit_unbox(ctx, fty, fval_info, jtype); - } else { + } + else if (!roots.empty()) { + split_value_into(ctx, fval_info, Align(julia_alignment(jtype)), dest, Align(jl_field_align(sty, i)), jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), roots); + } + else { emit_unbox_store(ctx, fval_info, dest, ctx.tbaa().tbaa_stack, Align(jl_field_align(sty, i))); } } @@ -4025,7 +4368,11 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } for (size_t i = nargs; i < nf; i++) { if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { - unsigned offs = jl_field_offset(sty, i); + ssize_t offs = jl_field_offset(sty, i); + ssize_t ptrsoffs = -1; + if (!inline_roots.empty()) + std::tie(offs, ptrsoffs) = split_value_field(sty, i); + assert(ptrsoffs < 0 && offs >= 0); int fsz = jl_field_size(sty, i) - 1; if (init_as_value) { unsigned llvm_idx = convert_struct_offset(ctx, cast(lt), offs + fsz); @@ -4033,19 +4380,23 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg } else { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); - ai.decorateInst(ctx.builder.CreateAlignedStore( - ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), - emit_ptrgep(ctx, strct, offs + fsz), - Align(1))); + Instruction *dest = cast(emit_ptrgep(ctx, strct, offs + fsz)); + if (promotion_point == nullptr) + promotion_point = dest; + ai.decorateInst(ctx.builder.CreateAlignedStore(ctx.builder.getInt8(0), dest, Align(1))); } } } - if (promotion_point && nargs < nf) { + if (nargs < nf) { assert(!init_as_value); IRBuilderBase::InsertPoint savedIP = ctx.builder.saveIP(); - ctx.builder.SetInsertPoint(promotion_point); - promotion_point = cast(ctx.builder.CreateFreeze(UndefValue::get(lt))); - ctx.builder.CreateStore(promotion_point, strct); + if (promotion_point) + ctx.builder.SetInsertPoint(promotion_point); + if (strct) { + promotion_point = cast(ctx.builder.CreateFreeze(UndefValue::get(lt))); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); + ai.decorateInst(ctx.builder.CreateStore(promotion_point, strct)); + } ctx.builder.restoreIP(savedIP); } if (type_is_ghost(lt)) @@ -4053,7 +4404,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else if (init_as_value) return mark_julia_type(ctx, strct, false, ty); else { - jl_cgval_t ret = mark_julia_slot(strct, ty, NULL, ctx.tbaa().tbaa_stack); + jl_cgval_t ret = mark_julia_slot(strct, ty, NULL, ctx.tbaa().tbaa_stack, inline_roots); if (is_promotable && promotion_point) { ret.promotion_point = promotion_point; ret.promotion_ssa = promotion_ssa; @@ -4157,7 +4508,20 @@ static jl_cgval_t _emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &mem, cons static Value *emit_memoryref_FCA(jl_codectx_t &ctx, const jl_cgval_t &ref, const jl_datatype_layout_t *layout) { - if (ref.ispointer()) { + if (!ref.inline_roots.empty()) { + LLVMContext &C = ctx.builder.getContext(); + StructType *type = get_memoryref_type(C, ctx.types().T_size, layout, 0); + LoadInst *load0 = ctx.builder.CreateLoad(type->getElementType(0), ref.V); + jl_aliasinfo_t ai0 = jl_aliasinfo_t::fromTBAA(ctx, ref.tbaa); + ai0.decorateInst(load0); + setName(ctx.emission_context, load0, "memory_ref_FCA0"); + Value *root = ctx.builder.CreateBitCast(ref.inline_roots[0], type->getElementType(1)); + Value *load = Constant::getNullValue(type); + load = ctx.builder.CreateInsertValue(load, load0, 0); + load = ctx.builder.CreateInsertValue(load, root, 1); + return load; + } + else if (ref.ispointer()) { LLVMContext &C = ctx.builder.getContext(); Type *type = get_memoryref_type(C, ctx.types().T_size, layout, 0); LoadInst *load = ctx.builder.CreateLoad(type, data_pointer(ctx, ref)); diff --git a/src/codegen.cpp b/src/codegen.cpp index 6d4ecc63e5ca1..c719f4ff54078 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1816,11 +1816,12 @@ struct jl_cgval_t { Value *Vboxed; Value *TIndex; // if `V` is an unboxed (tagged) Union described by `typ`, this gives the DataType index (1-based, small int) as an i8 + SmallVector inline_roots; // if present, `V` is a pointer, but not in canonical layout jl_value_t *constant; // constant value (rooted in linfo.def.roots) - jl_value_t *typ; // the original type of V, never NULL + jl_value_t *typ; // the original type of V, never nullptr bool isboxed; // whether this value is a jl_value_t* allocated on the heap with the right type tag bool isghost; // whether this value is "ghost" - MDNode *tbaa; // The related tbaa node. Non-NULL iff this holds an address. + MDNode *tbaa; // The related tbaa node. Non-nullptr iff this holds an address. // If non-null, this memory location may be promoted on use, by hoisting the // destination memory above the promotion point. Instruction *promotion_point; @@ -1831,13 +1832,15 @@ struct jl_cgval_t { bool ispointer() const { // whether this value is compatible with `data_pointer` + assert(inline_roots.empty()); return tbaa != nullptr; } jl_cgval_t(Value *Vval, jl_value_t *typ, Value *tindex) : // general value constructor - V(Vval), // V is allowed to be NULL in a jl_varinfo_t context, but not during codegen contexts + V(Vval), // V is allowed to be nullptr in a jl_varinfo_t context, but not during codegen contexts Vboxed(nullptr), TIndex(tindex), - constant(NULL), + inline_roots(), + constant(nullptr), typ(typ), isboxed(false), isghost(false), @@ -1845,13 +1848,15 @@ struct jl_cgval_t { promotion_point(nullptr), promotion_ssa(-1) { - assert(TIndex == NULL || TIndex->getType() == getInt8Ty(TIndex->getContext())); + assert(TIndex == nullptr || TIndex->getType() == getInt8Ty(TIndex->getContext())); } - jl_cgval_t(Value *Vptr, bool isboxed, jl_value_t *typ, Value *tindex, MDNode *tbaa) : // general pointer constructor + jl_cgval_t(Value *Vptr, bool isboxed, jl_value_t *typ, Value *tindex, MDNode *tbaa, Value* inline_roots) = delete; + jl_cgval_t(Value *Vptr, bool isboxed, jl_value_t *typ, Value *tindex, MDNode *tbaa, ArrayRef inline_roots) : // general pointer constructor V(Vptr), Vboxed(isboxed ? Vptr : nullptr), TIndex(tindex), - constant(NULL), + inline_roots(inline_roots), + constant(nullptr), typ(typ), isboxed(isboxed), isghost(false), @@ -1861,15 +1866,16 @@ struct jl_cgval_t { { if (Vboxed) assert(Vboxed->getType() == JuliaType::get_prjlvalue_ty(Vboxed->getContext())); - assert(tbaa != NULL); - assert(!(isboxed && TIndex != NULL)); - assert(TIndex == NULL || TIndex->getType() == getInt8Ty(TIndex->getContext())); + assert(tbaa != nullptr); + assert(!(isboxed && TIndex != nullptr)); + assert(TIndex == nullptr || TIndex->getType() == getInt8Ty(TIndex->getContext())); } explicit jl_cgval_t(jl_value_t *typ) : // ghost value constructor - // mark explicit to avoid being used implicitly for conversion from NULL (use jl_cgval_t() instead) - V(NULL), - Vboxed(NULL), - TIndex(NULL), + // mark explicit to avoid being used implicitly for conversion from nullptr (use jl_cgval_t() instead) + V(nullptr), + Vboxed(nullptr), + TIndex(nullptr), + inline_roots(), constant(((jl_datatype_t*)typ)->instance), typ(typ), isboxed(false), @@ -1885,6 +1891,7 @@ struct jl_cgval_t { V(v.V), Vboxed(v.Vboxed), TIndex(tindex), + inline_roots(v.inline_roots), constant(v.constant), typ(typ), isboxed(v.isboxed), @@ -1898,17 +1905,18 @@ struct jl_cgval_t { // this constructor expects we had a badly or equivalently typed version // make sure we aren't discarding the actual type information if (v.TIndex) { - assert((TIndex == NULL) == jl_is_concrete_type(typ)); + assert((TIndex == nullptr) == jl_is_concrete_type(typ)); } else { assert(isboxed || v.typ == typ || tindex); } } explicit jl_cgval_t() : // undef / unreachable constructor - V(NULL), - Vboxed(NULL), - TIndex(NULL), - constant(NULL), + V(nullptr), + Vboxed(nullptr), + TIndex(nullptr), + inline_roots(), + constant(nullptr), typ(jl_bottom_type), isboxed(false), isghost(true), @@ -1924,6 +1932,7 @@ struct jl_varinfo_t { Instruction *boxroot; // an address, if the var might be in a jl_value_t** stack slot (marked ctx.tbaa().tbaa_const, if appropriate) jl_cgval_t value; // a stack slot or constant value Value *pTIndex; // i8* stack slot for the value.TIndex tag describing `value.V` + AllocaInst *inline_roots; // stack roots for the inline_roots array, if needed DILocalVariable *dinfo; // if the variable might be used undefined and is not boxed // this i1 flag is true when it is defined @@ -1934,11 +1943,12 @@ struct jl_varinfo_t { bool usedUndef; bool used; - jl_varinfo_t(LLVMContext &ctxt) : boxroot(NULL), + jl_varinfo_t(LLVMContext &ctxt) : boxroot(nullptr), value(jl_cgval_t()), - pTIndex(NULL), - dinfo(NULL), - defFlag(NULL), + pTIndex(nullptr), + inline_roots(nullptr), + dinfo(nullptr), + defFlag(nullptr), isSA(false), isVolatile(false), isArgument(false), @@ -1962,7 +1972,7 @@ class jl_codectx_t { std::map phic_slots; std::map > scope_restore; SmallVector SAvalues; - SmallVector, 0> PhiNodes; + SmallVector, jl_value_t *>, 0> PhiNodes; SmallVector ssavalue_assigned; SmallVector ssavalue_usecount; jl_module_t *module = NULL; @@ -2110,7 +2120,8 @@ jl_aliasinfo_t jl_aliasinfo_t::fromTBAA(jl_codectx_t &ctx, MDNode *tbaa) { } static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed = NULL); -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_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, + ArrayRef ArgNames=None, unsigned nreq=0); 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, bool alloc); @@ -2133,6 +2144,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static unsigned julia_alignment(jl_value_t *jt); +static void recombine_value(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dst, jl_aliasinfo_t const &dst_ai, Align alignment, bool isVolatile); static GlobalVariable *prepare_global_in(Module *M, JuliaVariable *G) { @@ -2217,6 +2229,28 @@ static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, Type *lty, Align align) return new AllocaInst(lty, ctx.topalloca->getModule()->getDataLayout().getAllocaAddrSpace(), nullptr, align, "", /*InsertBefore=*/ctx.topalloca); } +static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, unsigned nb, Align align) +{ + // Stupid hack: SROA takes hints from the element type, and will happily split this allocation into lots of unaligned bits + // if it cannot find something better to do, which is terrible for performance. + // However, if we emit this with an element size equal to the alignment, it will instead split it into aligned chunks + // which is great for performance and vectorization. + if (alignTo(nb, align) == align.value()) // don't bother with making an array of length 1 + return emit_static_alloca(ctx, ctx.builder.getIntNTy(align.value() * 8), align); + return emit_static_alloca(ctx, ArrayType::get(ctx.builder.getIntNTy(align.value() * 8), alignTo(nb, align) / align.value()), align); +} + +static AllocaInst *emit_static_roots(jl_codectx_t &ctx, unsigned nroots) +{ + AllocaInst *staticroots = emit_static_alloca(ctx, ctx.types().T_prjlvalue, Align(sizeof(void*))); + staticroots->setOperand(0, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), nroots)); + IRBuilder<> builder(ctx.topalloca); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + // make sure these are nullptr early from LLVM's perspective, in case it decides to SROA it + ai.decorateInst(builder.CreateMemSet(staticroots, builder.getInt8(0), nroots * sizeof(void*), staticroots->getAlign()))->moveAfter(ctx.topalloca); + return staticroots; +} + static void undef_derived_strct(jl_codectx_t &ctx, Value *ptr, jl_datatype_t *sty, MDNode *tbaa) { assert(ptr->getType()->getPointerAddressSpace() != AddressSpace::Tracked); @@ -2264,7 +2298,7 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_value_t *typ) if (jl_is_type_type(typ)) { assert(is_uniquerep_Type(typ)); // replace T::Type{T} with T, by assuming that T must be a leaftype of some sort - jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ), None); constant.constant = jl_tparam0(typ); if (typ == (jl_value_t*)jl_typeofbottom_type->super) constant.isghost = true; @@ -2288,16 +2322,16 @@ static inline jl_cgval_t mark_julia_const(jl_codectx_t &ctx, jl_value_t *jv) if (jl_is_datatype_singleton((jl_datatype_t*)typ)) return ghostValue(ctx, typ); } - jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ), None); constant.constant = jv; return constant; } -static inline jl_cgval_t mark_julia_slot(Value *v, jl_value_t *typ, Value *tindex, MDNode *tbaa) +static inline jl_cgval_t mark_julia_slot(Value *v, jl_value_t *typ, Value *tindex, MDNode *tbaa, ArrayRef inline_roots=None) { // this enables lazy-copying of immutable values and stack or argument slots - jl_cgval_t tagval(v, false, typ, tindex, tbaa); + jl_cgval_t tagval(v, false, typ, tindex, tbaa, inline_roots); return tagval; } @@ -2317,22 +2351,41 @@ static bool valid_as_globalinit(const Value *v) { static Value *zext_struct(jl_codectx_t &ctx, Value *V); +// TODO: in the future, assume all callers will handle the interior pointers separately, and have +// have zext_struct strip them out, so we aren't saving those to the stack here causing shadow stores +// to be necessary too static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_t *typ, Value *tindex) { Value *loc; v = zext_struct(ctx, v); + Align align(julia_alignment(typ)); if (valid_as_globalinit(v)) { // llvm can't handle all the things that could be inside a ConstantExpr assert(jl_is_concrete_type(typ)); // not legal to have an unboxed abstract type - loc = get_pointer_to_constant(ctx.emission_context, cast(v), Align(julia_alignment(typ)), "_j_const", *jl_Module); + loc = get_pointer_to_constant(ctx.emission_context, cast(v), align, "_j_const", *jl_Module); } else { - loc = emit_static_alloca(ctx, v->getType(), Align(julia_alignment(typ))); - ctx.builder.CreateStore(v, loc); + loc = emit_static_alloca(ctx, v->getType(), align); + ctx.builder.CreateAlignedStore(v, loc, align); } return mark_julia_slot(loc, typ, tindex, ctx.tbaa().tbaa_stack); } static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, const jl_cgval_t &v) { + if (!v.inline_roots.empty()) { + //if (v.V == nullptr) { + // AllocaInst *loc = emit_static_roots(ctx, v.inline_roots.size()); + // for (size_t i = 0; i < v.inline_roots.counts(); i++) + // ctx.builder.CreateAlignedStore(v.inline_roots[i], emit_ptrgep(ctx, loc, i * sizeof(void*)), Align(sizeof(void*))); + // return mark_julia_slot(loc, v.typ, v.TIndex, ctx.tbaa().tbaa_gcframe); + //} + Align align(julia_alignment(v.typ)); + Type *ty = julia_type_to_llvm(ctx, v.typ); + AllocaInst *loc = emit_static_alloca(ctx, ty, align); + auto tbaa = v.V == nullptr ? ctx.tbaa().tbaa_gcframe : ctx.tbaa().tbaa_stack; + auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); + recombine_value(ctx, v, loc, stack_ai, align, false); + return mark_julia_slot(loc, v.typ, v.TIndex, tbaa); + } if (v.ispointer()) return v; return value_to_pointer(ctx, v.V, v.typ, v.TIndex); @@ -2354,13 +2407,14 @@ static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isbox if (type_is_ghost(T)) { return ghostValue(ctx, typ); } - if (v && !isboxed && v->getType()->isAggregateType() && CountTrackedPointers(v->getType()).count == 0) { + if (v && !isboxed && v->getType()->isAggregateType()) { // eagerly put this back onto the stack // llvm mem2reg pass will remove this if unneeded - return value_to_pointer(ctx, v, typ, NULL); + if (CountTrackedPointers(v->getType()).count == 0) + return value_to_pointer(ctx, v, typ, NULL); } if (isboxed) - return jl_cgval_t(v, isboxed, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + return jl_cgval_t(v, isboxed, typ, NULL, best_tbaa(ctx.tbaa(), typ), None); return jl_cgval_t(v, typ, NULL); } @@ -2395,7 +2449,7 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & if (alwaysboxed) { // discovered that this union-split type must actually be isboxed if (v.Vboxed) { - return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ), v.inline_roots); } else { // type mismatch (there weren't any boxed values in the union) @@ -2624,14 +2678,14 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & decay_derived(ctx, boxv), decay_derived(ctx, slotv)); } - jl_cgval_t newv = jl_cgval_t(slotv, false, typ, new_tindex, tbaa); + jl_cgval_t newv = jl_cgval_t(slotv, false, typ, new_tindex, tbaa, v.inline_roots); assert(boxv->getType() == ctx.types().T_prjlvalue); newv.Vboxed = boxv; return newv; } } else { - return jl_cgval_t(boxed(ctx, v), true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + return jl_cgval_t(boxed(ctx, v), true, typ, NULL, best_tbaa(ctx.tbaa(), typ), None); } return jl_cgval_t(v, typ, new_tindex); } @@ -2662,7 +2716,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ if (skip) { *skip = ctx.builder.CreateNot(emit_exactly_isa(ctx, v, (jl_datatype_t*)typ, true)); } - return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + return jl_cgval_t(v.Vboxed, true, typ, NULL, best_tbaa(ctx.tbaa(), typ), v.inline_roots); } if (mustbox_union) { // type mismatch: there weren't any boxed values in the union @@ -2684,7 +2738,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ unsigned new_idx = get_box_tindex((jl_datatype_t*)v.typ, typ); if (new_idx) { new_tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), new_idx); - if (v.V && !v.ispointer()) { + if (v.V && v.inline_roots.empty() && !v.ispointer()) { // TODO: remove this branch once all consumers of v.TIndex understand how to handle a non-ispointer value return jl_cgval_t(value_to_pointer(ctx, v), typ, new_tindex); } @@ -2708,7 +2762,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ } if (makeboxed) { // convert to a simple isboxed value - return jl_cgval_t(boxed(ctx, v), true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); + return mark_julia_type(ctx, boxed(ctx, v), true, typ); } } return jl_cgval_t(v, typ, new_tindex); @@ -3524,9 +3578,9 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a jl_datatype_t *sty = (jl_datatype_t*)argty; size_t sz = jl_datatype_size(sty); if (sz > 512 && !sty->layout->flags.haspadding && sty->layout->flags.isbitsegal) { - Value *varg1 = arg1.ispointer() ? data_pointer(ctx, arg1) : + Value *varg1 = arg1.inline_roots.empty() && arg1.ispointer() ? data_pointer(ctx, arg1) : value_to_pointer(ctx, arg1).V; - Value *varg2 = arg2.ispointer() ? data_pointer(ctx, arg2) : + Value *varg2 = arg2.inline_roots.empty() && arg2.ispointer() ? data_pointer(ctx, arg2) : value_to_pointer(ctx, arg2).V; varg1 = emit_pointer_from_objref(ctx, varg1); varg2 = emit_pointer_from_objref(ctx, varg2); @@ -3561,9 +3615,9 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a return ctx.builder.CreateICmpEQ(answer, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)); } else if (sz > 512 && jl_struct_try_layout(sty) && sty->layout->flags.isbitsegal) { - Value *varg1 = arg1.ispointer() ? data_pointer(ctx, arg1) : + Value *varg1 = arg1.inline_roots.empty() && arg1.ispointer() ? data_pointer(ctx, arg1) : value_to_pointer(ctx, arg1).V; - Value *varg2 = arg2.ispointer() ? data_pointer(ctx, arg2) : + Value *varg2 = arg2.inline_roots.empty() && arg2.ispointer() ? data_pointer(ctx, arg2) : value_to_pointer(ctx, arg2).V; varg1 = emit_pointer_from_objref(ctx, varg1); varg2 = emit_pointer_from_objref(ctx, varg2); @@ -4610,34 +4664,33 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // For tuples, we can emit code even if we don't know the exact // type (e.g. because we don't know the length). This is possible // as long as we know that all elements are of the same (leaf) type. - if (obj.ispointer()) { - if (order != jl_memory_order_notatomic && order != jl_memory_order_unspecified) { - emit_atomic_error(ctx, "getfield: non-atomic field cannot be accessed atomically"); - *ret = jl_cgval_t(); // unreachable - return true; - } - // Determine which was the type that was homogeneous - jl_value_t *jt = jl_tparam0(utt); - if (jl_is_vararg(jt)) - jt = jl_unwrap_vararg(jt); - assert(jl_is_datatype(jt)); - // This is not necessary for correctness, but allows to omit - // the extra code for getting the length of the tuple - if (!bounds_check_enabled(ctx, boundscheck)) { - vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); - } - else { - vidx = emit_bounds_check(ctx, obj, (jl_value_t*)obj.typ, vidx, - emit_datatype_nfields(ctx, emit_typeof(ctx, obj, false, false)), - jl_true); - } - bool isboxed = !jl_datatype_isinlinealloc((jl_datatype_t*)jt, 0); - Value *ptr = data_pointer(ctx, obj); - *ret = typed_load(ctx, ptr, vidx, - isboxed ? (jl_value_t*)jl_any_type : jt, - obj.tbaa, nullptr, isboxed, AtomicOrdering::NotAtomic, false); + jl_cgval_t ptrobj = obj.isboxed ? obj : value_to_pointer(ctx, obj); + if (order != jl_memory_order_notatomic && order != jl_memory_order_unspecified) { + emit_atomic_error(ctx, "getfield: non-atomic field cannot be accessed atomically"); + *ret = jl_cgval_t(); // unreachable return true; } + // Determine which was the type that was homogeneous + jl_value_t *jt = jl_tparam0(utt); + if (jl_is_vararg(jt)) + jt = jl_unwrap_vararg(jt); + assert(jl_is_datatype(jt)); + // This is not necessary for correctness, but allows to omit + // the extra code for getting the length of the tuple + if (!bounds_check_enabled(ctx, boundscheck)) { + vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); + } + else { + vidx = emit_bounds_check(ctx, ptrobj, (jl_value_t*)ptrobj.typ, vidx, + emit_datatype_nfields(ctx, emit_typeof(ctx, ptrobj, false, false)), + jl_true); + } + bool isboxed = !jl_datatype_isinlinealloc((jl_datatype_t*)jt, 0); + Value *ptr = data_pointer(ctx, ptrobj); + *ret = typed_load(ctx, ptr, vidx, + isboxed ? (jl_value_t*)jl_any_type : jt, + ptrobj.tbaa, nullptr, isboxed, AtomicOrdering::NotAtomic, false); + return true; } // Unknown object, but field known to be integer @@ -4914,7 +4967,12 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else if (jl_field_isptr(stt, fieldidx) || jl_type_hasptr(jl_field_type(stt, fieldidx))) { Value *fldv; size_t offs = jl_field_offset(stt, fieldidx) / sizeof(jl_value_t*); - if (obj.ispointer()) { + if (!obj.inline_roots.empty()) { + auto offsets = split_value_field(stt, fieldidx); + assert(offsets.second >= 0); + fldv = obj.inline_roots[offsets.second]; + } + else if (obj.ispointer()) { auto tbaa = best_field_tbaa(ctx, obj, stt, fieldidx, offs); if (!jl_field_isptr(stt, fieldidx)) offs += ((jl_datatype_t*)jl_field_type(stt, fieldidx))->layout->first_ptr; @@ -5033,26 +5091,18 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos SmallVector argvals(nfargs); unsigned idx = 0; AllocaInst *result = nullptr; - switch (returninfo.cc) { - case jl_returninfo_t::Boxed: - case jl_returninfo_t::Register: - case jl_returninfo_t::Ghosts: - break; - case jl_returninfo_t::SRet: - result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType(), Align(julia_alignment(jlretty))); - argvals[idx] = result; - idx++; - break; - case jl_returninfo_t::Union: - result = emit_static_alloca(ctx, ArrayType::get(getInt8Ty(ctx.builder.getContext()), returninfo.union_bytes), Align(returninfo.union_align)); + + if (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union) { + result = emit_static_alloca(ctx, returninfo.union_bytes, Align(returninfo.union_align)); setName(ctx.emission_context, result, "sret_box"); argvals[idx] = result; idx++; - break; } + AllocaInst *return_roots = nullptr; if (returninfo.return_roots) { - AllocaInst *return_roots = emit_static_alloca(ctx, ArrayType::get(ctx.types().T_prjlvalue, returninfo.return_roots), Align(alignof(jl_value_t*))); + assert(returninfo.cc == jl_returninfo_t::SRet); + return_roots = emit_static_roots(ctx, returninfo.return_roots); argvals[idx] = return_roots; idx++; } @@ -5063,16 +5113,27 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(specTypes, i); // n.b.: specTypes is required to be a datatype by construction for specsig - jl_cgval_t arg = argv[i]; if (is_opaque_closure && i == 0) { // Special implementation for opaque closures: their jt and thus // julia_type_to_llvm values are likely wrong, so override the // behavior here to directly pass the expected pointer based instead // just on passing arg as a pointer - arg = value_to_pointer(ctx, arg); - argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); + jl_cgval_t arg = argv[i]; + if (arg.isghost) { + argvals[idx] = Constant::getNullValue(ctx.builder.getPtrTy(AddressSpace::Derived)); + } + else { + if (!arg.isboxed) + arg = value_to_pointer(ctx, arg); + argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); + } + idx++; + continue; } - else if (is_uniquerep_Type(jt)) { + jl_cgval_t arg = update_julia_type(ctx, argv[i], jt); + if (arg.typ == jl_bottom_type) + return jl_cgval_t(); + if (is_uniquerep_Type(jt)) { continue; } else { @@ -5085,8 +5146,24 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos argvals[idx] = boxed(ctx, arg); } else if (et->isAggregateType()) { - arg = value_to_pointer(ctx, arg); - argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); + auto tracked = CountTrackedPointers(et); + if (tracked.count && !tracked.all) { + Value *val = arg.V; + SmallVector roots(arg.inline_roots); + if (roots.empty()) + std::tie(val, roots) = split_value(ctx, arg, Align(jl_datatype_align(jt))); + AllocaInst *proots = emit_static_roots(ctx, roots.size()); + for (size_t i = 0; i < roots.size(); i++) + ctx.builder.CreateAlignedStore(roots[i], emit_ptrgep(ctx, proots, i * sizeof(void*)), Align(sizeof(void*))); + assert(val); + argvals[idx++] = decay_derived(ctx, val); + argvals[idx] = proots; + } + else { + if (!arg.isboxed) + arg = value_to_pointer(ctx, arg); + argvals[idx] = decay_derived(ctx, data_pointer(ctx, arg)); + } } else { Value *val = emit_unbox(ctx, et, arg, jt); @@ -5132,7 +5209,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos break; case jl_returninfo_t::SRet: assert(result); - retval = mark_julia_slot(result, jlretty, NULL, ctx.tbaa().tbaa_stack); + retval = mark_julia_slot(result, jlretty, NULL, ctx.tbaa().tbaa_gcframe, load_gc_roots(ctx, return_roots, returninfo.return_roots)); break; case jl_returninfo_t::Union: { Value *box = ctx.builder.CreateExtractValue(call, 0); @@ -5460,7 +5537,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo JuliaFunction<> *cc; if (f.typ == (jl_value_t*)jl_intrinsic_type) { fptr = prepare_call(jlintrinsic_func); - F = f.ispointer() ? data_pointer(ctx, f) : value_to_pointer(ctx, f).V; + F = f.inline_roots.empty() && f.ispointer() ? data_pointer(ctx, f) : value_to_pointer(ctx, f).V; F = decay_derived(ctx, F); cc = julia_call3; } @@ -5712,42 +5789,53 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym, int allow_i } static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *varname) { - jl_value_t *typ = vi.value.typ; jl_cgval_t v; Value *isnull = NULL; if (vi.boxroot == NULL || vi.pTIndex != NULL) { - if ((!vi.isVolatile && vi.isSA) || vi.isArgument || vi.value.constant || !vi.value.V) { + if ((!vi.isVolatile && vi.isSA) || vi.isArgument || vi.value.constant || !(vi.value.V || vi.inline_roots)) { v = vi.value; if (vi.pTIndex) v.TIndex = ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), vi.pTIndex, Align(1)); } else { // copy value to a non-mutable (non-volatile SSA) location - AllocaInst *varslot = cast(vi.value.V); - setName(ctx.emission_context, varslot, jl_symbol_name(varname)); - Type *T = varslot->getAllocatedType(); - assert(!varslot->isArrayAllocation() && "variables not expected to be VLA"); - AllocaInst *ssaslot = cast(varslot->clone()); - setName(ctx.emission_context, ssaslot, jl_symbol_name(varname) + StringRef(".ssa")); - ssaslot->insertAfter(varslot); - if (vi.isVolatile) { - Value *unbox = ctx.builder.CreateAlignedLoad(ssaslot->getAllocatedType(), varslot, - varslot->getAlign(), - true); - ctx.builder.CreateAlignedStore(unbox, ssaslot, ssaslot->getAlign()); - } - else { - const DataLayout &DL = jl_Module->getDataLayout(); - uint64_t sz = DL.getTypeStoreSize(T); - emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign(), varslot->getAlign()); + // since this might be a union slot, the most convenient approach to copying + // is to move the whole alloca chunk + AllocaInst *ssaslot = nullptr; + if (vi.value.V) { + auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); + AllocaInst *varslot = cast(vi.value.V); + Type *T = varslot->getAllocatedType(); + assert(!varslot->isArrayAllocation() && "variables not expected to be VLA"); + ssaslot = cast(varslot->clone()); + setName(ctx.emission_context, ssaslot, varslot->getName() + StringRef(".ssa")); + ssaslot->insertAfter(varslot); + if (vi.isVolatile) { + Value *unbox = ctx.builder.CreateAlignedLoad(ssaslot->getAllocatedType(), varslot, varslot->getAlign(), true); + stack_ai.decorateInst(ctx.builder.CreateAlignedStore(unbox, ssaslot, ssaslot->getAlign())); + } + else { + const DataLayout &DL = jl_Module->getDataLayout(); + uint64_t sz = DL.getTypeStoreSize(T); + emit_memcpy(ctx, ssaslot, stack_ai, vi.value, sz, ssaslot->getAlign(), varslot->getAlign()); + } } Value *tindex = NULL; if (vi.pTIndex) tindex = ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), vi.pTIndex, Align(1), vi.isVolatile); - v = mark_julia_slot(ssaslot, vi.value.typ, tindex, ctx.tbaa().tbaa_stack); + v = mark_julia_slot(ssaslot, vi.value.typ, tindex, ctx.tbaa().tbaa_stack, None); + } + if (vi.inline_roots) { + AllocaInst *varslot = vi.inline_roots; + size_t nroots = cast(varslot->getArraySize())->getZExtValue(); + auto T_prjlvalue = varslot->getAllocatedType(); + if (auto AT = dyn_cast(T_prjlvalue)) { + nroots *= AT->getNumElements(); + T_prjlvalue = AT->getElementType(); + } + assert(T_prjlvalue == ctx.types().T_prjlvalue); + v.inline_roots = load_gc_roots(ctx, varslot, nroots, vi.isVolatile); } - if (vi.boxroot == NULL) - v = update_julia_type(ctx, v, typ); if (vi.usedUndef) { assert(vi.defFlag); isnull = ctx.builder.CreateAlignedLoad(getInt1Ty(ctx.builder.getContext()), vi.defFlag, Align(1), vi.isVolatile); @@ -5758,7 +5846,7 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va Value *box_isnull = NULL; if (vi.usedUndef) box_isnull = ctx.builder.CreateICmpNE(boxed, Constant::getNullValue(ctx.types().T_prjlvalue)); - maybe_mark_load_dereferenceable(boxed, vi.usedUndef || vi.pTIndex, typ); + maybe_mark_load_dereferenceable(boxed, vi.usedUndef || vi.pTIndex, vi.value.typ); if (vi.pTIndex) { // value is either boxed in the stack slot, or unboxed in value // as indicated by testing (pTIndex & UNION_BOX_MARKER) @@ -5767,15 +5855,14 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); if (vi.usedUndef) isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); - if (v.V) { // v.V will be null if it is a union of all ghost values + if (v.V) // v.V will be null if it is a union of all ghost values v.V = ctx.builder.CreateSelect(load_unbox, decay_derived(ctx, v.V), decay_derived(ctx, boxed)); - } else + else v.V = boxed; v.Vboxed = boxed; - v = update_julia_type(ctx, v, typ); } else { - v = mark_julia_type(ctx, boxed, true, typ); + v = mark_julia_type(ctx, boxed, true, vi.value.typ); if (vi.usedUndef) isnull = box_isnull; } @@ -5807,49 +5894,27 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu store_def_flag(ctx, vi, true); if (!vi.value.constant) { // check that this is not a virtual store - assert(vi.value.ispointer() || (vi.pTIndex && vi.value.V == NULL)); + assert(vi.inline_roots || vi.value.ispointer() || (vi.pTIndex && vi.value.V == NULL)); // store value - if (vi.value.V == NULL) { - // all ghost values in destination - nothing to copy or store - } - else if (rval_info.constant || !rval_info.ispointer()) { - if (rval_info.isghost) { - // all ghost values in source - nothing to copy or store - } - else { - if (rval_info.typ != vi.value.typ && !vi.pTIndex && !rval_info.TIndex) { - // isbits cast-on-assignment is invalid. this branch should be dead-code. - CreateTrap(ctx.builder); - } - else { - Value *dest = vi.value.V; - if (vi.pTIndex) // TODO: use lifetime-end here instead - ctx.builder.CreateStore(UndefValue::get(cast(vi.value.V)->getAllocatedType()), vi.value.V); - Type *store_ty = julia_type_to_llvm(ctx, rval_info.constant ? jl_typeof(rval_info.constant) : rval_info.typ); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); - ai.decorateInst(ctx.builder.CreateStore( - emit_unbox(ctx, store_ty, rval_info, rval_info.typ), - dest, - vi.isVolatile)); - } - } - } - else { - if (vi.pTIndex == NULL) { - assert(jl_is_concrete_type(vi.value.typ)); - // Sometimes we can get into situations where the LHS and RHS - // are the same slot. We're not allowed to memcpy in that case - // due to LLVM bugs. - // This check should probably mostly catch the relevant situations. - if (vi.value.V != rval_info.V) { - Value *copy_bytes = ConstantInt::get(getInt32Ty(ctx.builder.getContext()), jl_datatype_size(vi.value.typ)); - Align alignment(julia_alignment(rval_info.typ)); - emit_memcpy(ctx, vi.value.V, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), rval_info, copy_bytes, - alignment, alignment, vi.isVolatile); - } - } + rval_info = update_julia_type(ctx, rval_info, vi.value.typ); + if (rval_info.typ == jl_bottom_type) + return; + if (vi.pTIndex && vi.value.V) // TODO: use lifetime-end here instead + ctx.builder.CreateStore(UndefValue::get(cast(vi.value.V)->getAllocatedType()), vi.value.V); + // Sometimes we can get into situations where the LHS and RHS + // are the same slot. We're not allowed to memcpy in that case + // due to LLVM bugs. + // This check should probably mostly catch the relevant situations. + if (vi.value.V != nullptr ? vi.value.V != rval_info.V : vi.inline_roots != nullptr) { + MDNode *tbaa = ctx.tbaa().tbaa_stack; // Use vi.value.tbaa ? + if (rval_info.TIndex) + emit_unionmove(ctx, vi.value.V, tbaa, rval_info, /*skip*/isboxed, vi.isVolatile); else { - emit_unionmove(ctx, vi.value.V, ctx.tbaa().tbaa_stack, rval_info, /*skip*/isboxed, vi.isVolatile); + Align align(julia_alignment(rval_info.typ)); + if (vi.inline_roots) + split_value_into(ctx, rval_info, align, vi.value.V, align, jl_aliasinfo_t::fromTBAA(ctx, tbaa), vi.inline_roots, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe), vi.isVolatile); + else + emit_unbox_store(ctx, rval_info, vi.value.V, tbaa, align, vi.isVolatile); } } } @@ -5864,7 +5929,8 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) jl_value_t *phiType = NULL; if (jl_is_array(ssavalue_types)) { phiType = jl_array_ptr_ref(ssavalue_types, idx); - } else { + } + else { phiType = (jl_value_t*)jl_any_type; } jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(r, 0); @@ -5874,6 +5940,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) return; } AllocaInst *dest = nullptr; + SmallVector roots; // N.B.: For any memory space, used as a phi, // we need to emit space twice here. The reason for this is that // phi nodes may be arguments of other phi nodes, so if we don't @@ -5884,7 +5951,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) size_t min_align, nbytes; dest = try_emit_union_alloca(ctx, ((jl_uniontype_t*)phiType), allunbox, min_align, nbytes); if (dest) { - Instruction *phi = dest->clone(); + AllocaInst *phi = cast(dest->clone()); phi->insertAfter(dest); PHINode *Tindex_phi = PHINode::Create(getInt8Ty(ctx.builder.getContext()), jl_array_nrows(edges), "tindex_phi"); Tindex_phi->insertInto(BB, InsertPt); @@ -5893,14 +5960,14 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) Value *isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(Tindex_phi, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), UNION_BOX_MARKER)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); - ctx.builder.CreateMemCpy(phi, MaybeAlign(min_align), dest, dest->getAlign(), nbytes, false); + ctx.builder.CreateMemCpy(phi, Align(min_align), dest, dest->getAlign(), nbytes, false); ctx.builder.CreateLifetimeEnd(dest); Value *ptr = ctx.builder.CreateSelect(isboxed, decay_derived(ctx, ptr_phi), decay_derived(ctx, phi)); jl_cgval_t val = mark_julia_slot(ptr, phiType, Tindex_phi, best_tbaa(ctx.tbaa(), phiType)); val.Vboxed = ptr_phi; - ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, ptr_phi, r)); + ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, ptr_phi, roots, r)); ctx.SAvalues[idx] = val; ctx.ssavalue_assigned[idx] = true; return; @@ -5909,7 +5976,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) PHINode *Tindex_phi = PHINode::Create(getInt8Ty(ctx.builder.getContext()), jl_array_nrows(edges), "tindex_phi"); Tindex_phi->insertInto(BB, InsertPt); jl_cgval_t val = mark_julia_slot(NULL, phiType, Tindex_phi, ctx.tbaa().tbaa_stack); - ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, (PHINode*)NULL, r)); + ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, (PHINode*)nullptr, roots, r)); ctx.SAvalues[idx] = val; ctx.ssavalue_assigned[idx] = true; return; @@ -5928,22 +5995,38 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) } jl_cgval_t slot; PHINode *value_phi = NULL; - if (vtype->isAggregateType() && CountTrackedPointers(vtype).count == 0) { + if (!isboxed && vtype->isAggregateType()) { // the value will be moved into dest in the predecessor critical block. // here it's moved into phi in the successor (from dest) - Align align(julia_alignment(phiType)); - dest = emit_static_alloca(ctx, vtype, align); - Value *phi = emit_static_alloca(ctx, vtype, align); - ctx.builder.CreateMemCpy(phi, align, dest, align, jl_datatype_size(phiType), false); - ctx.builder.CreateLifetimeEnd(dest); - slot = mark_julia_slot(phi, phiType, NULL, ctx.tbaa().tbaa_stack); + auto tracked = CountTrackedPointers(vtype); + if (tracked.count) { + roots.resize(tracked.count); + assert(tracked.count == split_value_size((jl_datatype_t*)phiType).second); + for (size_t nr = 0; nr < tracked.count; nr++) { + auto root_phi = PHINode::Create(ctx.types().T_prjlvalue, jl_array_nrows(edges), "root_phi"); + root_phi->insertInto(BB, InsertPt); + roots[nr] = root_phi; + } + } + AllocaInst *phi = nullptr; + if (!tracked.all) { + Align align(julia_alignment(phiType)); + unsigned nb = jl_datatype_size(phiType); + dest = emit_static_alloca(ctx, nb, align); + phi = cast(dest->clone()); + phi->insertBefore(dest); + ctx.builder.CreateMemCpy(phi, align, dest, align, nb, false); + ctx.builder.CreateLifetimeEnd(dest); + } + slot = mark_julia_slot(phi, phiType, NULL, ctx.tbaa().tbaa_stack, + roots.empty() ? ArrayRef() : ArrayRef((Value *const *)&roots.front(), roots.size())); } else { value_phi = PHINode::Create(vtype, jl_array_nrows(edges), "value_phi"); value_phi->insertInto(BB, InsertPt); slot = mark_julia_type(ctx, value_phi, isboxed, phiType); } - ctx.PhiNodes.push_back(std::make_tuple(slot, BB, dest, value_phi, r)); + ctx.PhiNodes.push_back(std::make_tuple(slot, BB, dest, value_phi, roots, r)); ctx.SAvalues[idx] = slot; ctx.ssavalue_assigned[idx] = true; return; @@ -5963,8 +6046,9 @@ static void emit_ssaval_assign(jl_codectx_t &ctx, ssize_t ssaidx_0based, jl_valu it = ctx.phic_slots.emplace(ssaidx_0based, jl_varinfo_t(ctx.builder.getContext())).first; } slot = emit_varinfo(ctx, it->second, jl_symbol("phic")); - } else { - slot = emit_expr(ctx, r, ssaidx_0based); // slot could be a jl_value_t (unboxed) or jl_value_t* (ispointer) + } + else { + slot = emit_expr(ctx, r, ssaidx_0based); } if (slot.isboxed || slot.TIndex) { // see if inference suggested a different type for the ssavalue than the expression @@ -6123,11 +6207,22 @@ static void emit_upsilonnode(jl_codectx_t &ctx, ssize_t phic, jl_value_t *val) vi.pTIndex, Align(1), true); } else if (vi.value.V && !vi.value.constant && vi.value.typ != jl_bottom_type) { - assert(vi.value.ispointer()); - Type *T = cast(vi.value.V)->getAllocatedType(); - if (CountTrackedPointers(T).count) { - // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL - ctx.builder.CreateStore(Constant::getNullValue(T), vi.value.V, true); + assert(vi.inline_roots || vi.value.ispointer()); + if (vi.inline_roots) { + // memory optimization: make gc pointers re-initialized to NULL + AllocaInst *ssaroots = vi.inline_roots; + size_t nroots = cast(ssaroots->getArraySize())->getZExtValue(); + auto T_prjlvalue = ssaroots->getAllocatedType(); + if (auto AT = dyn_cast(T_prjlvalue)) { + nroots *= AT->getNumElements(); + T_prjlvalue = AT->getElementType(); + } + assert(T_prjlvalue == ctx.types().T_prjlvalue); + Value *nullval = Constant::getNullValue(T_prjlvalue); + auto stack_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + for (size_t i = 0; i < nroots; i++) { + stack_ai.decorateInst(ctx.builder.CreateAlignedStore(nullval, emit_ptrgep(ctx, ssaroots, i * sizeof(void*)), ssaroots->getAlign(), true)); + } } } } @@ -6865,14 +6960,17 @@ static void emit_cfunc_invalidate( ++AI; // gcstack_arg } for (size_t i = 0; i < nargs; i++) { + // n.b. calltype is required to be a datatype by construction for specsig jl_value_t *jt = jl_nth_slot_type(calltype, i); - // n.b. specTypes is required to be a datatype by construction for specsig - bool isboxed = false; - Type *et; if (i == 0 && is_for_opaque_closure) { - et = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); + Value *arg_v = &*AI; + ++AI; + myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const); + continue; } - else if (deserves_argbox(jt)) { + bool isboxed = false; + Type *et; + if (deserves_argbox(jt)) { et = ctx.types().T_prjlvalue; isboxed = true; } @@ -6889,8 +6987,14 @@ static void emit_cfunc_invalidate( else { Value *arg_v = &*AI; ++AI; - if ((i == 0 && is_for_opaque_closure) || (!isboxed && et->isAggregateType())) { - myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const); + if (!isboxed && et->isAggregateType()) { + auto tracked = CountTrackedPointers(et); + SmallVector roots; + if (tracked.count && !tracked.all) { + roots = load_gc_roots(ctx, &*AI, tracked.count); + ++AI; + } + myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const, roots); } else { assert(arg_v->getType() == et); @@ -6903,6 +7007,7 @@ static void emit_cfunc_invalidate( jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type); if (cc != jl_returninfo_t::Boxed) { emit_typecheck(ctx, gf_retbox, rettype, "cfunction"); + gf_retbox = update_julia_type(ctx, gf_retbox, rettype); } switch (cc) { @@ -6920,14 +7025,15 @@ static void emit_cfunc_invalidate( break; } case jl_returninfo_t::SRet: { + Value *sret = &*gf_thunk->arg_begin(); + Align align(julia_alignment(rettype)); if (return_roots) { - Value *root1 = gf_thunk->arg_begin() + 1; // root1 has type [n x {}*]* - // store the whole object in the first slot - ctx.builder.CreateStore(gf_ret, root1); + Value *roots = gf_thunk->arg_begin() + 1; // root1 has type [n x {}*]* + split_value_into(ctx, gf_retbox, align, sret, align, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), roots, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe)); + } + else { + emit_unbox_store(ctx, gf_retbox, sret, ctx.tbaa().tbaa_stack, align); } - Align alignment(julia_alignment(rettype)); - emit_memcpy(ctx, &*gf_thunk->arg_begin(), jl_aliasinfo_t::fromTBAA(ctx, nullptr), gf_ret, - jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), Align(alignment), Align(alignment)); ctx.builder.CreateRetVoid(); break; } @@ -7698,14 +7804,18 @@ static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, j Align(sizeof(void*))); retval = mark_julia_type(ctx, theArg, true, jl_any_type); } - ctx.builder.CreateRet(boxed(ctx, retval)); + if (retval.typ == jl_bottom_type) + CreateTrap(ctx.builder, false); + else + ctx.builder.CreateRet(boxed(ctx, retval)); } -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, size_t *arg_offset) +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, + ArrayRef ArgNames, unsigned nreq) { jl_returninfo_t props = {}; - SmallVector fsig; - SmallVector argnames; + SmallVector fsig; + SmallVector argnames; Type *rt = NULL; Type *srt = NULL; if (jlrettype == (jl_value_t*)jl_bottom_type) { @@ -7742,8 +7852,10 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value if (rt != getVoidTy(ctx.builder.getContext()) && deserves_sret(jlrettype, rt)) { auto tracked = CountTrackedPointers(rt, true); assert(!tracked.derived); - if (tracked.count && !tracked.all) + if (tracked.count && !tracked.all) { props.return_roots = tracked.count; + assert(props.return_roots == ((jl_datatype_t*)jlrettype)->layout->npointers); + } props.cc = jl_returninfo_t::SRet; props.union_bytes = jl_datatype_size(jlrettype); props.union_align = props.union_minalign = jl_datatype_align(jlrettype); @@ -7801,29 +7913,22 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value argnames.push_back("pgcstack_arg"); } - if (arg_offset) - *arg_offset = fsig.size(); size_t nparams = jl_nparams(sig); - if (used_arguments) - used_arguments->resize(nparams); - for (size_t i = 0; i < nparams; i++) { jl_value_t *jt = jl_tparam(sig, i); bool isboxed = false; - Type *ty = NULL; - if (i == 0 && is_opaque_closure) { - ty = nullptr; // special token to avoid computing this unnecessarily - } - else { + Type *et = nullptr; + if (i != 0 || !is_opaque_closure) { // special token for OC argument if (is_uniquerep_Type(jt)) continue; isboxed = deserves_argbox(jt); - ty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); - if (type_is_ghost(ty)) + et = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); + if (type_is_ghost(et)) continue; } AttrBuilder param(ctx.builder.getContext()); - if (ty == nullptr || ty->isAggregateType()) { // aggregate types are passed by pointer + Type *ty = et; + if (et == nullptr || et->isAggregateType()) { // aggregate types are passed by pointer param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::ReadOnly); ty = ctx.builder.getPtrTy(AddressSpace::Derived); @@ -7838,8 +7943,26 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value } attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param)); fsig.push_back(ty); - if (used_arguments) - used_arguments->set(i); + size_t argno = i < nreq ? i : nreq; + std::string genname; + if (!ArgNames.empty()) { + genname = ArgNames[argno]; + if (genname.empty()) + genname = (StringRef("#") + Twine(argno + 1)).str(); + if (i >= nreq) + genname += (StringRef("[") + Twine(i - nreq + 1) + StringRef("]")).str(); + const char *arg_typename = jl_is_datatype(jt) ? jl_symbol_name(((jl_datatype_t*)jt)->name->name) : ""; + argnames.push_back((genname + StringRef("::") + arg_typename).str()); + } + if (et && et->isAggregateType()) { + auto tracked = CountTrackedPointers(et); + if (tracked.count && !tracked.all) { + attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param)); + fsig.push_back(ctx.builder.getPtrTy(M->getDataLayout().getAllocaAddrSpace())); + if (!genname.empty()) + argnames.push_back((Twine(".roots.") + genname).str()); + } + } } AttributeSet FnAttrs; @@ -7887,12 +8010,6 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value return props; } -static void emit_sret_roots(jl_codectx_t &ctx, bool isptr, Value *Src, Type *T, Value *Shadow, unsigned count) -{ - unsigned emitted = TrackWithShadow(Src, T, isptr, Shadow, ctx.builder); //This comes from Late-GC-Lowering?? - assert(emitted == count); (void)emitted; (void)count; -} - static DISubroutineType * get_specsig_di(jl_codectx_t &ctx, jl_debugcache_t &debuginfo, jl_value_t *rt, jl_value_t *sig, DIBuilder &dbuilder) { @@ -8105,49 +8222,26 @@ static jl_llvm_functions_t Function *f = NULL; bool has_sret = false; if (specsig) { // assumes !va and !needsparams - BitVector used_args; - size_t args_begin; - returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, lam->specTypes, - jlrettype, ctx.is_opaque_closure, JL_FEAT_TEST(ctx,gcstack_arg), &used_args, &args_begin); - f = cast(returninfo.decl.getCallee()); - has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); - jl_init_function(f, ctx.emission_context.TargetTriple); + SmallVector ArgNames(0); if (ctx.emission_context.debug_level >= 2) { - auto arg_typename = [&](size_t i) JL_NOTSAFEPOINT { - auto tp = jl_tparam(lam->specTypes, i); - return jl_is_datatype(tp) ? jl_symbol_name(((jl_datatype_t*)tp)->name->name) : ""; - }; - size_t nreal = 0; - for (size_t i = 0; i < std::min(nreq, static_cast(used_args.size())); i++) { + ArgNames.resize(ctx.nargs, ""); + for (int i = 0; i < ctx.nargs; i++) { jl_sym_t *argname = slot_symbol(ctx, i); if (argname == jl_unused_sym) continue; - if (used_args.test(i)) { - auto &arg = *f->getArg(args_begin++); - nreal++; - auto name = jl_symbol_name(argname); - if (!name[0]) { - arg.setName(StringRef("#") + Twine(nreal) + StringRef("::") + arg_typename(i)); - } else { - arg.setName(name + StringRef("::") + arg_typename(i)); - } - } - } - if (va && ctx.vaSlot != -1) { - size_t vidx = 0; - for (size_t i = nreq; i < used_args.size(); i++) { - if (used_args.test(i)) { - auto &arg = *f->getArg(args_begin++); - auto type = arg_typename(i); - const char *name = jl_symbol_name(slot_symbol(ctx, ctx.vaSlot)); - if (!name[0]) - name = "..."; - vidx++; - arg.setName(name + StringRef("[") + Twine(vidx) + StringRef("]::") + type); - } - } + const char *name = jl_symbol_name(argname); + if (name[0] == '\0' && ctx.vaSlot == i) + ArgNames[i] = "..."; + else + ArgNames[i] = name; } } + returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, lam->specTypes, + jlrettype, ctx.is_opaque_closure, JL_FEAT_TEST(ctx,gcstack_arg), + ArgNames, nreq); + f = cast(returninfo.decl.getCallee()); + has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); + jl_init_function(f, ctx.emission_context.TargetTriple); // common pattern: see if all return statements are an argument in that // case the apply-generic call can re-use the original box for the return @@ -8348,14 +8442,16 @@ static jl_llvm_functions_t allocate_gc_frame(ctx, b0); Value *last_age = NULL; auto world_age_field = get_tls_world_age_field(ctx); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - last_age = ai.decorateInst(ctx.builder.CreateAlignedLoad( - ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); - ctx.world_age_at_entry = last_age; // Load world age for use in get_tls_world_age + { // scope + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + last_age = ai.decorateInst(ctx.builder.CreateAlignedLoad( + ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); + ctx.world_age_at_entry = last_age; // Load world age for use in get_tls_world_age + } // step 7. allocate local variables slots // must be in the first basic block for the llvm mem2reg pass to work - auto allocate_local = [&ctx, &dbuilder, &debugcache, topdebugloc, va, debug_enabled, M](jl_varinfo_t &varinfo, jl_sym_t *s, int i) { + auto allocate_local = [&ctx, &dbuilder, &debugcache, topdebugloc, va, debug_enabled](jl_varinfo_t &varinfo, jl_sym_t *s, int i) { jl_value_t *jt = varinfo.value.typ; assert(!varinfo.boxroot); // variables shouldn't have memory locs already if (varinfo.value.constant) { @@ -8375,13 +8471,13 @@ static jl_llvm_functions_t if (lv) { lv->setName(jl_symbol_name(s)); varinfo.value = mark_julia_slot(lv, jt, NULL, ctx.tbaa().tbaa_stack); - varinfo.pTIndex = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext()), Align(1)); + varinfo.pTIndex = emit_static_alloca(ctx, 1, Align(1)); setName(ctx.emission_context, varinfo.pTIndex, "tindex"); // TODO: attach debug metadata to this variable } else if (allunbox) { // all ghost values just need a selector allocated - AllocaInst *lv = emit_static_alloca(ctx, getInt8Ty(ctx.builder.getContext()), Align(1)); + AllocaInst *lv = emit_static_alloca(ctx, 1, Align(1)); lv->setName(jl_symbol_name(s)); varinfo.pTIndex = lv; varinfo.value.tbaa = NULL; @@ -8394,30 +8490,25 @@ static jl_llvm_functions_t return; } else if (deserves_stack(jt)) { - bool isboxed; - Type *vtype = julia_type_to_llvm(ctx, jt, &isboxed); - assert(!isboxed); - assert(!type_is_ghost(vtype) && "constants should already be handled"); - Value *lv = new AllocaInst(vtype, M->getDataLayout().getAllocaAddrSpace(), nullptr, Align(jl_datatype_align(jt)), jl_symbol_name(s), /*InsertBefore*/ctx.topalloca); - if (CountTrackedPointers(vtype).count) { - StoreInst *SI = new StoreInst(Constant::getNullValue(vtype), lv, false, Align(sizeof(void*))); - SI->insertAfter(ctx.topalloca); - } - varinfo.value = mark_julia_slot(lv, jt, NULL, ctx.tbaa().tbaa_stack); + auto sizes = split_value_size((jl_datatype_t*)jt); + AllocaInst *bits = sizes.first > 0 ? emit_static_alloca(ctx, sizes.first, Align(julia_alignment(jt))) : nullptr; + AllocaInst *roots = sizes.second > 0 ? emit_static_roots(ctx, sizes.second) : nullptr; + if (bits) bits->setName(jl_symbol_name(s)); + if (roots) roots->setName(StringRef(".roots.") + jl_symbol_name(s)); + varinfo.value = mark_julia_slot(bits, jt, NULL, ctx.tbaa().tbaa_stack, None); + varinfo.inline_roots = roots; alloc_def_flag(ctx, varinfo); if (debug_enabled && varinfo.dinfo) { assert((Metadata*)varinfo.dinfo->getType() != debugcache.jl_pvalue_dillvmt); - dbuilder.insertDeclare(lv, varinfo.dinfo, dbuilder.createExpression(), + dbuilder.insertDeclare(bits ? bits : roots, varinfo.dinfo, dbuilder.createExpression(), topdebugloc, ctx.builder.GetInsertBlock()); } return; } // otherwise give it a boxroot in this function - AllocaInst *av = new AllocaInst(ctx.types().T_prjlvalue, M->getDataLayout().getAllocaAddrSpace(), - nullptr, Align(sizeof(jl_value_t*)), jl_symbol_name(s), /*InsertBefore*/ctx.topalloca); - StoreInst *SI = new StoreInst(Constant::getNullValue(ctx.types().T_prjlvalue), av, false, Align(sizeof(void*))); - SI->insertAfter(ctx.topalloca); + AllocaInst *av = emit_static_roots(ctx, 1); + av->setName(jl_symbol_name(s)); varinfo.boxroot = av; if (debug_enabled && varinfo.dinfo) { SmallVector addr; @@ -8504,12 +8595,18 @@ static jl_llvm_functions_t ++AI; AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo())); jl_cgval_t theArg; - if (llvmArgType->isAggregateType()) { + if (!isboxed && llvmArgType->isAggregateType()) { maybe_mark_argument_dereferenceable(param, argType); - theArg = mark_julia_slot(Arg, argType, NULL, ctx.tbaa().tbaa_const); // this argument is by-pointer + SmallVector roots; + auto tracked = CountTrackedPointers(llvmArgType); + if (tracked.count && !tracked.all) { + roots = load_gc_roots(ctx, &*AI, tracked.count); + ++AI; + } + theArg = mark_julia_slot(Arg, argType, NULL, ctx.tbaa().tbaa_const, roots); // this argument is by-pointer } else { - if (isboxed) // e.g. is-pointer + if (isboxed) maybe_mark_argument_dereferenceable(param, argType); theArg = mark_julia_type(ctx, Arg, isboxed, argType); if (theArg.tbaa == ctx.tbaa().tbaa_immut) @@ -8566,95 +8663,92 @@ static jl_llvm_functions_t bool isboxed = deserves_argbox(argType); Type *llvmArgType = NULL; if (i == 0 && ctx.is_opaque_closure) { - isboxed = true; - llvmArgType = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); + isboxed = false; + llvmArgType = ctx.builder.getPtrTy(AddressSpace::Derived); argType = (jl_value_t*)jl_any_type; } else { llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); } - if (s == jl_unused_sym) { - if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) - ++AI; - continue; - } jl_varinfo_t &vi = ctx.slots[i]; - jl_cgval_t theArg; if (s == jl_unused_sym || vi.value.constant) { assert(vi.boxroot == NULL); - if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) + if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) { ++AI; + auto tracked = CountTrackedPointers(llvmArgType); + if (tracked.count && !tracked.all) + ++AI; + } + continue; } - else { - // If this is an opaque closure, implicitly load the env and switch - // the world age. - if (i == 0 && ctx.is_opaque_closure) { - // Load closure world - Value *oc_this = decay_derived(ctx, &*AI++); - Value *argaddr = oc_this; - Value *worldaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, world)); + jl_cgval_t theArg; + // If this is an opaque closure, implicitly load the env and switch + // the world age. + if (i == 0 && ctx.is_opaque_closure) { + // Load closure world + Value *oc_this = decay_derived(ctx, &*AI++); + Value *argaddr = oc_this; + Value *worldaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, world)); - jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, - nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); - ctx.world_age_at_entry = closure_world.V; // The tls world in a OC is the world of the closure - emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); + jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, + nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); + ctx.world_age_at_entry = closure_world.V; // The tls world in a OC is the world of the closure + emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); - // Load closure env - Value *envaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, captures)); + // Load closure env + Value *envaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, captures)); - jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, - nullptr, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); - theArg = update_julia_type(ctx, closure_env, vi.value.typ); - } - else if (specsig) { - theArg = get_specsig_arg(argType, llvmArgType, isboxed); + jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, + nullptr, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); + theArg = update_julia_type(ctx, closure_env, vi.value.typ); + } + else if (specsig) { + theArg = get_specsig_arg(argType, llvmArgType, isboxed); + } + else { + if (i == 0) { + // first (function) arg is separate in jlcall + theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); } else { - if (i == 0) { - // first (function) arg is separate in jlcall - theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); - } - else { - Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - Value *load = ai.decorateInst(maybe_mark_load_dereferenceable( - ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), - false, vi.value.typ)); - theArg = mark_julia_type(ctx, load, true, vi.value.typ); - if (debug_enabled && vi.dinfo && !vi.boxroot) { - SmallVector addr; + Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + Value *load = ai.decorateInst(maybe_mark_load_dereferenceable( + ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), + false, vi.value.typ)); + theArg = mark_julia_type(ctx, load, true, vi.value.typ); + if (debug_enabled && vi.dinfo && !vi.boxroot) { + SmallVector addr; + addr.push_back(llvm::dwarf::DW_OP_deref); + addr.push_back(llvm::dwarf::DW_OP_plus_uconst); + addr.push_back((i - 1) * sizeof(void*)); + if ((Metadata*)vi.dinfo->getType() != debugcache.jl_pvalue_dillvmt) addr.push_back(llvm::dwarf::DW_OP_deref); - addr.push_back(llvm::dwarf::DW_OP_plus_uconst); - addr.push_back((i - 1) * sizeof(void*)); - if ((Metadata*)vi.dinfo->getType() != debugcache.jl_pvalue_dillvmt) - addr.push_back(llvm::dwarf::DW_OP_deref); - dbuilder.insertDeclare(pargArray, vi.dinfo, dbuilder.createExpression(addr), - topdebugloc, - ctx.builder.GetInsertBlock()); - } + dbuilder.insertDeclare(pargArray, vi.dinfo, dbuilder.createExpression(addr), + topdebugloc, + ctx.builder.GetInsertBlock()); } } + } - - if (vi.boxroot == NULL) { - assert(vi.value.V == NULL && "unexpected variable slot created for argument"); - // keep track of original (possibly boxed) value to avoid re-boxing or moving - vi.value = theArg; - if (debug_enabled && vi.dinfo && theArg.V) { - if (theArg.ispointer()) { - dbuilder.insertDeclare(theArg.V, vi.dinfo, dbuilder.createExpression(), - topdebugloc, ctx.builder.GetInsertBlock()); - } - else { - dbuilder.insertDbgValueIntrinsic(theArg.V, vi.dinfo, dbuilder.createExpression(), - topdebugloc, ctx.builder.GetInsertBlock()); - } + if (vi.boxroot == nullptr) { + assert(vi.value.V == nullptr && vi.inline_roots == nullptr && "unexpected variable slot created for argument"); + // keep track of original (possibly boxed) value to avoid re-boxing or moving + vi.value = theArg; + if (debug_enabled && vi.dinfo && theArg.V) { + if (!theArg.inline_roots.empty() || theArg.ispointer()) { + dbuilder.insertDeclare(theArg.V, vi.dinfo, dbuilder.createExpression(), + topdebugloc, ctx.builder.GetInsertBlock()); + } + else { + dbuilder.insertDbgValueIntrinsic(theArg.V, vi.dinfo, dbuilder.createExpression(), + topdebugloc, ctx.builder.GetInsertBlock()); } } - else { - Value *argp = boxed(ctx, theArg); - ctx.builder.CreateStore(argp, vi.boxroot); - } + } + else { + Value *argp = boxed(ctx, theArg); + ctx.builder.CreateStore(argp, vi.boxroot); } } // step 9. allocate rest argument @@ -9129,29 +9223,31 @@ static jl_llvm_functions_t break; } if (sret) { - if (retvalinfo.ispointer()) { - if (returninfo.return_roots) { - Type *store_ty = julia_type_to_llvm(ctx, retvalinfo.typ); - emit_sret_roots(ctx, true, data_pointer(ctx, retvalinfo), store_ty, f->arg_begin() + 1, returninfo.return_roots); - } + Align align(returninfo.union_align); + if (!returninfo.return_roots && !retvalinfo.inline_roots.empty()) { + assert(retvalinfo.V == nullptr); + assert(returninfo.cc == jl_returninfo_t::SRet); + split_value_into(ctx, retvalinfo, align, nullptr, align, + jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), sret, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe)); + } + else if (returninfo.return_roots) { + assert(returninfo.cc == jl_returninfo_t::SRet); + Value *return_roots = f->arg_begin() + 1; + split_value_into(ctx, retvalinfo, align, sret, align, + jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), return_roots, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe)); + } + else if (retvalinfo.ispointer()) { if (returninfo.cc == jl_returninfo_t::SRet) { assert(jl_is_concrete_type(jlrettype)); - Align alignment(julia_alignment(jlrettype)); emit_memcpy(ctx, sret, jl_aliasinfo_t::fromTBAA(ctx, nullptr), retvalinfo, - jl_datatype_size(jlrettype), alignment, alignment); + jl_datatype_size(jlrettype), align, align); } else { // must be jl_returninfo_t::Union emit_unionmove(ctx, sret, nullptr, retvalinfo, /*skip*/isboxed_union); } } else { - Type *store_ty = retvalinfo.V->getType(); - Value *Val = retvalinfo.V; - if (returninfo.return_roots) { - assert(julia_type_to_llvm(ctx, retvalinfo.typ) == store_ty); - emit_sret_roots(ctx, false, Val, store_ty, f->arg_begin() + 1, returninfo.return_roots); - } - ctx.builder.CreateAlignedStore(Val, sret, Align(julia_alignment(retvalinfo.typ))); + ctx.builder.CreateAlignedStore(retvalinfo.V, sret, align); assert(retvalinfo.TIndex == NULL && "unreachable"); // unimplemented representation } } @@ -9288,8 +9384,9 @@ static jl_llvm_functions_t PHINode *VN; jl_value_t *r; AllocaInst *dest; + SmallVector roots; BasicBlock *PhiBB; - std::tie(phi_result, PhiBB, dest, VN, r) = tup; + std::tie(phi_result, PhiBB, dest, VN, roots, r) = tup; jl_value_t *phiType = phi_result.typ; jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(r, 0); jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(r, 1); @@ -9347,6 +9444,7 @@ static jl_llvm_functions_t val = mark_julia_const(ctx, val.constant); // be over-conservative at making sure `.typ` is set concretely, not tindex if (!jl_is_uniontype(phiType) || !TindexN) { if (VN) { + assert(roots.empty() && !dest); Value *V; if (val.typ == (jl_value_t*)jl_bottom_type) { V = undef_value_for_type(VN->getType()); @@ -9367,14 +9465,34 @@ static jl_llvm_functions_t VN->addIncoming(V, ctx.builder.GetInsertBlock()); assert(!TindexN); } - else if (dest && val.typ != (jl_value_t*)jl_bottom_type) { + else if ((dest || !roots.empty()) && val.typ != (jl_value_t*)jl_bottom_type) { // must be careful to emit undef here (rather than a bitcast or // load of val) if the runtime type of val isn't phiType + auto tracked = split_value_size((jl_datatype_t*)phiType).second; Value *isvalid = emit_isa_and_defined(ctx, val, phiType); - emit_guarded_test(ctx, isvalid, nullptr, [&] { - emit_unbox_store(ctx, update_julia_type(ctx, val, phiType), dest, ctx.tbaa().tbaa_stack, Align(julia_alignment(phiType))); - return nullptr; + assert(roots.size() == tracked && isvalid != nullptr); + SmallVector incomingroots(0); + if (tracked) + incomingroots.resize(tracked, Constant::getNullValue(ctx.types().T_prjlvalue)); + emit_guarded_test(ctx, isvalid, incomingroots, [&] { + jl_cgval_t typedval = update_julia_type(ctx, val, phiType); + SmallVector mayberoots(tracked, Constant::getNullValue(ctx.types().T_prjlvalue)); + if (typedval.typ != jl_bottom_type) { + Align align(julia_alignment(phiType)); + if (tracked) + split_value_into(ctx, typedval, align, dest, align, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), mayberoots); + else + emit_unbox_store(ctx, typedval, dest, ctx.tbaa().tbaa_stack, align); + } + return mayberoots; }); + for (size_t nr = 0; nr < tracked; nr++) + roots[nr]->addIncoming(incomingroots[nr], ctx.builder.GetInsertBlock()); + } + else if (!roots.empty()) { + Value *V = Constant::getNullValue(ctx.types().T_prjlvalue); + for (size_t nr = 0; nr < roots.size(); nr++) + roots[nr]->addIncoming(V, ctx.builder.GetInsertBlock()); } } else { @@ -9383,6 +9501,7 @@ static jl_llvm_functions_t // `V` is always initialized when it is used. // Ref https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96629 Value *V = nullptr; + assert(roots.empty()); if (val.typ == (jl_value_t*)jl_bottom_type) { if (VN) V = undef_value_for_type(VN->getType()); @@ -9473,11 +9592,10 @@ static jl_llvm_functions_t } if (TindexN) TindexN->addIncoming(RTindex, FromBB); - if (dest) { + if (dest) ctx.builder.CreateLifetimeStart(dest); - if (CountTrackedPointers(dest->getAllocatedType()).count) - ctx.builder.CreateStore(Constant::getNullValue(dest->getAllocatedType()), dest); - } + for (size_t nr = 0; nr < roots.size(); nr++) + roots[nr]->addIncoming(Constant::getNullValue(ctx.types().T_prjlvalue), FromBB); ctx.builder.ClearInsertionPoint(); } } @@ -9524,15 +9642,19 @@ static jl_llvm_functions_t if (ctx.vaSlot > 0) { // remove VA allocation if we never referenced it + assert(ctx.slots[ctx.vaSlot].isSA && ctx.slots[ctx.vaSlot].isArgument); Instruction *root = cast_or_null(ctx.slots[ctx.vaSlot].boxroot); if (root) { - Instruction *store_value = NULL; bool have_real_use = false; for (Use &U : root->uses()) { User *RU = U.getUser(); if (StoreInst *SRU = dyn_cast(RU)) { - if (!store_value) - store_value = dyn_cast(SRU->getValueOperand()); + assert(isa(SRU->getValueOperand()) || SRU->getValueOperand() == restTuple); + (void)SRU; + } + else if (MemSetInst *MSI = dyn_cast(RU)) { + assert(MSI->getValue() == ctx.builder.getInt8(0)); + (void)MSI; } else if (isa(RU)) { } @@ -9554,7 +9676,6 @@ static jl_llvm_functions_t if (use) use->eraseFromParent(); root->eraseFromParent(); - assert(!store_value || store_value == restTuple); restTuple->eraseFromParent(); } } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index c747edfeffe5f..09916297e16ff 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -441,14 +441,14 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va // up being dead code, and type inference knows that the other // branch's type is the only one that matters. if (type_is_ghost(to)) { - return NULL; + return nullptr; } CreateTrap(ctx.builder); return UndefValue::get(to); // type mismatch error } - Constant *c = x.constant ? julia_const_to_llvm(ctx, x.constant) : NULL; - if (!x.ispointer() || c) { // already unboxed, but sometimes need conversion + Constant *c = x.constant ? julia_const_to_llvm(ctx, x.constant) : nullptr; + if ((x.inline_roots.empty() && !x.ispointer()) || c != nullptr) { // already unboxed, but sometimes need conversion Value *unboxed = c ? c : x.V; return emit_unboxed_coercion(ctx, to, unboxed); } @@ -473,28 +473,17 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va } unsigned alignment = julia_alignment(jt); - Type *ptype = to->getPointerTo(); - if (p->getType() != ptype && isa(p)) { - // LLVM's mem2reg can't handle coercion if the load/store type does - // not match the type of the alloca. As such, it is better to - // perform the load using the alloca's type and then perform the - // appropriate coercion manually. - AllocaInst *AI = cast(p); - Type *AllocType = AI->getAllocatedType(); - const DataLayout &DL = jl_Module->getDataLayout(); - if (!AI->isArrayAllocation() && - (AllocType->isFloatingPointTy() || AllocType->isIntegerTy() || AllocType->isPointerTy()) && - (to->isFloatingPointTy() || to->isIntegerTy() || to->isPointerTy()) && - DL.getTypeSizeInBits(AllocType) == DL.getTypeSizeInBits(to)) { - Instruction *load = ctx.builder.CreateAlignedLoad(AllocType, p, Align(alignment)); - setName(ctx.emission_context, load, p->getName() + ".unbox"); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); - return emit_unboxed_coercion(ctx, to, ai.decorateInst(load)); - } + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); + if (!x.inline_roots.empty()) { + assert(x.typ == jt); + AllocaInst *combined = emit_static_alloca(ctx, to, Align(alignment)); + auto combined_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); + recombine_value(ctx, x, combined, combined_ai, Align(alignment), false); + p = combined; + ai = combined_ai; } Instruction *load = ctx.builder.CreateAlignedLoad(to, p, Align(alignment)); setName(ctx.emission_context, load, p->getName() + ".unbox"); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); return ai.decorateInst(load); } @@ -508,18 +497,25 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest return; } + auto dest_ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest); + + if (!x.inline_roots.empty()) { + recombine_value(ctx, x, dest, dest_ai, alignment, isVolatile); + return; + } + if (!x.ispointer()) { // already unboxed, but sometimes need conversion (e.g. f32 -> i32) assert(x.V); Value *unboxed = zext_struct(ctx, x.V); StoreInst *store = ctx.builder.CreateAlignedStore(unboxed, dest, alignment); store->setVolatile(isVolatile); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest); - ai.decorateInst(store); + dest_ai.decorateInst(store); return; } Value *src = data_pointer(ctx, x); - emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), Align(alignment), Align(julia_alignment(x.typ)), isVolatile); + auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, x.tbaa); + emit_memcpy(ctx, dest, dest_ai, src, src_ai, jl_datatype_size(x.typ), Align(alignment), Align(julia_alignment(x.typ)), isVolatile); } static jl_datatype_t *staticeval_bitstype(const jl_cgval_t &targ) @@ -832,10 +828,9 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); setName(ctx.emission_context, im1, "pointerset_idx"); - Value *thePtr; + Value *thePtr = emit_unbox(ctx, getPointerTy(ctx.builder.getContext()), e, e.typ); if (ety == (jl_value_t*)jl_any_type) { // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. - thePtr = emit_unbox(ctx, ctx.types().T_size->getPointerTo(), e, e.typ); auto gep = ctx.builder.CreateInBoundsGEP(ctx.types().T_size, thePtr, im1); setName(ctx.emission_context, gep, "pointerset_ptr"); auto val = ctx.builder.CreatePtrToInt(emit_pointer_from_objref(ctx, boxed(ctx, x)), ctx.types().T_size); @@ -844,8 +839,10 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_data); ai.decorateInst(store); } + else if (!x.inline_roots.empty()) { + recombine_value(ctx, e, thePtr, jl_aliasinfo_t(), Align(align_nb), false); + } else if (x.ispointer()) { - thePtr = emit_unbox(ctx, getPointerTy(ctx.builder.getContext()), e, e.typ); uint64_t size = jl_datatype_size(ety); im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); @@ -859,7 +856,6 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, ArrayRef argv) Type *ptrty = julia_type_to_llvm(ctx, ety, &isboxed); assert(!isboxed); if (!type_is_ghost(ptrty)) { - thePtr = emit_unbox(ctx, ptrty->getPointerTo(), e, e.typ); thePtr = ctx.builder.CreateInBoundsGEP(ptrty, thePtr, im1); typed_store(ctx, thePtr, x, jl_cgval_t(), ety, ctx.tbaa().tbaa_data, nullptr, nullptr, isboxed, AtomicOrdering::NotAtomic, AtomicOrdering::NotAtomic, align_nb, nullptr, true, false, false, false, false, false, nullptr, "atomic_pointerset", nullptr, nullptr); diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index 956c04dbc7ded..a99e18f3e3762 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -125,7 +125,6 @@ struct CountTrackedPointers { CountTrackedPointers(llvm::Type *T, bool ignore_loaded=false); }; -unsigned TrackWithShadow(llvm::Value *Src, llvm::Type *T, bool isptr, llvm::Value *Dst, llvm::IRBuilder<> &irbuilder); llvm::SmallVector ExtractTrackedValues(llvm::Value *Src, llvm::Type *STy, bool isptr, llvm::IRBuilder<> &irbuilder, llvm::ArrayRef perm_offsets={}); static inline void llvm_dump(llvm::Value *v) diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index 0605098bec361..76dcd944890ab 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -202,9 +202,9 @@ bool FinalLowerGC::runOnFunction(Function &F) } while (0) LOWER_INTRINSIC(newGCFrame, lowerNewGCFrame); + LOWER_INTRINSIC(getGCFrameSlot, lowerGetGCFrameSlot); LOWER_INTRINSIC(pushGCFrame, lowerPushGCFrame); LOWER_INTRINSIC(popGCFrame, lowerPopGCFrame); - LOWER_INTRINSIC(getGCFrameSlot, lowerGetGCFrameSlot); LOWER_INTRINSIC(GCAllocBytes, lowerGCAllocBytes); LOWER_INTRINSIC(queueGCRoot, lowerQueueGCRoot); LOWER_INTRINSIC(safepoint, lowerSafepoint); diff --git a/src/llvm-gc-interface-passes.h b/src/llvm-gc-interface-passes.h index cb485751d407b..d33567e887118 100644 --- a/src/llvm-gc-interface-passes.h +++ b/src/llvm-gc-interface-passes.h @@ -312,7 +312,6 @@ struct State { SmallVector> CalleeRoots; // We don't bother doing liveness on Allocas that were not mem2reg'ed. // they just get directly sunk into the root array. - SmallVector Allocas; DenseMap ArrayAllocas; DenseMap ShadowAllocas; SmallVector, 0> TrackedStores; @@ -332,9 +331,9 @@ struct LateLowerGCFrame: private JuliaPassContext { void MaybeNoteDef(State &S, BBState &BBS, Value *Def, const ArrayRef &SafepointsSoFar, SmallVector &&RefinedPtr = SmallVector()); - void NoteUse(State &S, BBState &BBS, Value *V, LargeSparseBitVector &Uses); - void NoteUse(State &S, BBState &BBS, Value *V) { - NoteUse(S, BBS, V, BBS.UpExposedUses); + void NoteUse(State &S, BBState &BBS, Value *V, LargeSparseBitVector &Uses, Function &F); + void NoteUse(State &S, BBState &BBS, Value *V, Function &F) { + NoteUse(S, BBS, V, BBS.UpExposedUses, F); } void LiftPhi(State &S, PHINode *Phi); @@ -348,7 +347,7 @@ struct LateLowerGCFrame: private JuliaPassContext { SmallVector NumberAll(State &S, Value *V); SmallVector NumberAllBase(State &S, Value *Base); - void NoteOperandUses(State &S, BBState &BBS, User &UI); + void NoteOperandUses(State &S, BBState &BBS, Instruction &UI); void MaybeTrackDst(State &S, MemTransferInst *MI); void MaybeTrackStore(State &S, StoreInst *I); State LocalScan(Function &F); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 8d1d5ff73b261..1d390a5115207 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -695,8 +695,15 @@ static int NoteSafepoint(State &S, BBState &BBS, CallInst *CI, SmallVectorImpl(V->getType())) { +// if (isSpecialPtr(V->getType())) +// if (isa(V) && !isa(V)) +// F.dump(); +// } +//#endif if (isa(V)) return; if (isa(V->getType())) { @@ -718,9 +725,9 @@ void LateLowerGCFrame::NoteUse(State &S, BBState &BBS, Value *V, LargeSparseBitV } } -void LateLowerGCFrame::NoteOperandUses(State &S, BBState &BBS, User &UI) { +void LateLowerGCFrame::NoteOperandUses(State &S, BBState &BBS, Instruction &UI) { for (Use &U : UI.operands()) { - NoteUse(S, BBS, U); + NoteUse(S, BBS, U, *UI.getFunction()); } } @@ -1377,7 +1384,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { unsigned nIncoming = Phi->getNumIncomingValues(); for (unsigned i = 0; i < nIncoming; ++i) { BBState &IncomingBBS = S.BBStates[Phi->getIncomingBlock(i)]; - NoteUse(S, IncomingBBS, Phi->getIncomingValue(i), IncomingBBS.PhiOuts); + NoteUse(S, IncomingBBS, Phi->getIncomingValue(i), IncomingBBS.PhiOuts, F); } } else if (tracked.count) { // We need to insert extra phis for the GC roots @@ -1403,7 +1410,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { } else if (auto *AI = dyn_cast(&I)) { Type *ElT = AI->getAllocatedType(); if (AI->isStaticAlloca() && isa(ElT) && ElT->getPointerAddressSpace() == AddressSpace::Tracked) { - S.Allocas.push_back(AI); + S.ArrayAllocas[AI] = cast(AI->getArraySize())->getZExtValue(); } } } @@ -1494,18 +1501,17 @@ SmallVector ExtractTrackedValues(Value *Src, Type *STy, bool isptr, I return Ptrs; } -unsigned TrackWithShadow(Value *Src, Type *STy, bool isptr, Value *Dst, IRBuilder<> &irbuilder) { - auto Ptrs = ExtractTrackedValues(Src, STy, isptr, irbuilder); - for (unsigned i = 0; i < Ptrs.size(); ++i) { - Value *Elem = Ptrs[i]; - Value *Slot = irbuilder.CreateConstInBoundsGEP1_32(irbuilder.getInt8Ty(), Dst, i * sizeof(void*)); - StoreInst *shadowStore = irbuilder.CreateAlignedStore(Elem, Slot, Align(sizeof(void*))); - shadowStore->setOrdering(AtomicOrdering::NotAtomic); - // TODO: shadowStore->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe); - } - return Ptrs.size(); -} - +//static unsigned TrackWithShadow(Value *Src, Type *STy, bool isptr, Value *Dst, IRBuilder<> &irbuilder) { +// auto Ptrs = ExtractTrackedValues(Src, STy, isptr, irbuilder); +// for (unsigned i = 0; i < Ptrs.size(); ++i) { +// Value *Elem = Ptrs[i]; +// Value *Slot = irbuilder.CreateConstInBoundsGEP1_32(irbuilder.getInt8Ty(), Dst, i * sizeof(void*)); +// StoreInst *shadowStore = irbuilder.CreateAlignedStore(Elem, Slot, Align(sizeof(void*))); +// shadowStore->setOrdering(AtomicOrdering::NotAtomic); +// // TODO: shadowStore->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe); +// } +// return Ptrs.size(); +//} // turn a memcpy into a set of loads void LateLowerGCFrame::MaybeTrackDst(State &S, MemTransferInst *MI) { @@ -2321,7 +2327,7 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St MaxColor = C; // Insert instructions for the actual gc frame - if (MaxColor != -1 || !S.Allocas.empty() || !S.ArrayAllocas.empty() || !S.TrackedStores.empty()) { + if (MaxColor != -1 || !S.ArrayAllocas.empty() || !S.TrackedStores.empty()) { // Create and push a GC frame. auto gcframe = CallInst::Create( getOrDeclare(jl_intrinsics::newGCFrame), @@ -2334,6 +2340,43 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St {gcframe, ConstantInt::get(T_int32, 0)}); pushGcframe->insertAfter(pgcstack); + // we don't run memsetopt after this, so run a basic approximation of it + // that removes any redundant memset calls in the prologue since getGCFrameSlot already includes the null store + Instruction *toerase = nullptr; + for (auto &I : F->getEntryBlock()) { + if (toerase) + toerase->eraseFromParent(); + toerase = nullptr; + Value *ptr; + Value *value; + bool isvolatile; + if (auto *SI = dyn_cast(&I)) { + ptr = SI->getPointerOperand(); + value = SI->getValueOperand(); + isvolatile = SI->isVolatile(); + } + else if (auto *MSI = dyn_cast(&I)) { + ptr = MSI->getDest(); + value = MSI->getValue(); + isvolatile = MSI->isVolatile(); + } + else { + continue; + } + ptr = ptr->stripInBoundsOffsets(); + AllocaInst *AI = dyn_cast(ptr); + if (isa(ptr)) + break; + if (!S.ArrayAllocas.count(AI)) + continue; + if (isvolatile || !isa(value) || !cast(value)->isNullValue()) + break; // stop once we reach a pointer operation that couldn't be analyzed or isn't a null store + toerase = &I; + } + if (toerase) + toerase->eraseFromParent(); + toerase = nullptr; + // Replace Allocas unsigned AllocaSlot = 2; // first two words are metadata auto replace_alloca = [this, gcframe, &AllocaSlot, T_int32](AllocaInst *&AI) { @@ -2367,11 +2410,6 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St AI->eraseFromParent(); AI = NULL; }; - for (AllocaInst *AI : S.Allocas) { - auto ns = cast(AI->getArraySize())->getZExtValue(); - replace_alloca(AI); - AllocaSlot += ns; - } for (auto AI : S.ArrayAllocas) { replace_alloca(AI.first); AllocaSlot += AI.second; diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 07308713bb789..26ae965b35319 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -501,10 +501,9 @@ function f37262(x) end end @testset "#37262" begin - str = "store volatile { i8, {}*, {}*, {}*, {}* } zeroinitializer, { i8, {}*, {}*, {}*, {}* }* %phic" - str_opaque = "store volatile { i8, ptr, ptr, ptr, ptr } zeroinitializer, ptr %phic" + str_opaque = "getelementptr inbounds i8, ptr %.roots.phic, i32 8\n store volatile ptr null" llvmstr = get_llvm(f37262, (Bool,), false, false, false) - @test (contains(llvmstr, str) || contains(llvmstr, str_opaque)) || llvmstr + @test contains(llvmstr, str_opaque) @test f37262(Base.inferencebarrier(true)) === nothing end From c601b113be8f81a00711455349a3dfab7755de9d Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Wed, 25 Sep 2024 05:26:48 -0700 Subject: [PATCH 259/548] Update TaskLocalRNG docstring according to #49110 (#55863) Since #49110, which is included in 1.10 and 1.11, spawning a task no longer advances the parent task's RNG state, so this statement in the docs was incorrect. --- stdlib/Random/src/Xoshiro.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index 1909effbbc9e6..09a3e386e9a2b 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -185,8 +185,8 @@ end TaskLocalRNG The `TaskLocalRNG` has state that is local to its task, not its thread. -It is seeded upon task creation, from the state of its parent task. -Therefore, task creation is an event that changes the parent's RNG state. +It is seeded upon task creation, from the state of its parent task, but without +advancing the state of the parent's RNG. As an upside, the `TaskLocalRNG` is pretty fast, and permits reproducible multithreaded simulations (barring race conditions), independent of scheduler @@ -203,6 +203,9 @@ may be any integer. !!! compat "Julia 1.11" Seeding `TaskLocalRNG()` with a negative integer seed requires at least Julia 1.11. + +!!! compat "Julia 1.10" + Task creation no longer advances the parent task's RNG state as of Julia 1.10. """ struct TaskLocalRNG <: AbstractRNG end TaskLocalRNG(::Nothing) = TaskLocalRNG() From 6e5e87b2cafda840b90347c2e74202d2608d7c29 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 25 Sep 2024 09:40:06 -0300 Subject: [PATCH 260/548] Root globals in toplevel exprs (#54433) This fixes #54422, the code here assumes that top level exprs are always rooted, but I don't see that referenced anywhere else, or guaranteed, so conservatively always root objects that show up in code. --- src/codegen.cpp | 5 +++-- test/gc.jl | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c719f4ff54078..abb21fcbca27e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6473,8 +6473,9 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_value_t *val = expr; if (jl_is_quotenode(expr)) val = jl_fieldref_noalloc(expr, 0); - if (jl_is_method(ctx.linfo->def.method)) // toplevel exprs are already rooted - val = jl_ensure_rooted(ctx, val); + // Toplevel exprs are rooted but because codegen assumes this is constant, it removes the write barriers for this code. + // This means we have to globally root the value here. (The other option would be to change how we optimize toplevel code) + val = jl_ensure_rooted(ctx, val); return mark_julia_const(ctx, val); } diff --git a/test/gc.jl b/test/gc.jl index f924f4952cfb0..e46ff0ed73fd9 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -72,3 +72,17 @@ end @testset "Base.GC docstrings" begin @test isempty(Docs.undocumented_names(GC)) end + +#testset doesn't work here because this needs to run in top level +#Check that we ensure objects in toplevel exprs are rooted +global dims54422 = [] # allocate the Binding +GC.gc(); GC.gc(); # force the binding to be old +GC.enable(false); # prevent new objects from being old +@eval begin + Base.Experimental.@force_compile # use the compiler + dims54422 = $([]) + nothing +end +GC.enable(true); GC.gc(false) # incremental collection +@test typeof(dims54422) == Vector{Any} +@test isempty(dims54422) From 7a76e32c0e28133c3e229df7009c1eb7a6cc86d5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 25 Sep 2024 19:35:03 -0400 Subject: [PATCH 261/548] codegen: fix alignment typos (#55880) So easy to type jl_datatype_align to get the natural alignment instead of julia_alignment to get the actual alignment. This should fix the Revise workload. Change is visible with ``` julia> code_llvm(Random.XoshiroSimd.forkRand, (Random.TaskLocalRNG, Base.Val{8})) ``` --- src/cgutils.cpp | 2 ++ src/codegen.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 9124638ce7446..7f96bb1047abc 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3637,6 +3637,8 @@ static void union_alloca_type(jl_uniontype_t *ut, }, (jl_value_t*)ut, counter); + if (align > JL_HEAP_ALIGNMENT) + align = JL_HEAP_ALIGNMENT; } static AllocaInst *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, bool &allunbox, size_t &min_align, size_t &nbytes) diff --git a/src/codegen.cpp b/src/codegen.cpp index abb21fcbca27e..a452e0fccd0c5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5151,7 +5151,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos Value *val = arg.V; SmallVector roots(arg.inline_roots); if (roots.empty()) - std::tie(val, roots) = split_value(ctx, arg, Align(jl_datatype_align(jt))); + std::tie(val, roots) = split_value(ctx, arg, Align(julia_alignment(jt))); AllocaInst *proots = emit_static_roots(ctx, roots.size()); for (size_t i = 0; i < roots.size(); i++) ctx.builder.CreateAlignedStore(roots[i], emit_ptrgep(ctx, proots, i * sizeof(void*)), Align(sizeof(void*))); @@ -7859,7 +7859,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value } props.cc = jl_returninfo_t::SRet; props.union_bytes = jl_datatype_size(jlrettype); - props.union_align = props.union_minalign = jl_datatype_align(jlrettype); + props.union_align = props.union_minalign = julia_alignment(jlrettype); // sret is always passed from alloca assert(M); fsig.push_back(rt->getPointerTo(M->getDataLayout().getAllocaAddrSpace())); From e4b29f71e7ca0e033ff3510b06d7534e4045e068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:34:03 +0100 Subject: [PATCH 262/548] Fix some corner cases of `isapprox` with unsigned integers (#55828) --- base/floatfuncs.jl | 4 +++- test/floatfuncs.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 67e7899b4107c..2c26f7cff1133 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -232,7 +232,9 @@ function isapprox(x::Integer, y::Integer; if norm === abs && atol < 1 && rtol == 0 return x == y else - return norm(x - y) <= max(atol, rtol*max(norm(x), norm(y))) + # We need to take the difference `max` - `min` when comparing unsigned integers. + _x, _y = x < y ? (x, y) : (y, x) + return norm(_y - _x) <= max(atol, rtol*max(norm(_x), norm(_y))) end end diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index f33ec75b58322..d5d697634bcfa 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -257,6 +257,35 @@ end end end +@testset "isapprox and unsigned integers" begin + for T in Base.BitUnsigned_types + # Test also combinations of different integer types + W = widen(T) + # The order of the operands for difference between unsigned integers is + # very important, test both combinations. + @test isapprox(T(42), T(42); rtol=T(0), atol=0.5) + @test isapprox(T(42), W(42); rtol=T(0), atol=0.5) + @test !isapprox(T(0), T(1); rtol=T(0), atol=0.5) + @test !isapprox(T(1), T(0); rtol=T(0), atol=0.5) + @test isapprox(T(1), T(3); atol=T(2)) + @test isapprox(T(4), T(2); atol=T(2)) + @test isapprox(T(1), W(3); atol=T(2)) + @test isapprox(T(4), W(2); atol=T(2)) + @test isapprox(T(5), T(7); atol=typemax(T)) + @test isapprox(T(8), T(6); atol=typemax(T)) + @test isapprox(T(1), T(2); rtol=1) + @test isapprox(T(6), T(3); rtol=1) + @test isapprox(T(1), W(2); rtol=1) + @test isapprox(T(6), W(3); rtol=1) + @test !isapprox(typemin(T), typemax(T)) + @test !isapprox(typemax(T), typemin(T)) + @test !isapprox(typemin(T), typemax(T); atol=typemax(T)-T(1)) + @test !isapprox(typemax(T), typemin(T); atol=typemax(T)-T(1)) + @test isapprox(typemin(T), typemax(T); atol=typemax(T)) + @test isapprox(typemax(T), typemin(T); atol=typemax(T)) + end +end + @testset "Conversion from floating point to unsigned integer near extremes (#51063)" begin @test_throws InexactError UInt32(4.2949673f9) @test_throws InexactError UInt64(1.8446744f19) From a5178a7c71d2253dd4b714dd2257f6d721e08534 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:19:51 -0400 Subject: [PATCH 263/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20ef9f76c17=20to=2051d4910c1=20(#55896)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 | 1 + .../Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 | 1 + .../Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 | 1 - .../Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 create mode 100644 deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 diff --git a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 new file mode 100644 index 0000000000000..b5b82565470c0 --- /dev/null +++ b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 @@ -0,0 +1 @@ +88b8a25a8d465ac8cc94d13bc5f51707 diff --git a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 new file mode 100644 index 0000000000000..a746b269d91f0 --- /dev/null +++ b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 @@ -0,0 +1 @@ +22262687f3bf75292ab0170e19a9c4a494022a653b2811443b8c52bc099dee0fddd09f6632ae42b3193adf3b0693ddcb6679b5d91e50a500f65261df5b7ced7d diff --git a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 deleted file mode 100644 index 39dbb56dbaf53..0000000000000 --- a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -080b5cb82d208245cba014f1dfcb8033 diff --git a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 b/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 deleted file mode 100644 index 2f95d4a0e28da..0000000000000 --- a/deps/checksums/Pkg-ef9f76c175872bab6803da4a5fa3fd99bce3d03a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1b91505c78d2608afa89ceea16f645bb41c0737815aec1853ad72c9751e7299b264135c9a40a6319f68b973073a151619b925d7a9655c46526bccf501b116113 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index f5ca169a775c6..34233c58702b4 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = ef9f76c175872bab6803da4a5fa3fd99bce3d03a +PKG_SHA1 = 51d4910c114a863d888659cb8962c1e161b2a421 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 32b9e1ac9fa31019aa3779b3c401a80bc94cb61f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 26 Sep 2024 21:06:46 -0400 Subject: [PATCH 264/548] Profile: fix order of fields in heapsnapshot & improve formatting (#55890) --- src/gc-heap-snapshot.cpp | 42 ++++++++----------- stdlib/Profile/src/heapsnapshot_reassemble.jl | 10 ++++- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index fcda11dad4f8a..72eb17115f4c7 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -618,38 +618,32 @@ void final_serialize_heap_snapshot(ios_t *json, ios_t *strings, HeapSnapshot &sn { // mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L2567-L2567 // also https://github.com/microsoft/vscode-v8-heap-tools/blob/c5b34396392397925ecbb4ecb904a27a2754f2c1/v8-heap-parser/src/decoder.rs#L43-L51 - ios_printf(json, "{\"snapshot\":{"); + ios_printf(json, "{\"snapshot\":{\n"); - ios_printf(json, "\"meta\":{"); - ios_printf(json, "\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",\"detachedness\"],"); - ios_printf(json, "\"node_types\":["); + ios_printf(json, " \"meta\":{\n"); + ios_printf(json, " \"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",\"detachedness\"],\n"); + ios_printf(json, " \"node_types\":["); snapshot.node_types.print_json_array(json, false); ios_printf(json, ","); - ios_printf(json, "\"string\", \"number\", \"number\", \"number\", \"number\", \"number\"],"); - ios_printf(json, "\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],"); - ios_printf(json, "\"edge_types\":["); + ios_printf(json, "\"string\", \"number\", \"number\", \"number\", \"number\", \"number\"],\n"); + ios_printf(json, " \"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); + ios_printf(json, " \"edge_types\":["); snapshot.edge_types.print_json_array(json, false); ios_printf(json, ","); - ios_printf(json, "\"string_or_number\",\"from_node\"],"); + ios_printf(json, "\"string_or_number\",\"from_node\"],\n"); // not used. Required by microsoft/vscode-v8-heap-tools - ios_printf(json, "\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",\"line\",\"column\"],"); - ios_printf(json, "\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],"); - ios_printf(json, "\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],"); - ios_printf(json, "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]"); + ios_printf(json, " \"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",\"line\",\"column\"],\n"); + ios_printf(json, " \"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n"); + ios_printf(json, " \"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); + ios_printf(json, " \"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]\n"); // end not used - ios_printf(json, "},\n"); // end "meta" + ios_printf(json, " },\n"); // end "meta" - ios_printf(json, "\"node_count\":%zu,", snapshot.num_nodes); - ios_printf(json, "\"edge_count\":%zu,", snapshot.num_edges); - ios_printf(json, "\"trace_function_count\":0"); // not used. Required by microsoft/vscode-v8-heap-tools - ios_printf(json, "},\n"); // end "snapshot" - - // not used. Required by microsoft/vscode-v8-heap-tools - ios_printf(json, "\"trace_function_infos\":[],"); - ios_printf(json, "\"trace_tree\":[],"); - ios_printf(json, "\"samples\":[],"); - ios_printf(json, "\"locations\":[]"); - // end not used + ios_printf(json, " \"node_count\":%zu,\n", snapshot.num_nodes); + ios_printf(json, " \"edge_count\":%zu,\n", snapshot.num_edges); + ios_printf(json, " \"trace_function_count\":0\n"); // not used. Required by microsoft/vscode-v8-heap-tools + ios_printf(json, "}\n"); // end "snapshot" + // this } is removed by the julia reassembler in Profile ios_printf(json, "}"); } diff --git a/stdlib/Profile/src/heapsnapshot_reassemble.jl b/stdlib/Profile/src/heapsnapshot_reassemble.jl index 2413ae538b8ac..b2d86ee1f27b6 100644 --- a/stdlib/Profile/src/heapsnapshot_reassemble.jl +++ b/stdlib/Profile/src/heapsnapshot_reassemble.jl @@ -155,7 +155,8 @@ function assemble_snapshot(in_prefix, io::IO) _write_decimal_number(io, nodes.edge_count[i], _digits_buf) print(io, ",0,0") end - print(io, "],\"edges\":[") + print(io, "],\n") + print(io, "\"edges\":[") e = 1 for n in 1:length(nodes) count = nodes.edge_count[n] @@ -177,6 +178,13 @@ function assemble_snapshot(in_prefix, io::IO) end println(io, "],") + # not used. Required by microsoft/vscode-v8-heap-tools + # This order of these fields is required by chrome dev tools otherwise loading fails + println(io, "\"trace_function_infos\":[],") + println(io, "\"trace_tree\":[],") + println(io, "\"samples\":[],") + println(io, "\"locations\":[],") + println(io, "\"strings\":[") open(string(in_prefix, ".strings"), "r") do strings_io first = true From 60be4094fb4eb4d4e4780b920a96e027522cd692 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 26 Sep 2024 21:07:47 -0400 Subject: [PATCH 265/548] Profile: Improve generation of clickable terminal links (#55857) --- base/path.jl | 13 ++++----- stdlib/Profile/src/Profile.jl | 53 +++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/base/path.jl b/base/path.jl index f6d3266d9738c..69c8d22c63c54 100644 --- a/base/path.jl +++ b/base/path.jl @@ -614,6 +614,11 @@ for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath) @eval $f(path::AbstractString) = $f(String(path)) end +# RFC3986 Section 2.1 +percent_escape(s) = '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%') +# RFC3986 Section 2.3 +encode_uri_component(s) = replace(s, r"[^A-Za-z0-9\-_.~/]+" => percent_escape) + """ uripath(path::AbstractString) @@ -636,10 +641,6 @@ function uripath end @static if Sys.iswindows() function uripath(path::String) - percent_escape(s) = # RFC3986 Section 2.1 - '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%') - encode_uri_component(s) = # RFC3986 Section 2.3 - replace(s, r"[^A-Za-z0-9\-_.~/]+" => percent_escape) path = abspath(path) if startswith(path, "\\\\") # UNC path, RFC8089 Appendix E.3 unixpath = join(eachsplit(path, path_separator_re, keepempty=false), '/') @@ -653,10 +654,6 @@ function uripath end end else function uripath(path::String) - percent_escape(s) = # RFC3986 Section 2.1 - '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%') - encode_uri_component(s) = # RFC3986 Section 2.3 - replace(s, r"[^A-Za-z0-9\-_.~/]+" => percent_escape) localpath = join(eachsplit(abspath(path), path_separator_re, keepempty=false), '/') host = if ispath("/proc/sys/fs/binfmt_misc/WSLInterop") # WSL sigil distro = get(ENV, "WSL_DISTRO_NAME", "") # See diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index c7ef1efb35945..b753c9ca88f24 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -7,7 +7,7 @@ Profiling support. ## CPU profiling - `@profile foo()` to profile a specific call. -- `Profile.print()` to print the report. +- `Profile.print()` to print the report. Paths are clickable links in supported terminals and specialized for JULIA_EDITOR etc. - `Profile.clear()` to clear the buffer. - Send a $(Sys.isbsd() ? "SIGINFO (ctrl-t)" : "SIGUSR1") signal to the process to automatically trigger a profile and print. @@ -198,7 +198,9 @@ const META_OFFSET_THREADID = 5 Prints profiling results to `io` (by default, `stdout`). If you do not supply a `data` vector, the internal buffer of accumulated backtraces -will be used. +will be used. Paths are clickable links in supported terminals and +specialized for [`JULIA_EDITOR`](@ref) with line numbers, or just file +links if no editor is set. The keyword arguments can be any combination of: @@ -807,26 +809,35 @@ end # make a terminal-clickable link to the file and linenum. # Similar to `define_default_editors` in `Base.Filesystem` but for creating URIs not commands function editor_link(path::String, linenum::Int) - editor = get(ENV, "JULIA_EDITOR", "") - - if editor == "code" - return "vscode://file/$path:$linenum" - elseif editor == "subl" || editor == "sublime_text" - return "subl://$path:$linenum" - elseif editor == "idea" || occursin("idea", editor) - return "idea://open?file=$path&line=$linenum" - elseif editor == "pycharm" - return "pycharm://open?file=$path&line=$linenum" - elseif editor == "atom" - return "atom://core/open/file?filename=$path&line=$linenum" - elseif editor == "emacsclient" - return "emacs://open?file=$path&line=$linenum" - elseif editor == "vim" || editor == "nvim" - return "vim://open?file=$path&line=$linenum" - else - # TODO: convert the path to a generic URI (line numbers are not supported by generic URI) - return path + # Note: the editor path can include spaces (if escaped) and flags. + editor = nothing + for var in ["JULIA_EDITOR", "VISUAL", "EDITOR"] + str = get(ENV, var, nothing) + str isa String || continue + editor = str + break + end + path_encoded = Base.Filesystem.encode_uri_component(path) + if editor !== nothing + if editor == "code" + return "vscode://file/$path_encoded:$linenum" + elseif editor == "subl" || editor == "sublime_text" + return "subl://open?url=file://$path_encoded&line=$linenum" + elseif editor == "idea" || occursin("idea", editor) + return "idea://open?file=$path_encoded&line=$linenum" + elseif editor == "pycharm" + return "pycharm://open?file=$path_encoded&line=$linenum" + elseif editor == "atom" + return "atom://core/open/file?filename=$path_encoded&line=$linenum" + elseif editor == "emacsclient" || editor == "emacs" + return "emacs://open?file=$path_encoded&line=$linenum" + elseif editor == "vim" || editor == "nvim" + # Note: Vim/Nvim may not support standard URI schemes without specific plugins + return "vim://open?file=$path_encoded&line=$linenum" + end end + # fallback to generic URI, but line numbers are not supported by generic URI + return Base.Filesystem.uripath(path) end function print_flat(io::IO, lilist::Vector{StackFrame}, From 4b27a169bda6ac970fc677962c30af51a6a9ca74 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:34:58 +0900 Subject: [PATCH 266/548] inference: add missing `TypeVar` handling for `instanceof_tfunc` (#55884) I thought these sort of problems had been addressed by d60f92c, but it seems some were missed. Specifically, `t.a` and `t.b` from `t::Union` could be `TypeVar`, and if they are passed to a subroutine or recursed without being unwrapped or rewrapped, errors like JuliaLang/julia#55882 could occur. This commit resolves the issue by calling `unwraptv` in the `Union` handling within `instanceof_tfunc`. I also found a similar issue inside `nfields_tfunc`, so that has also been fixed, and test cases have been added. While I haven't been able to make up a test case specifically for the fix in `instanceof_tfunc`, I have confirmed that this commit certainly fixes the issue reported in JuliaLang/julia#55882. - fixes JuliaLang/julia#55882 --- base/compiler/tfuncs.jl | 8 ++++---- test/compiler/inference.jl | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ab3b50763deec..cc8ba227bd088 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -135,8 +135,8 @@ function instanceof_tfunc(@nospecialize(t), astag::Bool=false, @nospecialize(tro end return tr, isexact, isconcrete, istype elseif isa(t, Union) - ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(t.a, astag, troot) - tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(t.b, astag, troot) + ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(unwraptv(t.a), astag, troot) + tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(unwraptv(t.b), astag, troot) isconcrete = isconcrete_a && isconcrete_b istype = istype_a && istype_b # most users already handle the Union case, so here we assume that @@ -563,9 +563,9 @@ add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 1) end end if isa(x, Union) - na = nfields_tfunc(𝕃, x.a) + na = nfields_tfunc(𝕃, unwraptv(x.a)) na === Int && return Int - return tmerge(na, nfields_tfunc(𝕃, x.b)) + return tmerge(𝕃, na, nfields_tfunc(𝕃, unwraptv(x.b))) end return Int end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d1382d3c84b82..46009e0790942 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6152,3 +6152,6 @@ end t155751 = Union{AbstractArray{UInt8, 4}, Array{Float32, 4}, Grid55751{Float32, 3, _A} where _A} t255751 = Array{Float32, 3} @test Core.Compiler.tmerge_types_slow(t155751,t255751) == AbstractArray # shouldn't hang + +issue55882_nfields(x::Union{T,Nothing}) where T<:Number = nfields(x) +@test Base.infer_return_type(issue55882_nfields) <: Int From 0dbb6eb679c1c124c212ae9ce399004873041cf1 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Fri, 27 Sep 2024 10:29:32 +0200 Subject: [PATCH 267/548] Install terminfo data under /usr/share/julia (#55881) Just like all other libraries, we don't want internal Julia files to mess with system files. Introduced by https://github.com/JuliaLang/julia/pull/55411. --- Makefile | 2 +- base/terminfo.jl | 2 +- deps/terminfo.mk | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 735d342a79eb5..e440f243d876e 100644 --- a/Makefile +++ b/Makefile @@ -410,7 +410,7 @@ endif $(INSTALL_F) $(JULIAHOME)/contrib/julia.appdata.xml $(DESTDIR)$(datarootdir)/metainfo/ # Install terminal info database ifneq ($(WITH_TERMINFO),0) - cp -R -L $(build_datarootdir)/terminfo $(DESTDIR)$(datarootdir) + cp -R -L $(build_datarootdir)/julia/terminfo $(DESTDIR)$(datarootdir)/julia/ endif # Update RPATH entries and JL_SYSTEM_IMAGE_PATH if $(private_libdir_rel) != $(build_private_libdir_rel) diff --git a/base/terminfo.jl b/base/terminfo.jl index 79713f4a86aa3..8ea8387077d36 100644 --- a/base/terminfo.jl +++ b/base/terminfo.jl @@ -262,7 +262,7 @@ function find_terminfo_file(term::String) append!(terminfo_dirs, replace(split(ENV["TERMINFO_DIRS"], ':'), "" => "/usr/share/terminfo")) - push!(terminfo_dirs, normpath(Sys.BINDIR, DATAROOTDIR, "terminfo")) + push!(terminfo_dirs, normpath(Sys.BINDIR, DATAROOTDIR, "julia", "terminfo")) Sys.isunix() && push!(terminfo_dirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") for dir in terminfo_dirs diff --git a/deps/terminfo.mk b/deps/terminfo.mk index 63194f786f566..60865838a813e 100644 --- a/deps/terminfo.mk +++ b/deps/terminfo.mk @@ -22,8 +22,8 @@ $(BUILDDIR)/TermInfoDB-v$(TERMINFO_VER)/build-checked: $(BUILDDIR)/TermInfoDB-v$ echo 1 > $@ define TERMINFO_INSTALL - mkdir -p $2/$$(build_datarootdir) - cp -R $1/terminfo $2/$$(build_datarootdir) + mkdir -p $2/$$(build_datarootdir)/julia + cp -R $1/terminfo $2/$$(build_datarootdir)/julia/ endef $(eval $(call staged-install, \ terminfo,TermInfoDB-v$(TERMINFO_VER), \ From 6e33dfb202e5a0adce02fd29220f6314101edc1c Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:37:07 -0300 Subject: [PATCH 268/548] expose metric to report reasons why full GCs were triggered (#55826) Additional GC observability tool. This will help us to diagnose why some of our servers are triggering so many full GCs in certain circumstances. --- base/timing.jl | 27 +++++++++++++++++++++++++++ src/gc-stock.c | 15 +++++++++++++-- src/gc-stock.h | 14 ++++++++++++++ test/gc.jl | 11 +++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 80ebb74abee26..6d97d70d2f04c 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -104,6 +104,33 @@ function gc_page_utilization_data() return Base.unsafe_wrap(Array, page_utilization_raw, JL_GC_N_MAX_POOLS, own=false) end +# must be kept in sync with `src/gc-stock.h`` +const FULL_SWEEP_REASONS = [:FULL_SWEEP_REASON_SWEEP_ALWAYS_FULL, :FULL_SWEEP_REASON_FORCED_FULL_SWEEP, + :FULL_SWEEP_REASON_USER_MAX_EXCEEDED, :FULL_SWEEP_REASON_LARGE_PROMOTION_RATE] + +""" + Base.full_sweep_reasons() + +Return a dictionary of the number of times each full sweep reason has occurred. + +The reasons are: +- `:FULL_SWEEP_REASON_SWEEP_ALWAYS_FULL`: Full sweep was caused due to `always_full` being set in the GC debug environment +- `:FULL_SWEEP_REASON_FORCED_FULL_SWEEP`: Full sweep was forced by `GC.gc(true)` +- `:FULL_SWEEP_REASON_USER_MAX_EXCEEDED`: Full sweep was forced due to the system reaching the heap soft size limit +- `:FULL_SWEEP_REASON_LARGE_PROMOTION_RATE`: Full sweep was forced by a large promotion rate across GC generations + +Note that the set of reasons is not guaranteed to be stable across minor versions of Julia. +""" +function full_sweep_reasons() + reason = cglobal(:jl_full_sweep_reasons, UInt64) + reasons_as_array = Base.unsafe_wrap(Vector{UInt64}, reason, length(FULL_SWEEP_REASONS), own=false) + d = Dict{Symbol, Int64}() + for (i, r) in enumerate(FULL_SWEEP_REASONS) + d[r] = reasons_as_array[i] + end + return d +end + """ Base.jit_total_bytes() diff --git a/src/gc-stock.c b/src/gc-stock.c index d25f8917f302d..6b97881909bbd 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -40,6 +40,8 @@ uv_sem_t gc_sweep_assists_needed; uv_mutex_t gc_queue_observer_lock; // Tag for sentinel nodes in bigval list uintptr_t gc_bigval_sentinel_tag; +// Table recording number of full GCs due to each reason +JL_DLLEXPORT uint64_t jl_full_sweep_reasons[FULL_SWEEP_NUM_REASONS]; // Flag that tells us whether we need to support conservative marking // of objects. @@ -3043,10 +3045,12 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // we either free some space or get an OOM error. if (gc_sweep_always_full) { sweep_full = 1; + gc_count_full_sweep_reason(FULL_SWEEP_REASON_SWEEP_ALWAYS_FULL); } if (collection == JL_GC_FULL && !prev_sweep_full) { sweep_full = 1; recollect = 1; + gc_count_full_sweep_reason(FULL_SWEEP_REASON_FORCED_FULL_SWEEP); } if (sweep_full) { // these are the difference between the number of gc-perm bytes scanned @@ -3182,10 +3186,17 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) } double old_ratio = (double)promoted_bytes/(double)heap_size; - if (heap_size > user_max || old_ratio > 0.15) + if (heap_size > user_max) { next_sweep_full = 1; - else + gc_count_full_sweep_reason(FULL_SWEEP_REASON_USER_MAX_EXCEEDED); + } + else if (old_ratio > 0.15) { + next_sweep_full = 1; + gc_count_full_sweep_reason(FULL_SWEEP_REASON_LARGE_PROMOTION_RATE); + } + else { next_sweep_full = 0; + } if (heap_size > user_max || thrashing) under_pressure = 1; // sweeping is over diff --git a/src/gc-stock.h b/src/gc-stock.h index 45c93bf4289ae..46f7d3e11e105 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -505,6 +505,20 @@ FORCE_INLINE void gc_big_object_link(bigval_t *sentinel_node, bigval_t *node) JL sentinel_node->next = node; } +// Must be kept in sync with `base/timing.jl` +#define FULL_SWEEP_REASON_SWEEP_ALWAYS_FULL (0) +#define FULL_SWEEP_REASON_FORCED_FULL_SWEEP (1) +#define FULL_SWEEP_REASON_USER_MAX_EXCEEDED (2) +#define FULL_SWEEP_REASON_LARGE_PROMOTION_RATE (3) +#define FULL_SWEEP_NUM_REASONS (4) + +extern JL_DLLEXPORT uint64_t jl_full_sweep_reasons[FULL_SWEEP_NUM_REASONS]; +STATIC_INLINE void gc_count_full_sweep_reason(int reason) JL_NOTSAFEPOINT +{ + assert(reason >= 0 && reason < FULL_SWEEP_NUM_REASONS); + jl_full_sweep_reasons[reason]++; +} + extern uv_mutex_t gc_perm_lock; extern uv_mutex_t gc_threads_lock; extern uv_cond_t gc_threads_cond; diff --git a/test/gc.jl b/test/gc.jl index e46ff0ed73fd9..c532f17f04eb5 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -49,6 +49,13 @@ function issue_54275_test() @test !live_bytes_has_grown_too_much end +function full_sweep_reasons_test() + GC.gc() + reasons = Base.full_sweep_reasons() + @test reasons[:FULL_SWEEP_REASON_FORCED_FULL_SWEEP] >= 1 + @test keys(reasons) == Set(Base.FULL_SWEEP_REASONS) +end + # !!! note: # Since we run our tests on 32bit OS as well we confine ourselves # to parameters that allocate about 512MB of objects. Max RSS is lower @@ -73,6 +80,10 @@ end @test isempty(Docs.undocumented_names(GC)) end +@testset "Full GC reasons" begin + full_sweep_reasons_test() +end + #testset doesn't work here because this needs to run in top level #Check that we ensure objects in toplevel exprs are rooted global dims54422 = [] # allocate the Binding From 3aad027fc5631f2b5ca81e0133518f134b2b6c03 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 27 Sep 2024 10:15:24 -0400 Subject: [PATCH 269/548] Revert "Improve printing of several arguments" (#55894) Reverts JuliaLang/julia#55754 as it overrode some performance heuristics which appeared to be giving a significant gain/loss in performance: Closes https://github.com/JuliaLang/julia/issues/55893 --- base/strings/io.jl | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index c78e3e2e043b6..754e058cd2f54 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -42,7 +42,9 @@ end function print(io::IO, xs...) lock(io) try - foreach(Fix1(print, io), xs) + for x in xs + print(io, x) + end finally unlock(io) end @@ -136,9 +138,15 @@ function print_to_string(xs...) if isempty(xs) return "" end - siz = sum(_str_sizehint, xs; init = 0) + siz::Int = 0 + for x in xs + siz += _str_sizehint(x) + end + # specialized for performance reasons s = IOBuffer(sizehint=siz) - print(s, xs...) + for x in xs + print(s, x) + end String(_unsafe_take!(s)) end @@ -146,10 +154,16 @@ function string_with_env(env, xs...) if isempty(xs) return "" end - siz = sum(_str_sizehint, xs; init = 0) + siz::Int = 0 + for x in xs + siz += _str_sizehint(x) + end + # specialized for performance reasons s = IOBuffer(sizehint=siz) env_io = IOContext(s, env) - print(env_io, xs...) + for x in xs + print(env_io, x) + end String(_unsafe_take!(s)) end From 00f0a6c63c1e5ce996fba5ef187522f4990ee9b4 Mon Sep 17 00:00:00 2001 From: David Widmann Date: Sat, 28 Sep 2024 02:21:21 +0200 Subject: [PATCH 270/548] Do not trigger deprecation warnings in `Test.detect_ambiguities` and `Test.detect_unbound_args` (#55869) #55868 --- stdlib/Test/src/Test.jl | 4 ++-- test/ambiguous.jl | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index b4ada2ce3a9cf..46bc2d8790cec 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -2087,7 +2087,7 @@ function detect_ambiguities(mods::Module...; while !isempty(work) mod = pop!(work) for n in names(mod, all = true) - Base.isdeprecated(mod, n) && continue + (!Base.isbindingresolved(mod, n) || Base.isdeprecated(mod, n)) && continue if !isdefined(mod, n) if is_in_mods(mod, recursive, mods) if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds @@ -2158,7 +2158,7 @@ function detect_unbound_args(mods...; while !isempty(work) mod = pop!(work) for n in names(mod, all = true) - Base.isdeprecated(mod, n) && continue + (!Base.isbindingresolved(mod, n) || Base.isdeprecated(mod, n)) && continue if !isdefined(mod, n) if is_in_mods(mod, recursive, mods) if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds diff --git a/test/ambiguous.jl b/test/ambiguous.jl index acdfdc70ba30c..2f8a4193cf592 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -162,6 +162,22 @@ end ambs = detect_ambiguities(Ambig48312) @test length(ambs) == 4 +module UnboundAmbig55868 + module B + struct C end + export C + Base.@deprecate_binding D C + end + using .B + export C, D +end +@test !Base.isbindingresolved(UnboundAmbig55868, :C) +@test !Base.isbindingresolved(UnboundAmbig55868, :D) +@test isempty(detect_unbound_args(UnboundAmbig55868)) +@test isempty(detect_ambiguities(UnboundAmbig55868)) +@test !Base.isbindingresolved(UnboundAmbig55868, :C) +@test !Base.isbindingresolved(UnboundAmbig55868, :D) + # Test that Core and Base are free of ambiguities # not using isempty so this prints more information when it fails @testset "detect_ambiguities" begin From 4a4ca9c815207a80ea81b884b196dfeafc3cb877 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 27 Sep 2024 21:49:29 -0400 Subject: [PATCH 271/548] do not intentionally suppress errors in precompile script from being reported or failing the result (#55909) I was slightly annoying that the build was set up to succeed if this step failed, so I removed the error suppression and fixed up the script slightly --- contrib/generate_precompile.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index d3e73a1b1865a..60f7290c7a0ac 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -347,8 +347,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe print_state("step1" => "F$n_step1") return :ok end - Base.errormonitor(step1) - !PARALLEL_PRECOMPILATION && wait(step1) + PARALLEL_PRECOMPILATION ? bind(statements_step1, step1) : wait(step1) # Create a staging area where all the loaded packages are available PrecompileStagingArea = Module() @@ -362,7 +361,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe # Make statements unique statements = Set{String}() # Execute the precompile statements - for sts in [statements_step1,], statement in sts + for statement in statements_step1 # Main should be completely clean occursin("Main.", statement) && continue Base.in!(statement, statements) && continue @@ -398,6 +397,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe println() # Seems like a reasonable number right now, adjust as needed # comment out if debugging script + have_repl = false n_succeeded > (have_repl ? 650 : 90) || @warn "Only $n_succeeded precompile statements" fetch(step1) == :ok || throw("Step 1 of collecting precompiles failed.") @@ -408,7 +408,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe finally fancyprint && print(ansi_enablecursor) GC.gc(true); GC.gc(false); # reduce memory footprint - return end generate_precompile_statements() From ff0a1befb9cff915abb06c551e1bed4ab7331790 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 28 Sep 2024 15:45:14 +0530 Subject: [PATCH 272/548] Remove eigvecs method for SymTridiagonal (#55903) The fallback method does the same, so this specialized method isn't necessary --- stdlib/LinearAlgebra/src/tridiag.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index e755ce63e9b2a..ca61eb8519d42 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -319,8 +319,6 @@ eigmax(A::SymTridiagonal) = eigvals(A, size(A, 1):size(A, 1))[1] eigmin(A::SymTridiagonal) = eigvals(A, 1:1)[1] #Compute selected eigenvectors only corresponding to particular eigenvalues -eigvecs(A::SymTridiagonal) = eigen(A).vectors - """ eigvecs(A::SymTridiagonal[, eigvals]) -> Matrix From 97ecdb8595c4a1fbe68ba6f39b3244e8cdabc2c6 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 28 Sep 2024 19:02:49 -0400 Subject: [PATCH 273/548] add --trim option for generating smaller binaries (#55047) This adds a command line option `--trim` that builds images where code is only included if it is statically reachable from methods marked using the new function `entrypoint`. Compile-time errors are given for call sites that are too dynamic to allow trimming the call graph (however there is an `unsafe` option if you want to try building anyway to see what happens). The PR has two other components. One is changes to Base that generally allow more code to be compiled in this mode. These changes will either be merged in separate PRs or moved to a separate part of the workflow (where we will build a custom system image for this purpose). The branch is set up this way to make it easy to check out and try the functionality. The other component is everything in the `juliac/` directory, which implements a compiler driver script based on this new option, along with some examples and tests. This will eventually become a package "app" that depends on PackageCompiler and provides a CLI for all of this stuff, so it will not be merged here. To try an example: ``` julia contrib/juliac.jl --output-exe hello --trim test/trimming/hello.jl ``` When stripped the resulting executable is currently about 900kb on my machine. Also includes a lot of work by @topolarity --------- Co-authored-by: Gabriel Baraldi Co-authored-by: Tim Holy Co-authored-by: Cody Tapscott --- Makefile | 4 +- NEWS.md | 2 + base/experimental.jl | 14 + base/libuv.jl | 5 +- base/options.jl | 1 + base/reflection.jl | 11 +- base/strings/io.jl | 4 + contrib/julia-config.jl | 2 +- contrib/juliac-buildscript.jl | 277 ++++++++++++++++++ contrib/juliac.jl | 110 +++++++ doc/src/devdocs/sysimg.md | 77 +++++ doc/src/manual/command-line-interface.md | 2 +- src/aotcompile.cpp | 69 ++++- src/cgutils.cpp | 12 + src/codegen-stubs.c | 1 + src/codegen.cpp | 355 +++++++++++++++++++++-- src/gf.c | 26 +- src/init.c | 11 +- src/jitlayers.h | 6 +- src/jl_exported_funcs.inc | 3 + src/jloptions.c | 26 +- src/jloptions.h | 1 + src/julia.expmap.in | 4 +- src/julia.h | 7 + src/julia_internal.h | 7 +- src/module.c | 2 +- src/precompile.c | 10 +- src/precompile_utils.c | 80 +++++ src/staticdata.c | 190 ++++++++++-- src/support/arraylist.h | 17 +- stdlib/LinearAlgebra/src/blas.jl | 2 +- stdlib/LinearAlgebra/src/lbt.jl | 4 +- test/Makefile | 8 +- test/trimming/Makefile | 55 ++++ test/trimming/hello.jl | 6 + test/trimming/init.c | 9 + test/trimming/trimming.jl | 7 + 37 files changed, 1338 insertions(+), 89 deletions(-) create mode 100644 contrib/juliac-buildscript.jl create mode 100644 contrib/juliac.jl create mode 100644 test/trimming/Makefile create mode 100644 test/trimming/hello.jl create mode 100644 test/trimming/init.c create mode 100644 test/trimming/trimming.jl diff --git a/Makefile b/Makefile index e440f243d876e..4fd8b878c5d1f 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ julia-deps: | $(DIRS) $(build_datarootdir)/julia/base $(build_datarootdir)/julia julia-stdlib: | $(DIRS) julia-deps @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/stdlib -julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl +julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl $(build_datarootdir)/julia/juliac.jl $(build_datarootdir)/julia/juliac-buildscript.jl @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/base julia-libccalltest: julia-deps @@ -181,7 +181,7 @@ $(build_sysconfdir)/julia/startup.jl: $(JULIAHOME)/etc/startup.jl | $(build_sysc @echo Creating usr/etc/julia/startup.jl @cp $< $@ -$(build_datarootdir)/julia/julia-config.jl: $(JULIAHOME)/contrib/julia-config.jl | $(build_datarootdir)/julia +$(build_datarootdir)/julia/%: $(JULIAHOME)/contrib/% | $(build_datarootdir)/julia $(INSTALL_M) $< $(dir $@) $(build_depsbindir)/stringreplace: $(JULIAHOME)/contrib/stringreplace.c | $(build_depsbindir) diff --git a/NEWS.md b/NEWS.md index 9ecdd87f0c2bb..ca2bf1f615012 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ Julia v1.12 Release Notes New language features --------------------- +- New option `--trim` for building "trimmed" binaries, where code not provably reachable from entry points + is removed. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]). - A new keyword argument `usings::Bool` has been added to `names`. By using this, we can now find all the names available in module `A` by `names(A; all=true, imported=true, usings=true)`. ([#54609]) - the `@atomic(...)` macro family supports now the reference assignment syntax, e.g. diff --git a/base/experimental.jl b/base/experimental.jl index 58c7258120f3f..6e757e9fa0e5f 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -457,4 +457,18 @@ without adding them to the global method table. """ :@MethodTable +""" + Base.Experimental.entrypoint(f, argtypes::Tuple) + +Mark a method for inclusion when the `--trim` option is specified. +""" +function entrypoint(@nospecialize(f), @nospecialize(argtypes::Tuple)) + entrypoint(Tuple{Core.Typeof(f), argtypes...}) +end + +function entrypoint(@nospecialize(argt::Type)) + ccall(:jl_add_entrypoint, Int32, (Any,), argt) + nothing +end + end diff --git a/base/libuv.jl b/base/libuv.jl index 143201598fde0..3c9f79dfa7b2c 100644 --- a/base/libuv.jl +++ b/base/libuv.jl @@ -133,7 +133,10 @@ function uv_return_spawn end function uv_asynccb end function uv_timercb end -function reinit_stdio() +reinit_stdio() = _reinit_stdio() +# we need this so it can be called by codegen to print errors, even after +# reinit_stdio has been redefined by the juliac build script. +function _reinit_stdio() global stdin = init_stdio(ccall(:jl_stdin_stream, Ptr{Cvoid}, ()))::IO global stdout = init_stdio(ccall(:jl_stdout_stream, Ptr{Cvoid}, ()))::IO global stderr = init_stdio(ccall(:jl_stderr_stream, Ptr{Cvoid}, ()))::IO diff --git a/base/options.jl b/base/options.jl index 41ce3c9e20909..1de7a2acb1e06 100644 --- a/base/options.jl +++ b/base/options.jl @@ -58,6 +58,7 @@ struct JLOptions permalloc_pkgimg::Int8 heap_size_hint::UInt64 trace_compile_timing::Int8 + trim::Int8 end # This runs early in the sysimage != is not defined yet diff --git a/base/reflection.jl b/base/reflection.jl index 5b395efc58190..fe48b6f9aa6b9 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1504,6 +1504,13 @@ struct CodegenParams """ use_jlplt::Cint + """ + If enabled, only provably reachable code (from functions marked with `entrypoint`) is included + in the output system image. Errors or warnings can be given for call sites too dynamic to handle. + The option is disabled by default. (0=>disabled, 1=>safe (static errors), 2=>unsafe, 3=>unsafe plus warnings) + """ + trim::Cint + """ A pointer of type @@ -1519,14 +1526,14 @@ struct CodegenParams prefer_specsig::Bool=false, gnu_pubnames::Bool=true, debug_info_kind::Cint = default_debug_info_kind(), debug_info_level::Cint = Cint(JLOptions().debug_level), safepoint_on_entry::Bool=true, - gcstack_arg::Bool=true, use_jlplt::Bool=true, + gcstack_arg::Bool=true, use_jlplt::Bool=true, trim::Cint=Cint(0), lookup::Ptr{Cvoid}=unsafe_load(cglobal(:jl_rettype_inferred_addr, Ptr{Cvoid}))) return new( Cint(track_allocations), Cint(code_coverage), Cint(prefer_specsig), Cint(gnu_pubnames), debug_info_kind, debug_info_level, Cint(safepoint_on_entry), - Cint(gcstack_arg), Cint(use_jlplt), + Cint(gcstack_arg), Cint(use_jlplt), Cint(trim), lookup) end end diff --git a/base/strings/io.jl b/base/strings/io.jl index 754e058cd2f54..df34712b519d5 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -51,6 +51,8 @@ function print(io::IO, xs...) return nothing end +setfield!(typeof(print).name.mt, :max_args, 10, :monotonic) + """ println([io::IO], xs...) @@ -74,6 +76,7 @@ julia> String(take!(io)) """ println(io::IO, xs...) = print(io, xs..., "\n") +setfield!(typeof(println).name.mt, :max_args, 10, :monotonic) ## conversion of general objects to strings ## """ @@ -149,6 +152,7 @@ function print_to_string(xs...) end String(_unsafe_take!(s)) end +setfield!(typeof(print_to_string).name.mt, :max_args, 10, :monotonic) function string_with_env(env, xs...) if isempty(xs) diff --git a/contrib/julia-config.jl b/contrib/julia-config.jl index c692b3f522fb2..8b1eb55cbe4f4 100755 --- a/contrib/julia-config.jl +++ b/contrib/julia-config.jl @@ -67,7 +67,7 @@ function ldlibs(doframework) "julia" end if Sys.isunix() - return "-Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname" + return "-L$(shell_escape(private_libDir())) -Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname" else return "-l$libname -lopenlibm" end diff --git a/contrib/juliac-buildscript.jl b/contrib/juliac-buildscript.jl new file mode 100644 index 0000000000000..50f96198c416b --- /dev/null +++ b/contrib/juliac-buildscript.jl @@ -0,0 +1,277 @@ +# Script to run in the process that generates juliac's object file output + +inputfile = ARGS[1] +output_type = ARGS[2] +add_ccallables = ARGS[3] == "true" + +# Initialize some things not usually initialized when output is requested +Sys.__init__() +Base.init_depot_path() +Base.init_load_path() +Base.init_active_project() +task = current_task() +task.rngState0 = 0x5156087469e170ab +task.rngState1 = 0x7431eaead385992c +task.rngState2 = 0x503e1d32781c2608 +task.rngState3 = 0x3a77f7189200c20b +task.rngState4 = 0x5502376d099035ae +uuid_tuple = (UInt64(0), UInt64(0)) +ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, uuid_tuple) +ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred) + +# Patch methods in Core and Base + +@eval Core begin + DomainError(@nospecialize(val), @nospecialize(msg::AbstractString)) = (@noinline; $(Expr(:new, :DomainError, :val, :msg))) +end + +(f::Base.RedirectStdStream)(io::Core.CoreSTDOUT) = Base._redirect_io_global(io, f.unix_fd) + +@eval Base begin + _assert_tostring(msg) = "" + reinit_stdio() = nothing + JuliaSyntax.enable_in_core!() = nothing + init_active_project() = ACTIVE_PROJECT[] = nothing + set_active_project(projfile::Union{AbstractString,Nothing}) = ACTIVE_PROJECT[] = projfile + disable_library_threading() = nothing + start_profile_listener() = nothing + @inline function invokelatest(f::F, args...; kwargs...) where F + return f(args...; kwargs...) + end + function sprint(f::F, args::Vararg{Any,N}; context=nothing, sizehint::Integer=0) where {F<:Function,N} + s = IOBuffer(sizehint=sizehint) + if context isa Tuple + f(IOContext(s, context...), args...) + elseif context !== nothing + f(IOContext(s, context), args...) + else + f(s, args...) + end + String(_unsafe_take!(s)) + end + function show_typeish(io::IO, @nospecialize(T)) + if T isa Type + show(io, T) + elseif T isa TypeVar + print(io, (T::TypeVar).name) + else + print(io, "?") + end + end + function show(io::IO, T::Type) + if T isa DataType + print(io, T.name.name) + if T !== T.name.wrapper && length(T.parameters) > 0 + print(io, "{") + first = true + for p in T.parameters + if !first + print(io, ", ") + end + first = false + if p isa Int + show(io, p) + elseif p isa Type + show(io, p) + elseif p isa Symbol + print(io, ":") + print(io, p) + elseif p isa TypeVar + print(io, p.name) + else + print(io, "?") + end + end + print(io, "}") + end + elseif T isa Union + print(io, "Union{") + show_typeish(io, T.a) + print(io, ", ") + show_typeish(io, T.b) + print(io, "}") + elseif T isa UnionAll + print(io, T.body::Type) + print(io, " where ") + print(io, T.var.name) + end + end + show_type_name(io::IO, tn::Core.TypeName) = print(io, tn.name) + + mapreduce(f::F, op::F2, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) where {F, F2} = + _mapreduce_dim(f, op, init, A, dims) + mapreduce(f::F, op::F2, A::AbstractArrayOrBroadcasted...; kw...) where {F, F2} = + reduce(op, map(f, A...); kw...) + + _mapreduce_dim(f::F, op::F2, nt, A::AbstractArrayOrBroadcasted, ::Colon) where {F, F2} = + mapfoldl_impl(f, op, nt, A) + + _mapreduce_dim(f::F, op::F2, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) where {F, F2} = + _mapreduce(f, op, IndexStyle(A), A) + + _mapreduce_dim(f::F, op::F2, nt, A::AbstractArrayOrBroadcasted, dims) where {F, F2} = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A) + + _mapreduce_dim(f::F, op::F2, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) where {F,F2} = + mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) + + mapreduce_empty_iter(f::F, op::F2, itr, ItrEltype) where {F, F2} = + reduce_empty_iter(MappingRF(f, op), itr, ItrEltype) + mapreduce_first(f::F, op::F2, x) where {F,F2} = reduce_first(op, f(x)) + + _mapreduce(f::F, op::F2, A::AbstractArrayOrBroadcasted) where {F,F2} = _mapreduce(f, op, IndexStyle(A), A) + mapreduce_empty(::typeof(identity), op::F, T) where {F} = reduce_empty(op, T) + mapreduce_empty(::typeof(abs), op::F, T) where {F} = abs(reduce_empty(op, T)) + mapreduce_empty(::typeof(abs2), op::F, T) where {F} = abs2(reduce_empty(op, T)) +end +@eval Base.Unicode begin + function utf8proc_map(str::Union{String,SubString{String}}, options::Integer, chartransform::F = identity) where F + nwords = utf8proc_decompose(str, options, C_NULL, 0, chartransform) + buffer = Base.StringVector(nwords*4) + nwords = utf8proc_decompose(str, options, buffer, nwords, chartransform) + nbytes = ccall(:utf8proc_reencode, Int, (Ptr{UInt8}, Int, Cint), buffer, nwords, options) + nbytes < 0 && utf8proc_error(nbytes) + return String(resize!(buffer, nbytes)) + end +end +@eval Base.GMP begin + function __init__() + try + ccall((:__gmp_set_memory_functions, libgmp), Cvoid, + (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}), + cglobal(:jl_gc_counted_malloc), + cglobal(:jl_gc_counted_realloc_with_old_size), + cglobal(:jl_gc_counted_free_with_size)) + ZERO.alloc, ZERO.size, ZERO.d = 0, 0, C_NULL + ONE.alloc, ONE.size, ONE.d = 1, 1, pointer(_ONE) + catch ex + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") + end + # This only works with a patched version of GMP, ignore otherwise + try + ccall((:__gmp_set_alloc_overflow_function, libgmp), Cvoid, + (Ptr{Cvoid},), + cglobal(:jl_throw_out_of_memory_error)) + ALLOC_OVERFLOW_FUNCTION[] = true + catch ex + # ErrorException("ccall: could not find function...") + if typeof(ex) != ErrorException + rethrow() + end + end + end +end +@eval Base.Sort begin + issorted(itr; + lt::T=isless, by::F=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) where {T,F} = + issorted(itr, ord(lt,by,rev,order)) +end +@eval Base.TOML begin + function try_return_datetime(p, year, month, day, h, m, s, ms) + return DateTime(year, month, day, h, m, s, ms) + end + function try_return_date(p, year, month, day) + return Date(year, month, day) + end + function parse_local_time(l::Parser) + h = @try parse_int(l, false) + h in 0:23 || return ParserError(ErrParsingDateTime) + _, m, s, ms = @try _parse_local_time(l, true) + # TODO: Could potentially parse greater accuracy for the + # fractional seconds here. + return try_return_time(l, h, m, s, ms) + end + function try_return_time(p, h, m, s, ms) + return Time(h, m, s, ms) + end +end + +# Load user code + +import Base.Experimental.entrypoint + +let mod = Base.include(Base.__toplevel__, inputfile) + if !isa(mod, Module) + mod = Main + end + if output_type == "--output-exe" && isdefined(mod, :main) && !add_ccallables + entrypoint(mod.main, ()) + end + #entrypoint(join, (Base.GenericIOBuffer{Memory{UInt8}}, Array{Base.SubString{String}, 1}, String)) + #entrypoint(join, (Base.GenericIOBuffer{Memory{UInt8}}, Array{String, 1}, Char)) + entrypoint(Base.task_done_hook, (Task,)) + entrypoint(Base.wait, ()) + entrypoint(Base.trypoptask, (Base.StickyWorkqueue,)) + entrypoint(Base.checktaskempty, ()) + if add_ccallables + ccall(:jl_add_ccallable_entrypoints, Cvoid, ()) + end +end + +# Additional method patches depending on whether user code loads certain stdlibs + +let loaded = Symbol.(Base.loaded_modules_array()) # TODO better way to do this + if :SparseArrays in loaded + using SparseArrays + @eval SparseArrays.CHOLMOD begin + function __init__() + ccall((:SuiteSparse_config_malloc_func_set, :libsuitesparseconfig), + Cvoid, (Ptr{Cvoid},), cglobal(:jl_malloc, Ptr{Cvoid})) + ccall((:SuiteSparse_config_calloc_func_set, :libsuitesparseconfig), + Cvoid, (Ptr{Cvoid},), cglobal(:jl_calloc, Ptr{Cvoid})) + ccall((:SuiteSparse_config_realloc_func_set, :libsuitesparseconfig), + Cvoid, (Ptr{Cvoid},), cglobal(:jl_realloc, Ptr{Cvoid})) + ccall((:SuiteSparse_config_free_func_set, :libsuitesparseconfig), + Cvoid, (Ptr{Cvoid},), cglobal(:jl_free, Ptr{Cvoid})) + end + end + end + if :Artifacts in loaded + using Artifacts + @eval Artifacts begin + function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, _::Val{lazyartifacts}) where lazyartifacts + moduleroot = Base.moduleroot(__module__) + if haskey(Base.module_keys, moduleroot) + # Process overrides for this UUID, if we know what it is + process_overrides(artifact_dict, Base.module_keys[moduleroot].uuid) + end + + # If the artifact exists, we're in the happy path and we can immediately + # return the path to the artifact: + dirs = artifact_paths(hash; honor_overrides=true) + for dir in dirs + if isdir(dir) + return jointail(dir, path_tail) + end + end + end + end + end + if :Pkg in loaded + using Pkg + @eval Pkg begin + __init__() = rand() #TODO, methods that do nothing don't get codegened + end + end + if :StyledStrings in loaded + using StyledStrings + @eval StyledStrings begin + __init__() = rand() + end + end +end + +empty!(Core.ARGS) +empty!(Base.ARGS) +empty!(LOAD_PATH) +empty!(DEPOT_PATH) +empty!(Base.TOML_CACHE.d) +Base.TOML.reinit!(Base.TOML_CACHE.p, "") +Base.ACTIVE_PROJECT[] = nothing +@eval Base begin + PROGRAM_FILE = "" +end +@eval Sys begin + BINDIR = "" + STDLIB = "" +end diff --git a/contrib/juliac.jl b/contrib/juliac.jl new file mode 100644 index 0000000000000..61e0e91958667 --- /dev/null +++ b/contrib/juliac.jl @@ -0,0 +1,110 @@ +# Julia compiler wrapper script +# NOTE: The interface and location of this script are considered unstable/experimental + +cmd = Base.julia_cmd() +cmd = `$cmd --startup-file=no --history-file=no` +output_type = nothing # exe, sharedlib, sysimage +trim = nothing +outname = nothing +file = nothing +add_ccallables = false + +help = findfirst(x->x == "--help", ARGS) +if help !== nothing + println( + """ + Usage: julia juliac.jl [--output-exe | --output-lib | --output-sysimage] [options] + --trim= Only output code statically determined to be reachable + --compile-ccallable Include all methods marked `@ccallable` in output + --verbose Request verbose output + """) + exit(0) +end + +let i = 1 + while i <= length(ARGS) + arg = ARGS[i] + if arg == "--output-exe" || arg == "--output-lib" || arg == "--output-sysimage" + isnothing(output_type) || error("Multiple output types specified") + global output_type = arg + i == length(ARGS) && error("Output specifier requires an argument") + global outname = ARGS[i+1] + i += 1 + elseif startswith(arg, "--trim") + arg = split(arg, '=') + if length(arg) == 1 + global trim = "safe" + else + global trim = arg[2] + end + elseif arg == "--compile-ccallable" + global add_ccallables = true + else + if arg[1] == '-' || !isnothing(file) + println("Unexpected argument `$arg`") + exit(1) + end + global file = arg + end + i += 1 + end +end + +isnothing(outname) && error("No output file specified") +isnothing(file) && error("No input file specified") + +absfile = abspath(file) +cflags = readchomp(`$(cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --cflags `) +cflags = Base.shell_split(cflags) +allflags = readchomp(`$(cmd) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR,"julia", "julia-config.jl")) --allflags`) +allflags = Base.shell_split(allflags) +tmpdir = mktempdir(cleanup=false) +initsrc_path = joinpath(tmpdir, "init.c") +init_path = joinpath(tmpdir, "init.a") +img_path = joinpath(tmpdir, "img.a") +bc_path = joinpath(tmpdir, "img-bc.a") + +open(initsrc_path, "w") do io + print(io, """ + #include + __attribute__((constructor)) void static_init(void) { + if (jl_is_initialized()) + return; + julia_init(JL_IMAGE_IN_MEMORY); + jl_exception_clear(); + } + """) +end + +static_call_graph_arg() = isnothing(trim) ? `` : `--trim=$(trim)` +is_verbose() = verbose ? `--verbose-compilation=yes` : `` +cmd = addenv(`$cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $(static_call_graph_arg()) $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) + +if !success(pipeline(cmd; stdout, stderr)) + println(stderr, "\nFailed to compile $file") + exit(1) +end + +run(`cc $(cflags) -g -c -o $init_path $initsrc_path`) + +if output_type == "--output-lib" || output_type == "--output-sysimage" + of, ext = splitext(outname) + soext = "." * Base.BinaryPlatforms.platform_dlext() + if ext == "" + outname = of * soext + end +end + +julia_libs = Base.shell_split(Base.isdebugbuild() ? "-ljulia-debug -ljulia-internal-debug" : "-ljulia -ljulia-internal") +try + if output_type == "--output-lib" + run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + elseif output_type == "--output-sysimage" + run(`cc $(allflags) -o $outname -shared -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $(julia_libs)`) + else + run(`cc $(allflags) -o $outname -Wl,$(Base.Linking.WHOLE_ARCHIVE) $img_path -Wl,$(Base.Linking.NO_WHOLE_ARCHIVE) $init_path $(julia_libs)`) + end +catch + println("\nCompilation failed.") + exit(1) +end diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index 7d4f7afdbb86a..64c309e1fb02a 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -117,3 +117,80 @@ See code comments for each components for more implementation details. depending on the ISA. The target selection will prefer exact CPU name match, larger vector register size, and larger number of features. An overview of this process is in `src/processor.cpp`. + +## Trimming + +System images are typically quite large, since Base includes a lot of functionality, and by +default system images also include several packages such as LinearAlgebra for convenience +and backwards compatibility. Most programs will use only a fraction of the functions in +these packages. Therefore it makes sense to build binaries that exclude unused functions +to save space, referred to as "trimming". + +While the basic idea of trimming is sound, Julia has dynamic and reflective features that make it +difficult (or impossible) to know in general which functions are unused. As an extreme example, +consider code like + +``` +getglobal(Base, Symbol(readchomp(stdin)))(1) +``` + +This code reads a function name from `stdin` and calls the named function from Base on the value +`1`. In this case it is impossible to predict which function will be called, so no functions +can reliably be considered "unused". With some noteworthy exceptions (Julia's own REPL being +one of them), most real-world programs do not do things like this. + +Less extreme cases occur, for example, when there are type instabilities that make it impossible +for the compiler to predict which method will be called. However, if code is well-typed and does +not use reflection, a complete and (hopefully) relatively small set of needed methods can be +determined, and the rest can be removed. The `--trim` command-line option requests this kind of +compilation. + +When `--trim` is specified in a command used to build a system image, the compiler begins +tracing calls starting at methods marked using `Base.Experimental.entrypoint`. If a call is too +dynamic to reasonably narrow down the possible call targets, an error is given at compile +time showing the location of the call. For testing purposes, it is possible to skip these +errors by specifying `--trim=unsafe` or `--trim=unsafe-warn`. Then you will get a system +image built, but it may crash at run time if needed code is not present. + +It typically makes sense to specify `--strip-ir` along with `--trim`, since trimmed binaries +are fully compiled and therefore don't need Julia IR. At some point we may make `--trim` imply +`--strip-ir`, but for now we have kept them orthogonal. + +To get the smallest possible binary, it will also help to specify `--strip-metadata` and +run the Unix `strip` utility. However, those steps remove Julia-specific and native (DWARF format) +debug info, respectively, and so will make debugging more difficult. + +### Common problems + +- The Base global variables `stdin`, `stdout`, and `stderr` are non-constant and so their + types are not known. All printing should use a specific IO object with a known type. + The easiest substitution is to use `print(Core.stdout, x)` instead of `print(x)` or + `print(stdout, x)`. +- Use tools like `JET`, `Cthulhu`, and/or `SnoopCompile` to identify failures of type-inference, and + follow our [Performance Tips](@ref) to fix them. + +### Compatibility concerns + +We have identified many small changes to Base that significantly increase the set of programs +that can be reliably trimmed. Unfortunately some of those changes would be considered breaking, +and so are only applied when trimming is requested (this is done by an external build script, +currently maintained inside the test suite as `test/trimming/buildscript.jl`). +Therefore in many cases trimming will require you to opt in to new variants of Base and some +standard libraries. + +If you want to use trimming, it is important to set up continuous integration testing that +performs a trimmed build and fully tests the resulting program. +Fortunately, if your program successfully compiles with `--trim` then it is very likely to work +the same as it did before. However, CI is needed to ensure that your program continues to build +with trimming as you develop it. + +Package authors may wish to test that their package is "trimming safe", however this is impossible +in general. Trimming is only expected to work given concrete entry points such as `main()` and +library entry points meant to be called from outside Julia. For generic packages, existing tests +for type stability like `@inferred` and `JET` are about as close as you can get to checking +trim compatibility. + +Trimming also introduces new compatibility issues between minor versions of Julia. At this time, +we are not able to guarantee that a program that can be trimmed in one version of Julia +can also be trimmed in all future versions of Julia. However, breakage of that kind is expected +to be rare. We also plan to try to *increase* the set of programs that can be trimmed over time. diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 41c3eacd61d26..ef20e51ea6e4e 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -218,7 +218,7 @@ The following is a complete list of command-line switches available when launchi |`--trace-compile-timing` |If --trace-compile is enabled show how long each took to compile in ms| |`--image-codegen` |Force generate code in imaging mode| |`--permalloc-pkgimg={yes\|no*}` |Copy the data section of package images into memory| - +|`--trim={no*|safe|unsafe|unsafe-warn}` |Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. The three non-default options differ in how they handle dynamic call sites. In safe mode, such sites result in compile-time errors. In unsafe mode, such sites are allowed but the resulting binary might be missing needed code and can throw runtime errors. With unsafe-warn, such sites will trigger warnings at compile-time and might error at runtime.| !!! compat "Julia 1.1" In Julia 1.0, the default `--project=@.` option did not search up from the root diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index b4c8ef6095a55..c2f112f9c9d5c 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -95,6 +95,17 @@ void jl_get_function_id_impl(void *native_code, jl_code_instance_t *codeinst, } } +extern "C" JL_DLLEXPORT_CODEGEN +void jl_get_llvm_mis_impl(void *native_code, arraylist_t* MIs) +{ + jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; + auto map = data->jl_fvar_map; + for (auto &ci : map) { + jl_method_instance_t *mi = ci.first->def; + arraylist_push(MIs, mi); + } +} + extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_impl(void *native_code, arraylist_t *gvs) { @@ -284,6 +295,7 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ jl_value_t *ci = cgparams.lookup(mi, world, world); JL_GC_PROMISE_ROOTED(ci); jl_code_instance_t *codeinst = NULL; + JL_GC_PUSH1(&codeinst); if (ci != jl_nothing && jl_atomic_load_relaxed(&((jl_code_instance_t *)ci)->inferred) != jl_nothing) { codeinst = (jl_code_instance_t*)ci; } @@ -301,9 +313,11 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ jl_mi_cache_insert(mi, codeinst); } } + JL_GC_POP(); return codeinst; } +arraylist_t new_invokes; // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup, and can // also be used be extern consumers like GPUCompiler.jl to obtain a module containing @@ -353,8 +367,12 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm params.imaging_mode = imaging; params.debug_level = cgparams->debug_info_level; params.external_linkage = _external_linkage; + arraylist_new(&new_invokes, 0); size_t compile_for[] = { jl_typeinf_world, _world }; - for (int worlds = 0; worlds < 2; worlds++) { + int worlds = 0; + if (jl_options.trim != JL_TRIM_NO) + worlds = 1; + for (; worlds < 2; worlds++) { JL_TIMING(NATIVE_AOT, NATIVE_Codegen); size_t this_world = compile_for[worlds]; if (!this_world) @@ -373,6 +391,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm continue; } mi = (jl_method_instance_t*)item; +compile_mi: src = NULL; // if this method is generally visible to the current compilation world, // and this is either the primary world, or not applicable in the primary world @@ -380,16 +399,47 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm if (jl_atomic_load_relaxed(&mi->def.method->primary_world) <= this_world && this_world <= jl_atomic_load_relaxed(&mi->def.method->deleted_world)) { // find and prepare the source code to compile jl_code_instance_t *codeinst = jl_ci_cache_lookup(*cgparams, mi, this_world); - if (codeinst && !params.compiled_functions.count(codeinst)) { + if (jl_options.trim != JL_TRIM_NO && !codeinst) { + // If we're building a small image, we need to compile everything + // to ensure that we have all the information we need. + jl_safe_printf("Codegen decided not to compile code root"); + jl_(mi); + abort(); + } + if (codeinst && !params.compiled_functions.count(codeinst) && !data->jl_fvar_map.count(codeinst)) { // now add it to our compilation results - JL_GC_PROMISE_ROOTED(codeinst->rettype); - orc::ThreadSafeModule result_m = jl_create_ts_module(name_from_method_instance(codeinst->def), - params.tsctx, clone.getModuleUnlocked()->getDataLayout(), - Triple(clone.getModuleUnlocked()->getTargetTriple())); - jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, NULL, params); - if (result_m) - params.compiled_functions[codeinst] = {std::move(result_m), std::move(decls)}; + // Const returns do not do codegen, but juliac inspects codegen results so make a dummy fvar entry to represent it + if (jl_options.trim != JL_TRIM_NO && jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) { + data->jl_fvar_map[codeinst] = std::make_tuple((uint32_t)-3, (uint32_t)-3); + } else { + JL_GC_PROMISE_ROOTED(codeinst->rettype); + orc::ThreadSafeModule result_m = jl_create_ts_module(name_from_method_instance(codeinst->def), + params.tsctx, clone.getModuleUnlocked()->getDataLayout(), + Triple(clone.getModuleUnlocked()->getTargetTriple())); + jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, NULL, params); + if (result_m) + params.compiled_functions[codeinst] = {std::move(result_m), std::move(decls)}; + else if (jl_options.trim != JL_TRIM_NO) { + // if we're building a small image, we need to compile everything + // to ensure that we have all the information we need. + jl_safe_printf("codegen failed to compile code root"); + jl_(mi); + abort(); + } + } } + } else if (this_world != jl_typeinf_world) { + /* + jl_safe_printf("Codegen could not find requested codeinstance to be compiled\n"); + jl_(mi); + abort(); + */ + } + // TODO: is goto the best way to do this? + jl_compile_workqueue(params, policy); + mi = (jl_method_instance_t*)arraylist_pop(&new_invokes); + if (mi != NULL) { + goto compile_mi; } } @@ -397,6 +447,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm jl_compile_workqueue(params, policy); } JL_GC_POP(); + arraylist_free(&new_invokes); // process the globals array, before jl_merge_module destroys them SmallVector gvars(params.global_targets.size()); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7f96bb1047abc..4547e693755cd 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2336,6 +2336,12 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ret = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type); } else { + if (trim_may_error(ctx.params->trim)) { + // if we know the return type, we can assume the result is of that type + errs() << "ERROR: Dynamic call to setfield/modifyfield\n"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, 3, julia_call); ret = mark_julia_type(ctx, callval, true, jl_any_type); } @@ -4077,6 +4083,12 @@ static jl_cgval_t union_store(jl_codectx_t &ctx, rhs = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type); } else { + if (trim_may_error(ctx.params->trim)) { + // if we know the return type, we can assume the result is of that type + errs() << "ERROR: Dynamic call to setfield/modifyfield\n"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, 3, julia_call); rhs = mark_julia_type(ctx, callval, true, jl_any_type); } diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 41812d903816c..7ddb68fd6b036 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -15,6 +15,7 @@ JL_DLLEXPORT void jl_dump_native_fallback(void *native_code, ios_t *z, ios_t *s) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_gvs_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE JL_DLLEXPORT void jl_get_llvm_external_fns_fallback(void *native_code, arraylist_t *gvs) UNAVAILABLE +JL_DLLEXPORT void jl_get_llvm_mis_fallback(void *native_code, arraylist_t* MIs) UNAVAILABLE JL_DLLEXPORT void jl_extern_c_fallback(jl_function_t *f, jl_value_t *rt, jl_value_t *argt, char *name) UNAVAILABLE JL_DLLEXPORT jl_value_t *jl_dump_method_asm_fallback(jl_method_instance_t *linfo, size_t world, diff --git a/src/codegen.cpp b/src/codegen.cpp index a452e0fccd0c5..a7a985284c87b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include // target machine computation @@ -1651,31 +1652,23 @@ static const auto &builtin_func_map() { return builtins; } +static const auto &may_dispatch_builtins() { + static std::unordered_set builtins( + {jl_f__apply_iterate_addr, + jl_f__apply_pure_addr, + jl_f__call_in_world_addr, + jl_f__call_in_world_total_addr, + jl_f__call_latest_addr, + }); + return builtins; +} + static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction<>{XSTR(jl_new_opaque_closure_jlcall), get_func_sig, get_func_attrs}; static _Atomic(uint64_t) globalUniqueGeneratedNames{1}; // --- code generation --- -extern "C" { - jl_cgparams_t jl_default_cgparams = { - /* track_allocations */ 1, - /* code_coverage */ 1, - /* prefer_specsig */ 0, -#ifdef _OS_WINDOWS_ - /* gnu_pubnames */ 0, -#else - /* gnu_pubnames */ 1, -#endif - /* debug_info_kind */ (int) DICompileUnit::DebugEmissionKind::FullDebug, - /* debug_line_info */ 1, - /* safepoint_on_entry */ 1, - /* gcstack_arg */ 1, - /* use_jlplt*/ 1, - /* lookup */ jl_rettype_inferred_addr }; -} - - static MDNode *best_tbaa(jl_tbaacache_t &tbaa_cache, jl_value_t *jt) { jt = jl_unwrap_unionall(jt); if (jt == (jl_value_t*)jl_datatype_type || @@ -1987,7 +1980,7 @@ class jl_codectx_t { size_t max_world = -1; const char *name = NULL; StringRef file{}; - ssize_t *line = NULL; + int32_t line = -1; Value *spvals_ptr = NULL; Value *argArray = NULL; Value *argCount = NULL; @@ -2146,6 +2139,179 @@ static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static unsigned julia_alignment(jl_value_t *jt); static void recombine_value(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dst, jl_aliasinfo_t const &dst_ai, Align alignment, bool isVolatile); +static void print_stack_crumbs(jl_codectx_t &ctx) +{ + errs() << "\n"; + errs() << "Stacktrace:\n"; + jl_method_instance_t *caller = ctx.linfo; + jl_((jl_value_t*)caller); + errs() << "In " << ctx.file << ":" << ctx.line << "\n"; + while (true) { + auto it = ctx.emission_context.enqueuers.find(caller); + if (it != ctx.emission_context.enqueuers.end()) { + caller = std::get(it->second); + } else { + break; + } + if (caller) { + if (jl_is_method_instance(caller)) { + for (auto it2 = std::get(it->second).begin(); it2 != (std::prev(std::get(it->second).end())); ++it2) { + auto frame = *it2; + errs() << std::get<0>(frame) << " \n"; + errs() << "In " << std::get<1>(frame) << ":" << std::get(frame) << "\n"; + } + auto &frame = std::get(it->second).front(); + jl_((jl_value_t*)caller); + errs() << "In " << std::get<1>(frame) << ":" << std::get(frame) << "\n"; + } + } + else + break; + } + abort(); +} + +static jl_value_t *StackFrame( + jl_value_t *linfo, + std::string fn_name, + std::string filepath, + int32_t lineno, + jl_value_t *inlined) +{ + jl_value_t *StackFrame = jl_get_global(jl_base_module, jl_symbol("StackFrame")); + assert(StackFrame != nullptr); + + jl_value_t *args[7] = { + /* func */ (jl_value_t *)jl_symbol(fn_name.c_str()), + /* line */ (jl_value_t *)jl_symbol(filepath.c_str()), + /* line */ jl_box_int32(lineno), + /* linfo */ (jl_value_t *)linfo, + /* from_c */ jl_false, + /* inlined */ inlined, + /* pointer */ jl_box_uint64(0) + }; + + jl_value_t *frame = nullptr; + JL_TRY { + frame = jl_apply_generic(StackFrame, args, 7); + } JL_CATCH { + jl_safe_printf("Error creating stack frame\n"); + } + return frame; +} + +static void push_frames(jl_codectx_t &ctx, jl_method_instance_t *caller, jl_method_instance_t *callee, int no_debug=false) +{ + CallFrames frames; + auto it = ctx.emission_context.enqueuers.find(callee); + if (it != ctx.emission_context.enqueuers.end()) + return; + if (no_debug) { // Used in tojlinvoke + frames.push_back({ctx.funcName, "", 0}); + ctx.emission_context.enqueuers.insert({callee, {caller, std::move(frames)}}); + return; + } + auto DL = ctx.builder.getCurrentDebugLocation(); + auto filename = std::string(DL->getFilename()); + auto line = DL->getLine(); + auto fname = std::string(DL->getScope()->getSubprogram()->getName()); + frames.push_back({fname, filename, line}); + auto DI = DL.getInlinedAt(); + while (DI) { + auto filename = std::string(DI->getFilename()); + auto line = DI->getLine(); + auto fname = std::string(DI->getScope()->getSubprogram()->getName()); + frames.push_back({fname, filename, line}); + DI = DI->getInlinedAt(); + } + ctx.emission_context.enqueuers.insert({callee, {caller, std::move(frames)}}); +} + +static jl_array_t* build_stack_crumbs(jl_codectx_t &ctx) JL_NOTSAFEPOINT +{ + static intptr_t counter = 5; + jl_method_instance_t *caller = (jl_method_instance_t*)counter; //nothing serves as a sentinel for the bottom for the stack + push_frames(ctx, ctx.linfo, (jl_method_instance_t*)caller); + counter++; + jl_array_t *out = jl_alloc_array_1d(jl_array_any_type, 0); + JL_GC_PUSH1(&out); + while (true) { + auto it = ctx.emission_context.enqueuers.find(caller); + if (it != ctx.emission_context.enqueuers.end()) { + caller = std::get(it->second); + } else { + break; + } + if (caller) { + assert(ctx.emission_context.enqueuers.count(caller) == 1); + if (jl_is_method_instance(caller)) { + //TODO: Use a subrange when C++20 is a thing + for (auto it2 = std::get(it->second).begin(); it2 != (std::prev(std::get(it->second).end())); ++it2) { + auto frame = *it2; + jl_value_t *stackframe = StackFrame(jl_nothing, std::get<0>(frame), std::get<1>(frame), std::get(frame), jl_true); + if (stackframe == nullptr) + print_stack_crumbs(ctx); + jl_array_ptr_1d_push(out, stackframe); + } + auto &frame = std::get(it->second).back(); + jl_value_t *stackframe = StackFrame((jl_value_t *)caller, std::get<0>(frame), std::get<1>(frame), std::get(frame), jl_false); + if (stackframe == nullptr) + print_stack_crumbs(ctx); + jl_array_ptr_1d_push(out, stackframe); + } + } + else + break; + } + JL_GC_POP(); + return out; +} + +static void print_stacktrace(jl_codectx_t &ctx, int trim) +{ + jl_task_t *ct = jl_get_current_task(); + assert(ct); + + // Temporarily operate in the current age + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); + jl_array_t* bt = build_stack_crumbs(ctx); + JL_GC_PUSH1(&bt); + + // Call `reinit_stdio` to get TTY IO objects (w/ color) + jl_value_t *reinit_stdio = jl_get_global(jl_base_module, jl_symbol("_reinit_stdio")); + assert(reinit_stdio); + jl_apply_generic(reinit_stdio, nullptr, 0); + + // Show the backtrace + jl_value_t *show_backtrace = jl_get_global(jl_base_module, jl_symbol("show_backtrace")); + jl_value_t *base_stderr = jl_get_global(jl_base_module, jl_symbol("stderr")); + assert(show_backtrace && base_stderr); + + JL_TRY { + jl_value_t *args[2] = { base_stderr, (jl_value_t *)bt }; + jl_apply_generic(show_backtrace, args, 2); + } JL_CATCH { + jl_printf(JL_STDERR,"Error showing backtrace\n"); + print_stack_crumbs(ctx); + } + + jl_printf(JL_STDERR, "\n\n"); + JL_GC_POP(); + ct->world_age = last_age; + + if (trim == JL_TRIM_SAFE) { + jl_printf(JL_STDERR,"Aborting compilation due to finding a dynamic dispatch"); + exit(1); + } + return; +} + +static int trim_may_error(int trim) +{ + return (trim == JL_TRIM_SAFE) || (trim == JL_TRIM_UNSAFE_WARN); +} + static GlobalVariable *prepare_global_in(Module *M, JuliaVariable *G) { return G->realize(M); @@ -4281,6 +4447,12 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *theArgs = emit_ptrgep(ctx, ctx.argArray, ctx.nReqArgs * sizeof(jl_value_t*)); Value *r = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { theF, theArgs, nva }); *ret = mark_julia_type(ctx, r, true, jl_any_type); + if (trim_may_error(ctx.params->trim)) { + // if we know the return type, we can assume the result is of that type + errs() << "ERROR: Dynamic call to Core._apply_iterate detected\n"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } return true; } } @@ -5388,12 +5560,25 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR if (need_to_emit) { Function *trampoline_decl = cast(jl_Module->getNamedValue(protoname)); ctx.call_targets[codeinst] = {cc, return_roots, trampoline_decl, specsig}; + if (trim_may_error(ctx.params->trim)) + push_frames(ctx, ctx.linfo, mi); } } } } } if (!handled) { + if (trim_may_error(ctx.params->trim)) { + if (lival.constant) { + arraylist_push(&new_invokes, lival.constant); + push_frames(ctx, ctx.linfo, (jl_method_instance_t*)lival.constant); + } else { + errs() << "Dynamic call to unknown function"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + + print_stacktrace(ctx, ctx.params->trim); + } + } Value *r = emit_jlcall(ctx, jlinvoke_func, boxed(ctx, lival), argv, nargs, julia_call2); result = mark_julia_type(ctx, r, true, rt); } @@ -5453,7 +5638,12 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ return mark_julia_type(ctx, oldnew, true, rt); } } - + if (trim_may_error(ctx.params->trim)) { + errs() << "ERROR: dynamic invoke modify call to"; + jl_(args[0]); + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } // emit function and arguments Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs, julia_call); return mark_julia_type(ctx, callval, true, rt); @@ -5523,10 +5713,15 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo bool handled = emit_builtin_call(ctx, &result, f.constant, argv, nargs - 1, rt, ex, is_promotable); if (handled) return result; - + jl_fptr_args_t builtin_fptr = jl_get_builtin_fptr((jl_datatype_t*)jl_typeof(f.constant)); // special case for some known builtin not handled by emit_builtin_call - auto it = builtin_func_map().find(jl_get_builtin_fptr((jl_datatype_t*)jl_typeof(f.constant))); + auto it = builtin_func_map().find(builtin_fptr); if (it != builtin_func_map().end()) { + if (trim_may_error(ctx.params->trim) && may_dispatch_builtins().count(builtin_fptr)) { + errs() << "ERROR: Dynamic call to builtin" << jl_symbol_name(((jl_datatype_t*)jl_typeof(f.constant))->name->name); + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } Value *ret = emit_jlcall(ctx, it->second, Constant::getNullValue(ctx.types().T_prjlvalue), ArrayRef(argv).drop_front(), nargs - 1, julia_call); setName(ctx.emission_context, ret, it->second->name + "_ret"); return mark_julia_type(ctx, ret, true, rt); @@ -5565,7 +5760,79 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo } } } + int failed_dispatch = !argv[0].constant; + if (ctx.params->trim != JL_TRIM_NO) { + size_t min_valid = 1; + size_t max_valid = ~(size_t)0; + size_t latest_world = jl_get_world_counter(); // TODO: marshal the world age of the compilation here. + + // Find all methods matching the call signature + jl_array_t *matches = NULL; + jl_value_t *tup = NULL; + JL_GC_PUSH2(&tup, &matches); + if (!failed_dispatch) { + SmallVector argtypes; + for (auto& arg: argv) + argtypes.push_back(arg.typ); + tup = jl_apply_tuple_type_v(argtypes.data(), argtypes.size()); + matches = (jl_array_t*)jl_matching_methods((jl_tupletype_t*)tup, jl_nothing, 10 /*TODO: make global*/, 1, + latest_world, &min_valid, &max_valid, NULL); + if ((jl_value_t*)matches == jl_nothing) + failed_dispatch = 1; + } + + // Expand each matching method to its unique specialization, if it has exactly one + if (!failed_dispatch) { + size_t k; + size_t len = new_invokes.len; + for (k = 0; k < jl_array_nrows(matches); k++) { + jl_method_match_t *match = (jl_method_match_t *)jl_array_ptr_ref(matches, k); + jl_method_instance_t *mi = jl_method_match_to_mi(match, latest_world, min_valid, max_valid, 0); + if (!mi) { + if (jl_array_nrows(matches) == 1) { + // if the method match is not compileable, but there is only one, fall back to + // unspecialized implementation + mi = jl_get_unspecialized(match->method); + } + else { + new_invokes.len = len; + failed_dispatch = 1; + break; + } + } + arraylist_push(&new_invokes, mi); + } + } + JL_GC_POP(); + } + if (failed_dispatch && trim_may_error(ctx.params->trim)) { + errs() << "Dynamic call to "; + jl_jmp_buf *old_buf = jl_get_safe_restore(); + jl_jmp_buf buf; + jl_set_safe_restore(&buf); + if (!jl_setjmp(buf, 0)) { + jl_static_show((JL_STREAM*)STDERR_FILENO, (jl_value_t*)args[0]); + jl_printf((JL_STREAM*)STDERR_FILENO,"("); + for (size_t i = 1; i < nargs; ++i) { + jl_value_t *typ = argv[i].typ; + if (!jl_is_concrete_type(typ)) // Print type in red + jl_printf((JL_STREAM*)STDERR_FILENO, "\x1b[31m"); + jl_static_show((JL_STREAM*)STDERR_FILENO, (jl_value_t*)argv[i].typ); + if (!jl_is_concrete_type(typ)) + jl_printf((JL_STREAM*)STDERR_FILENO, "\x1b[0m"); + if (i != nargs-1) + jl_printf((JL_STREAM*)STDERR_FILENO,", "); + } + jl_printf((JL_STREAM*)STDERR_FILENO,")\n"); + } + else { + jl_printf((JL_STREAM*)STDERR_FILENO, "\n!!! ERROR while printing error -- ABORTING !!!\n"); + } + jl_set_safe_restore(old_buf); + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } // emit function and arguments Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, n_generic_args, julia_call); return mark_julia_type(ctx, callval, true, rt); @@ -6710,6 +6977,13 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ ((jl_method_t*)source.constant)->nargs > 0 && jl_is_valid_oc_argtype((jl_tupletype_t*)argt.constant, (jl_method_t*)source.constant); + if (!can_optimize && trim_may_error(ctx.params->trim)) { + // if we know the return type, we can assume the result is of that type + errs() << "ERROR: Dynamic call to OpaqueClosure method\n"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } + if (can_optimize) { jl_value_t *closure_t = NULL; jl_value_t *env_t = NULL; @@ -6909,6 +7183,11 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptr GlobalVariable::InternalLinkage, name, M); jl_init_function(f, params.TargetTriple); + if (trim_may_error(params.params->trim)) { + arraylist_push(&new_invokes, codeinst->def); // Try t compile this invoke + // TODO: Debuginfo! + push_frames(ctx, ctx.linfo, codeinst->def, 1); + } jl_name_jlfunc_args(params, f); //f->setAlwaysInline(); ctx.f = f; // for jl_Module @@ -8126,6 +8405,7 @@ static jl_llvm_functions_t if (lam && jl_is_method(lam->def.method)) { toplineno = lam->def.method->line; ctx.file = jl_symbol_name(lam->def.method->file); + ctx.line = lam->def.method->line; } else if ((jl_value_t*)src->debuginfo != jl_nothing) { // look for the file and line info of the original start of this block, as reported by lowering @@ -8134,6 +8414,7 @@ static jl_llvm_functions_t debuginfo = debuginfo->linetable; ctx.file = jl_debuginfo_file(debuginfo); struct jl_codeloc_t lineidx = jl_uncompress1_codeloc(debuginfo->codelocs, 0); + ctx.line = lineidx.line; toplineno = std::max((int32_t)0, lineidx.line); } if (ctx.file.empty()) @@ -9904,7 +10185,7 @@ void jl_compile_workqueue( if (it == params.compiled_functions.end()) { // Reinfer the function. The JIT came along and removed the inferred // method body. See #34993 - if (policy != CompilationPolicy::Default && + if ((policy != CompilationPolicy::Default || params.params->trim) && jl_atomic_load_relaxed(&codeinst->inferred) == jl_nothing) { // XXX: SOURCE_MODE_FORCE_SOURCE is wrong here (neither sufficient nor necessary) codeinst = jl_type_infer(codeinst->def, jl_atomic_load_relaxed(&codeinst->max_world), SOURCE_MODE_FORCE_SOURCE); @@ -9935,6 +10216,16 @@ void jl_compile_workqueue( if (proto.specsig) { // expected specsig if (!preal_specsig) { + if (params.params->trim) { + auto it = params.compiled_functions.find(codeinst); //TODO: What to do about this + errs() << "Bailed out to invoke when compiling:"; + jl_(codeinst->def); + if (it != params.compiled_functions.end()) { + errs() << it->second.second.functionObject << "\n"; + errs() << it->second.second.specFunctionObject << "\n"; + } else + errs() << "codeinst not in compile_functions\n"; + } // emit specsig-to-(jl)invoke conversion StringRef invokeName; if (invoke != NULL) @@ -10124,6 +10415,22 @@ int jl_opaque_ptrs_set = 0; extern "C" void jl_init_llvm(void) { + jl_default_cgparams = { + /* track_allocations */ 1, + /* code_coverage */ 1, + /* prefer_specsig */ 0, +#ifdef _OS_WINDOWS_ + /* gnu_pubnames */ 0, +#else + /* gnu_pubnames */ 1, +#endif + /* debug_info_kind */ (int) DICompileUnit::DebugEmissionKind::FullDebug, + /* debug_info_level */ (int) jl_options.debug_level, + /* safepoint_on_entry */ 1, + /* gcstack_arg */ 1, + /* use_jlplt*/ 1, + /* trim */ 0, + /* lookup */ jl_rettype_inferred_addr }; jl_page_size = jl_getpagesize(); jl_default_debug_info_kind = (int) DICompileUnit::DebugEmissionKind::FullDebug; jl_default_cgparams.debug_info_level = (int) jl_options.debug_level; diff --git a/src/gf.c b/src/gf.c index e6f5b4ee007f7..321711c839aa8 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1360,8 +1360,7 @@ static inline jl_typemap_entry_t *lookup_leafcache(jl_genericmemory_t *leafcache } return NULL; } - -static jl_method_instance_t *cache_method( +jl_method_instance_t *cache_method( jl_methtable_t *mt, _Atomic(jl_typemap_t*) *cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature jl_method_t *definition, @@ -1707,7 +1706,7 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue jl_printf(s, ".\n"); jl_uv_flush(s); } - if (jl_generating_output()) { + if (jl_generating_output() && jl_options.incremental) { jl_printf(JL_STDERR, "ERROR: Method overwriting is not permitted during Module precompilation. Use `__precompile__(false)` to opt-out of precompilation.\n"); jl_throw(jl_precompilable_error); } @@ -2411,7 +2410,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t na // spvals is any matched static parameter values, m is the Method, // full is a boolean indicating if that method fully covers the input // -// lim is the max # of methods to return. if there are more, returns jl_false. +// lim is the max # of methods to return. if there are more, returns jl_nothing. // Negative values stand for no limit. // Unless lim == -1, remove matches that are unambiguously covered by earlier ones JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, jl_value_t *mt, int lim, int include_ambiguous, @@ -2431,7 +2430,7 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, jl_value_t * return ml_matches((jl_methtable_t*)mt, types, lim, include_ambiguous, 1, world, 1, min_valid, max_valid, ambig); } -jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT) +JL_DLLEXPORT jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT) { // one unspecialized version of a function can be shared among all cached specializations if (!jl_is_method(def) || def->source == NULL) { @@ -2910,7 +2909,7 @@ jl_method_instance_t *jl_normalize_to_compilable_mi(jl_method_instance_t *mi JL_ } // return a MethodInstance for a compileable method_match -jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *match, size_t world, size_t min_valid, size_t max_valid, int mt_cache) +JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *match, size_t world, size_t min_valid, size_t max_valid, int mt_cache) { jl_method_t *m = match->method; jl_svec_t *env = match->sparams; @@ -3112,6 +3111,21 @@ JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) return 1; } +JL_DLLEXPORT int jl_add_entrypoint(jl_tupletype_t *types) +{ + size_t world = jl_atomic_load_acquire(&jl_world_counter); + size_t min_valid = 0; + size_t max_valid = ~(size_t)0; + jl_method_instance_t *mi = jl_get_compile_hint_specialization(types, world, &min_valid, &max_valid, 1); + if (mi == NULL) + return 0; + JL_GC_PROMISE_ROOTED(mi); + if (jl_generating_output() && jl_options.trim) { + arraylist_push(jl_entrypoint_mis, mi); + } + return 1; +} + // add type of `f` to front of argument tuple type jl_value_t *jl_argtype_with_function(jl_value_t *f, jl_value_t *types0) { diff --git a/src/init.c b/src/init.c index 86c0877b14289..413d4e8055e54 100644 --- a/src/init.c +++ b/src/init.c @@ -44,6 +44,7 @@ extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE); // list of modules being deserialized with __init__ methods jl_array_t *jl_module_init_order; +arraylist_t *jl_entrypoint_mis; JL_DLLEXPORT size_t jl_page_size; @@ -721,6 +722,7 @@ static void restore_fp_env(void) static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct); JL_DLLEXPORT int jl_default_debug_info_kind; +JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; static void init_global_mutexes(void) { JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex"); @@ -841,8 +843,10 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ JL_TIMING(JULIA_INIT, JULIA_INIT); jl_resolve_sysimg_location(rel); // loads sysimg if available, and conditionally sets jl_options.cpu_target - if (rel == JL_IMAGE_IN_MEMORY) + if (rel == JL_IMAGE_IN_MEMORY) { jl_set_sysimg_so(jl_exe_handle); + jl_options.image_file = jl_options.julia_bin; + } else if (jl_options.image_file) jl_preload_sysimg_so(jl_options.image_file); if (jl_options.cpu_target == NULL) @@ -899,6 +903,11 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ JL_GC_POP(); } + if (jl_options.trim) { + jl_entrypoint_mis = (arraylist_t *)malloc_s(sizeof(arraylist_t)); + arraylist_new(jl_entrypoint_mis, 0); + } + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) jl_install_sigint_handler(); } diff --git a/src/jitlayers.h b/src/jitlayers.h index 107782e354d4a..93669c2351d88 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -29,7 +29,7 @@ #include "llvm-version.h" #include #include - +#include // As of LLVM 13, there are two runtime JIT linker implementations, the older // RuntimeDyld (used via orc::RTDyldObjectLinkingLayer) and the newer JITLink @@ -65,6 +65,7 @@ using namespace llvm; extern "C" jl_cgparams_t jl_default_cgparams; +extern arraylist_t new_invokes; DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeContext, LLVMOrcThreadSafeContextRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeModule, LLVMOrcThreadSafeModuleRef) @@ -211,7 +212,7 @@ struct jl_codegen_call_target_t { typedef SmallVector, 0> jl_workqueue_t; // TODO DenseMap? typedef std::map> jl_compiled_functions_t; - +typedef std::list> CallFrames; struct jl_codegen_params_t { orc::ThreadSafeContext tsctx; orc::ThreadSafeContext::Lock tsctx_lock; @@ -230,6 +231,7 @@ struct jl_codegen_params_t { std::map ditypes; std::map llvmtypes; DenseMap mergedConstants; + llvm::MapVector> enqueuers; // Map from symbol name (in a certain library) to its GV in sysimg and the // DL handle address in the current session. StringMap> libMapGV; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 7abf2b055bb8c..0c712ef37cb5b 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -344,6 +344,8 @@ XX(jl_new_typevar) \ XX(jl_next_from_addrinfo) \ XX(jl_normalize_to_compilable_sig) \ + XX(jl_method_match_to_mi) \ + XX(jl_get_unspecialized) \ XX(jl_no_exc_handler) \ XX(jl_object_id) \ XX(jl_object_id_) \ @@ -522,6 +524,7 @@ YY(jl_dump_native) \ YY(jl_get_llvm_gvs) \ YY(jl_get_llvm_external_fns) \ + YY(jl_get_llvm_mis) \ YY(jl_dump_function_asm) \ YY(jl_LLVMCreateDisasm) \ YY(jl_LLVMDisasmInstruction) \ diff --git a/src/jloptions.c b/src/jloptions.c index f63f4de020e26..530d5e2577a9a 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -101,6 +101,7 @@ JL_DLLEXPORT void jl_init_options(void) 0, // permalloc_pkgimg 0, // heap-size-hint 0, // trace_compile_timing + 0, // trim }; jl_options_initialized = 1; } @@ -251,7 +252,7 @@ static const char opts_hidden[] = " --strip-ir Remove IR (intermediate representation) of compiled\n" " functions\n\n" - // compiler debugging (see the devdocs for tips on using these options) + // compiler debugging and experimental (see the devdocs for tips on using these options) " --output-unopt-bc Generate unoptimized LLVM bitcode (.bc)\n" " --output-bc Generate LLVM bitcode (.bc)\n" " --output-asm Generate an assembly file (.s)\n" @@ -265,6 +266,13 @@ static const char opts_hidden[] = " compile in ms\n" " --image-codegen Force generate code in imaging mode\n" " --permalloc-pkgimg={yes|no*} Copy the data section of package images into memory\n" + " --trim={no*|safe|unsafe|unsafe-warn}\n" + " Build a sysimage including only code provably reachable\n" + " from methods marked by calling `entrypoint`. In unsafe\n" + " mode, the resulting binary might be missing needed code\n" + " and can throw errors. With unsafe-warn warnings will be\n" + " printed for dynamic call sites that might lead to such\n" + " errors. In safe mode compile-time errors are given instead.\n" ; JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) @@ -311,7 +319,8 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_strip_ir, opt_heap_size_hint, opt_gc_threads, - opt_permalloc_pkgimg + opt_permalloc_pkgimg, + opt_trim, }; static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:m:"; static const struct option longopts[] = { @@ -375,6 +384,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "strip-ir", no_argument, 0, opt_strip_ir }, { "permalloc-pkgimg",required_argument, 0, opt_permalloc_pkgimg }, { "heap-size-hint", required_argument, 0, opt_heap_size_hint }, + { "trim", optional_argument, 0, opt_trim }, { 0, 0, 0, 0 } }; @@ -934,6 +944,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --permalloc-pkgimg={yes|no} (%s)", optarg); break; + case opt_trim: + if (optarg == NULL || !strcmp(optarg,"safe")) + jl_options.trim = JL_TRIM_SAFE; + else if (!strcmp(optarg,"no")) + jl_options.trim = JL_TRIM_NO; + else if (!strcmp(optarg,"unsafe")) + jl_options.trim = JL_TRIM_UNSAFE; + else if (!strcmp(optarg,"unsafe-warn")) + jl_options.trim = JL_TRIM_UNSAFE_WARN; + else + jl_errorf("julia: invalid argument to --trim={safe|no|unsafe|unsafe-warn} (%s)", optarg); + break; default: jl_errorf("julia: unhandled option -- %c\n" "This is a bug, please report it.", c); diff --git a/src/jloptions.h b/src/jloptions.h index aac2a64a373a8..3d7deedb59e15 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -62,6 +62,7 @@ typedef struct { int8_t permalloc_pkgimg; uint64_t heap_size_hint; int8_t trace_compile_timing; + int8_t trim; } jl_options_t; #endif diff --git a/src/julia.expmap.in b/src/julia.expmap.in index e5f9ee890205f..29366f6296a85 100644 --- a/src/julia.expmap.in +++ b/src/julia.expmap.in @@ -5,8 +5,8 @@ asprintf; bitvector_*; ios_*; - arraylist_grow; - small_arraylist_grow; + arraylist_*; + small_arraylist_*; jl_*; ijl_*; _jl_mutex_*; diff --git a/src/julia.h b/src/julia.h index abb8a57ff13b0..73b96cf0183d1 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2579,6 +2579,11 @@ JL_DLLEXPORT int jl_generating_output(void) JL_NOTSAFEPOINT; #define JL_OPTIONS_USE_PKGIMAGES_YES 1 #define JL_OPTIONS_USE_PKGIMAGES_NO 0 +#define JL_TRIM_NO 0 +#define JL_TRIM_SAFE 1 +#define JL_TRIM_UNSAFE 2 +#define JL_TRIM_UNSAFE_WARN 3 + // Version information #include // Generated file @@ -2626,10 +2631,12 @@ typedef struct { int gcstack_arg; // Pass the ptls value as an argument with swiftself int use_jlplt; // Whether to use the Julia PLT mechanism or emit symbols directly + int trim; // can we emit dynamic dispatches? // Cache access. Default: jl_rettype_inferred_native. jl_codeinstance_lookup_t lookup; } jl_cgparams_t; extern JL_DLLEXPORT int jl_default_debug_info_kind; +extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; typedef struct { int emit_metadata; diff --git a/src/julia_internal.h b/src/julia_internal.h index f00667d016796..9a61c3d18356f 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -664,8 +664,9 @@ JL_DLLEXPORT jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, size_t min_world, size_t max_world, jl_debuginfo_t *edges); -jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); +JL_DLLEXPORT jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_read_codeinst_invoke(jl_code_instance_t *ci, uint8_t *specsigflags, jl_callptr_t *invoke, void **specptr, int waitcompile) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *match, size_t world, size_t min_valid, size_t max_valid, int mt_cache); JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst_uninit(jl_method_instance_t *mi, jl_value_t *owner); JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( @@ -683,6 +684,7 @@ JL_DLLEXPORT const char *jl_debuginfo_name(jl_value_t *func) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); +JL_DLLEXPORT int jl_add_entrypoint(jl_tupletype_t *types); jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); jl_value_t *jl_code_or_ci_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); @@ -853,6 +855,7 @@ extern htable_t jl_current_modules JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module JL_GLOBALLY_ROOTED; extern jl_genericmemory_t *jl_global_roots_list JL_GLOBALLY_ROOTED; extern jl_genericmemory_t *jl_global_roots_keyset JL_GLOBALLY_ROOTED; +extern arraylist_t *jl_entrypoint_mis; JL_DLLEXPORT int jl_is_globally_rooted(jl_value_t *val JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert) JL_GLOBALLY_ROOTED; @@ -1902,7 +1905,7 @@ JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncod int32_t *func_idx, int32_t *specfunc_idx); JL_DLLIMPORT void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, jl_method_instance_t **linfos, size_t n); - +JL_DLLIMPORT void jl_get_llvm_mis(void *native_code, arraylist_t* MIs); JL_DLLIMPORT void jl_init_codegen(void); JL_DLLIMPORT void jl_teardown_codegen(void) JL_NOTSAFEPOINT; JL_DLLIMPORT int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; diff --git a/src/module.c b/src/module.c index 96d94049cff13..a6c05d279f5b0 100644 --- a/src/module.c +++ b/src/module.c @@ -856,7 +856,7 @@ JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var) return kind == BINDING_KIND_DECLARED || !jl_bkind_is_some_guard(kind); } -static uint_t bindingkey_hash(size_t idx, jl_value_t *data) +uint_t bindingkey_hash(size_t idx, jl_value_t *data) { 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; diff --git a/src/precompile.c b/src/precompile.c index c40e867ea699e..5088d45a5ad74 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -116,14 +116,16 @@ JL_DLLEXPORT void jl_write_compiler_output(void) if (f) { jl_array_ptr_1d_push(jl_module_init_order, m); int setting = jl_get_module_compile((jl_module_t*)m); - if (setting != JL_OPTIONS_COMPILE_OFF && - setting != JL_OPTIONS_COMPILE_MIN) { + if ((setting != JL_OPTIONS_COMPILE_OFF && (jl_options.trim || + (setting != JL_OPTIONS_COMPILE_MIN)))) { // TODO: this would be better handled if moved entirely to jl_precompile // since it's a slightly duplication of effort jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f); JL_GC_PUSH1(&tt); tt = jl_apply_tuple_type_v(&tt, 1); jl_compile_hint((jl_tupletype_t*)tt); + if (jl_options.trim) + jl_add_entrypoint((jl_tupletype_t*)tt); JL_GC_POP(); } } @@ -188,6 +190,10 @@ JL_DLLEXPORT void jl_write_compiler_output(void) jl_printf(JL_STDERR, "\n ** incremental compilation may be broken for this module **\n\n"); } } + if (jl_options.trim) { + exit(0); // Some finalizers need to run and we've blown up the bindings table + // TODO: Is this still needed + } JL_GC_POP(); jl_gc_enable_finalizers(ct, 1); } diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 5a4f599d1f0eb..a78d1e66dbb51 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -321,3 +321,83 @@ static void *jl_precompile_worklist(jl_array_t *worklist, jl_array_t *extext_met JL_GC_POP(); return native_code; } + +static int enq_ccallable_entrypoints_(jl_typemap_entry_t *def, void *closure) +{ + jl_method_t *m = def->func.method; + if (m->external_mt) + return 1; + if (m->ccallable) + jl_add_entrypoint((jl_tupletype_t*)jl_svecref(m->ccallable, 1)); + return 1; +} + +static int enq_ccallable_entrypoints(jl_methtable_t *mt, void *env) +{ + return jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), enq_ccallable_entrypoints_, env); +} + +JL_DLLEXPORT void jl_add_ccallable_entrypoints(void) +{ + jl_foreach_reachable_mtable(enq_ccallable_entrypoints, NULL); +} + +static void *jl_precompile_trimmed(size_t world) +{ + // array of MethodInstances and ccallable aliases to include in the output + jl_array_t *m = jl_alloc_vec_any(0); + jl_value_t *ccallable = NULL; + JL_GC_PUSH2(&m, &ccallable); + jl_method_instance_t *mi; + while (1) + { + mi = (jl_method_instance_t*)arraylist_pop(jl_entrypoint_mis); + if (mi == NULL) + break; + assert(jl_is_method_instance(mi)); + + jl_array_ptr_1d_push(m, (jl_value_t*)mi); + ccallable = (jl_value_t *)mi->def.method->ccallable; + if (ccallable) + jl_array_ptr_1d_push(m, ccallable); + } + + jl_cgparams_t params = jl_default_cgparams; + params.trim = jl_options.trim; + void *native_code = jl_create_native(m, NULL, ¶ms, 0, /* imaging */ 1, 0, + world); + JL_GC_POP(); + return native_code; +} + +static void jl_rebuild_methtables(arraylist_t* MIs, htable_t* mtables) +{ + size_t i; + for (i = 0; i < MIs->len; i++) { + jl_method_instance_t *mi = (jl_method_instance_t*)MIs->items[i]; + jl_method_t *m = mi->def.method; + jl_methtable_t *old_mt = jl_method_get_table(m); + if ((jl_value_t *)old_mt == jl_nothing) + continue; + jl_sym_t *name = old_mt->name; + if (!ptrhash_has(mtables, old_mt)) + ptrhash_put(mtables, old_mt, jl_new_method_table(name, m->module)); + jl_methtable_t *mt = (jl_methtable_t*)ptrhash_get(mtables, old_mt); + size_t world = jl_atomic_load_acquire(&jl_world_counter); + jl_value_t * lookup = jl_methtable_lookup(mt, m->sig, world); + // Check if the method is already in the new table, if not then insert it there + if (lookup == jl_nothing || (jl_method_t*)lookup != m) { + //TODO: should this be a function like unsafe_insert_method? + size_t min_world = jl_atomic_load_relaxed(&m->primary_world); + size_t max_world = jl_atomic_load_relaxed(&m->deleted_world); + jl_atomic_store_relaxed(&m->primary_world, ~(size_t)0); + jl_atomic_store_relaxed(&m->deleted_world, 1); + jl_typemap_entry_t *newentry = jl_method_table_add(mt, m, NULL); + jl_atomic_store_relaxed(&m->primary_world, min_world); + jl_atomic_store_relaxed(&m->deleted_world, max_world); + jl_atomic_store_relaxed(&newentry->min_world, min_world); + jl_atomic_store_relaxed(&newentry->max_world, max_world); + } + } + +} diff --git a/src/staticdata.c b/src/staticdata.c index 363aa46b62221..f54cc9692eaea 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -363,6 +363,9 @@ static void *to_seroder_entry(size_t idx) return (void*)((char*)HT_NOTFOUND + 1 + idx); } +static htable_t new_methtables; +static size_t precompilation_world; + static int ptr_cmp(const void *l, const void *r) { uintptr_t left = *(const uintptr_t*)l; @@ -770,22 +773,41 @@ static uintptr_t jl_fptr_id(void *fptr) #define jl_queue_for_serialization(s, v) jl_queue_for_serialization_((s), (jl_value_t*)(v), 1, 0) static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED; - static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_t *m) JL_GC_DISABLED { jl_queue_for_serialization(s, m->name); jl_queue_for_serialization(s, m->parent); - jl_queue_for_serialization(s, jl_atomic_load_relaxed(&m->bindings)); + if (jl_options.trim) { + jl_queue_for_serialization_(s, (jl_value_t*)jl_atomic_load_relaxed(&m->bindings), 0, 1); + } else { + jl_queue_for_serialization(s, jl_atomic_load_relaxed(&m->bindings)); + } jl_queue_for_serialization(s, jl_atomic_load_relaxed(&m->bindingkeyset)); - if (jl_options.strip_metadata) { + if (jl_options.strip_metadata || jl_options.trim) { jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings); for (size_t i = 0; i < jl_svec_len(table); i++) { jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - jl_sym_t *name = b->globalref->name; - if (name == jl_docmeta_sym && jl_get_binding_value(b)) - record_field_change((jl_value_t**)&b->value, jl_nothing); + if (jl_options.strip_metadata) { + jl_sym_t *name = b->globalref->name; + if (name == jl_docmeta_sym && jl_get_binding_value(b)) + record_field_change((jl_value_t**)&b->value, jl_nothing); + } + if (jl_options.trim) { + jl_value_t *val = jl_get_binding_value(b); + // keep binding objects that are defined and ... + if (val && + // ... point to modules ... + (jl_is_module(val) || + // ... or point to __init__ methods ... + !strcmp(jl_symbol_name(b->globalref->name), "__init__") || + // ... or point to Base functions accessed by the runtime + (m == jl_base_module && (!strcmp(jl_symbol_name(b->globalref->name), "wait") || + !strcmp(jl_symbol_name(b->globalref->name), "task_done_hook"))))) { + jl_queue_for_serialization(s, b); + } + } } } @@ -944,6 +966,23 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ jl_queue_for_serialization_(s, get_replaceable_field((jl_value_t**)&bpart->next, 0), 1, immediate); } else if (layout->nfields > 0) { + if (jl_options.trim) { + if (jl_is_method(v)) { + jl_method_t *m = (jl_method_t *)v; + if (jl_is_svec(jl_atomic_load_relaxed(&m->specializations))) + jl_queue_for_serialization_(s, (jl_value_t*)jl_atomic_load_relaxed(&m->specializations), 0, 1); + } + else if (jl_typetagis(v, jl_typename_type)) { + jl_typename_t *tn = (jl_typename_t*)v; + if (tn->mt != NULL && !tn->mt->frozen) { + jl_methtable_t * new_methtable = (jl_methtable_t *)ptrhash_get(&new_methtables, tn->mt); + if (new_methtable != HT_NOTFOUND) + record_field_change((jl_value_t **)&tn->mt, (jl_value_t*)new_methtable); + else + record_field_change((jl_value_t **)&tn->mt, NULL); + } + } + } char *data = (char*)jl_data_ptr(v); size_t i, np = layout->npointers; for (i = 0; i < np; i++) { @@ -989,6 +1028,7 @@ done_fields: ; } } + static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED { if (!jl_needs_serialization(s, v)) @@ -2407,6 +2447,53 @@ static void jl_prune_type_cache_linear(jl_svec_t *cache) jl_svecset(cache, ins++, jl_nothing); } +uint_t bindingkey_hash(size_t idx, jl_value_t *data); + +static void jl_prune_module_bindings(jl_module_t * m) JL_GC_DISABLED +{ + jl_svec_t * bindings = jl_atomic_load_relaxed(&m->bindings); + size_t l = jl_svec_len(bindings), i; + arraylist_t bindings_list; + arraylist_new(&bindings_list, 0); + if (l == 0) + return; + for (i = 0; i < l; i++) { + jl_value_t *ti = jl_svecref(bindings, i); + if (ti == jl_nothing) + continue; + jl_binding_t *ref = ((jl_binding_t*)ti); + if (!((ptrhash_get(&serialization_order, ref) == HT_NOTFOUND) && + (ptrhash_get(&serialization_order, ref->globalref) == HT_NOTFOUND))) { + jl_svecset(bindings, i, jl_nothing); + arraylist_push(&bindings_list, ref); + } + } + jl_genericmemory_t* bindingkeyset = jl_atomic_load_relaxed(&m->bindingkeyset); + _Atomic(jl_genericmemory_t*)bindingkeyset2; + jl_atomic_store_relaxed(&bindingkeyset2,(jl_genericmemory_t*)jl_an_empty_memory_any); + jl_svec_t *bindings2 = jl_alloc_svec_uninit(bindings_list.len); + for (i = 0; i < bindings_list.len; i++) { + jl_binding_t *ref = (jl_binding_t*)bindings_list.items[i]; + jl_svecset(bindings2, i, ref); + jl_smallintset_insert(&bindingkeyset2, (jl_value_t*)m, bindingkey_hash, i, (jl_value_t*)bindings2); + } + void *idx = ptrhash_get(&serialization_order, bindings); + assert(idx != HT_NOTFOUND && idx != (void*)(uintptr_t)-1); + assert(serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] == bindings); + ptrhash_put(&serialization_order, bindings2, idx); + serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] = bindings2; + + idx = ptrhash_get(&serialization_order, bindingkeyset); + assert(idx != HT_NOTFOUND && idx != (void*)(uintptr_t)-1); + assert(serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] == bindingkeyset); + ptrhash_put(&serialization_order, jl_atomic_load_relaxed(&bindingkeyset2), idx); + serialization_queue.items[(char*)idx - 1 - (char*)HT_NOTFOUND] = jl_atomic_load_relaxed(&bindingkeyset2); + jl_atomic_store_relaxed(&m->bindings, bindings2); + jl_atomic_store_relaxed(&m->bindingkeyset, jl_atomic_load_relaxed(&bindingkeyset2)); + jl_gc_wb(m, bindings2); + jl_gc_wb(m, jl_atomic_load_relaxed(&bindingkeyset2)); +} + static void strip_slotnames(jl_array_t *slotnames) { // replace slot names with `?`, except unused_sym since the compiler looks at it @@ -2473,7 +2560,7 @@ static int strip_all_codeinfos__(jl_typemap_entry_t *def, void *_env) if (m->source) { int stripped_ir = 0; if (jl_options.strip_ir) { - int should_strip_ir = 0; + int should_strip_ir = jl_options.trim; if (!should_strip_ir) { if (jl_atomic_load_relaxed(&m->unspecialized)) { jl_code_instance_t *unspec = jl_atomic_load_relaxed(&jl_atomic_load_relaxed(&m->unspecialized)->cache); @@ -2675,8 +2762,46 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // strip metadata and IR when requested if (jl_options.strip_metadata || jl_options.strip_ir) jl_strip_all_codeinfos(); + // collect needed methods and replace method tables that are in the tags array + htable_new(&new_methtables, 0); + arraylist_t MIs; + arraylist_new(&MIs, 0); + arraylist_t gvars; + arraylist_new(&gvars, 0); + arraylist_t external_fns; + arraylist_new(&external_fns, 0); int en = jl_gc_enable(0); + if (native_functions) { + jl_get_llvm_gvs(native_functions, &gvars); + jl_get_llvm_external_fns(native_functions, &external_fns); + if (jl_options.trim) + jl_get_llvm_mis(native_functions, &MIs); + } + if (jl_options.trim) { + jl_rebuild_methtables(&MIs, &new_methtables); + jl_methtable_t *mt = (jl_methtable_t *)ptrhash_get(&new_methtables, jl_type_type_mt); + JL_GC_PROMISE_ROOTED(mt); + if (mt != HT_NOTFOUND) + jl_type_type_mt = mt; + else + jl_type_type_mt = jl_new_method_table(jl_type_type_mt->name, jl_type_type_mt->module); + + mt = (jl_methtable_t *)ptrhash_get(&new_methtables, jl_kwcall_mt); + JL_GC_PROMISE_ROOTED(mt); + if (mt != HT_NOTFOUND) + jl_kwcall_mt = mt; + else + jl_kwcall_mt = jl_new_method_table(jl_kwcall_mt->name, jl_kwcall_mt->module); + + mt = (jl_methtable_t *)ptrhash_get(&new_methtables, jl_nonfunction_mt); + JL_GC_PROMISE_ROOTED(mt); + if (mt != HT_NOTFOUND) + jl_nonfunction_mt = mt; + else + jl_nonfunction_mt = jl_new_method_table(jl_nonfunction_mt->name, jl_nonfunction_mt->module); + } + nsym_tag = 0; htable_new(&symbol_table, 0); htable_new(&fptr_to_id, sizeof(id_to_fptrs) / sizeof(*id_to_fptrs)); @@ -2722,14 +2847,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, htable_new(&s.callers_with_edges, 0); jl_value_t **const*const tags = get_tags(); // worklist == NULL ? get_tags() : NULL; - arraylist_t gvars; - arraylist_t external_fns; - arraylist_new(&gvars, 0); - arraylist_new(&external_fns, 0); - if (native_functions) { - jl_get_llvm_gvs(native_functions, &gvars); - jl_get_llvm_external_fns(native_functions, &external_fns); - } if (worklist == NULL) { // empty!(Core.ARGS) @@ -2788,6 +2905,8 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // step 1.2: ensure all gvars are part of the sysimage too record_gvars(&s, &gvars); record_external_fns(&s, &external_fns); + if (jl_options.trim) + record_gvars(&s, &MIs); jl_serialize_reachable(&s); // step 1.3: prune (garbage collect) special weak references from the jl_global_roots_list if (worklist == NULL) { @@ -2808,8 +2927,30 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // step 1.4: prune (garbage collect) some special weak references from // built-in type caches too for (i = 0; i < serialization_queue.len; i++) { - jl_typename_t *tn = (jl_typename_t*)serialization_queue.items[i]; - if (jl_is_typename(tn)) { + jl_value_t *v = (jl_value_t*)serialization_queue.items[i]; + if (jl_options.trim) { + if (jl_is_method(v)){ + jl_method_t *m = (jl_method_t*)v; + jl_value_t *specializations_ = jl_atomic_load_relaxed(&m->specializations); + if (!jl_is_svec(specializations_)) + continue; + + jl_svec_t *specializations = (jl_svec_t *)specializations_; + size_t l = jl_svec_len(specializations), i; + for (i = 0; i < l; i++) { + jl_value_t *mi = jl_svecref(specializations, i); + if (mi == jl_nothing) + continue; + if (ptrhash_get(&serialization_order, mi) == HT_NOTFOUND) + jl_svecset(specializations, i, jl_nothing); + } + } else if (jl_is_module(v)) { + jl_prune_module_bindings((jl_module_t*)v); + } + } + // Not else + if (jl_is_typename(v)) { + jl_typename_t *tn = (jl_typename_t*)v; jl_atomic_store_relaxed(&tn->cache, jl_prune_type_cache_hash(jl_atomic_load_relaxed(&tn->cache))); jl_gc_wb(tn, jl_atomic_load_relaxed(&tn->cache)); @@ -2918,7 +3059,9 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_write_value(&s, global_roots_keyset); jl_write_value(&s, s.ptls->root_task->tls); write_uint32(f, jl_get_gs_ctr()); - write_uint(f, jl_atomic_load_acquire(&jl_world_counter)); + size_t world = jl_atomic_load_acquire(&jl_world_counter); + // assert(world == precompilation_world); // This triggers on a normal build of julia + write_uint(f, world); write_uint(f, jl_typeinf_world); } else { @@ -2971,6 +3114,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, htable_free(&nullptrs); htable_free(&symbol_table); htable_free(&fptr_to_id); + htable_free(&new_methtables); nsym_tag = 0; jl_gc_enable(en); @@ -3000,6 +3144,10 @@ static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_a JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *worklist, bool_t emit_split, ios_t **s, ios_t **z, jl_array_t **udeps, int64_t *srctextpos) { + if (jl_options.strip_ir || jl_options.trim) { + // make sure this is precompiled for jl_foreach_reachable_mtable + jl_get_loaded_modules(); + } jl_gc_collect(JL_GC_FULL); jl_gc_collect(JL_GC_INCREMENTAL); // sweep finalizers JL_TIMING(SYSIMG_DUMP, SYSIMG_DUMP); @@ -3049,7 +3197,11 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } } else if (_native_data != NULL) { - *_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL); + precompilation_world = jl_atomic_load_acquire(&jl_world_counter); + if (jl_options.trim) + *_native_data = jl_precompile_trimmed(precompilation_world); + else + *_native_data = jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL); } // Make sure we don't run any Julia code concurrently after this point diff --git a/src/support/arraylist.h b/src/support/arraylist.h index 6ad2f0e2f28c9..a83bd2808756c 100644 --- a/src/support/arraylist.h +++ b/src/support/arraylist.h @@ -20,11 +20,11 @@ typedef struct { void *_space[AL_N_INLINE]; } arraylist_t; -arraylist_t *arraylist_new(arraylist_t *a, size_t size) JL_NOTSAFEPOINT; -void arraylist_free(arraylist_t *a) JL_NOTSAFEPOINT; +JL_DLLEXPORT arraylist_t *arraylist_new(arraylist_t *a, size_t size) JL_NOTSAFEPOINT; +JL_DLLEXPORT void arraylist_free(arraylist_t *a) JL_NOTSAFEPOINT; -void arraylist_push(arraylist_t *a, void *elt) JL_NOTSAFEPOINT; -void *arraylist_pop(arraylist_t *a) JL_NOTSAFEPOINT; +JL_DLLEXPORT void arraylist_push(arraylist_t *a, void *elt) JL_NOTSAFEPOINT; +JL_DLLEXPORT void *arraylist_pop(arraylist_t *a) JL_NOTSAFEPOINT; JL_DLLEXPORT void arraylist_grow(arraylist_t *a, size_t n) JL_NOTSAFEPOINT; typedef struct { @@ -34,11 +34,12 @@ typedef struct { void *_space[SMALL_AL_N_INLINE]; } small_arraylist_t; -small_arraylist_t *small_arraylist_new(small_arraylist_t *a, uint32_t size) JL_NOTSAFEPOINT; -void small_arraylist_free(small_arraylist_t *a) JL_NOTSAFEPOINT; -void small_arraylist_push(small_arraylist_t *a, void *elt) JL_NOTSAFEPOINT; -void *small_arraylist_pop(small_arraylist_t *a) JL_NOTSAFEPOINT; +JL_DLLEXPORT small_arraylist_t *small_arraylist_new(small_arraylist_t *a, uint32_t size) JL_NOTSAFEPOINT; +JL_DLLEXPORT void small_arraylist_free(small_arraylist_t *a) JL_NOTSAFEPOINT; + +JL_DLLEXPORT void small_arraylist_push(small_arraylist_t *a, void *elt) JL_NOTSAFEPOINT; +JL_DLLEXPORT void *small_arraylist_pop(small_arraylist_t *a) JL_NOTSAFEPOINT; JL_DLLEXPORT void small_arraylist_grow(small_arraylist_t *a, uint32_t n) JL_NOTSAFEPOINT; #ifdef __cplusplus diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index 413b7866c5444..3c15630091162 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -159,7 +159,7 @@ function check() interface = USE_BLAS64 ? :ilp64 : :lp64 if !any(lib.interface == interface for lib in config.loaded_libs) interfacestr = uppercase(string(interface)) - @error("No loaded BLAS libraries were built with $interfacestr support.") + println(Core.stderr, "No loaded BLAS libraries were built with $interfacestr support.") exit(1) end end diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index 606ddedbe1343..81d10f930c8c5 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -17,7 +17,7 @@ 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")) + println(Core.stderr, string("Warning: [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 @@ -132,7 +132,7 @@ struct LBTConfig if str_ptr != C_NULL push!(exported_symbols, unsafe_string(str_ptr)) else - @error("NULL string in lbt_config.exported_symbols[$(sym_idx)]") + println(Core.stderr, "Error: NULL string in lbt_config.exported_symbols[$(sym_idx)]") end end diff --git a/test/Makefile b/test/Makefile index 1b9cb377c943d..6ebdd3c764fd5 100644 --- a/test/Makefile +++ b/test/Makefile @@ -24,6 +24,8 @@ EMBEDDING_ARGS := "JULIA=$(JULIA_EXECUTABLE)" "BIN=$(SRCDIR)/embedding" "CC=$(CC GCEXT_ARGS := "JULIA=$(JULIA_EXECUTABLE)" "BIN=$(SRCDIR)/gcext" "CC=$(CC)" +TRIMMING_ARGS := "JULIA=$(JULIA_EXECUTABLE)" "BIN=$(JULIAHOME)/usr/bin" "CC=$(CC)" + default: $(TESTS): @@ -66,6 +68,9 @@ embedding: gcext: @$(MAKE) -C $(SRCDIR)/$@ check $(GCEXT_ARGS) +trimming: + @$(MAKE) -C $(SRCDIR)/$@ check $(TRIMMING_ARGS) + clangsa: @$(MAKE) -C $(SRCDIR)/$@ @@ -73,5 +78,6 @@ clean: @$(MAKE) -C embedding $@ $(EMBEDDING_ARGS) @$(MAKE) -C gcext $@ $(GCEXT_ARGS) @$(MAKE) -C llvmpasses $@ + @$(MAKE) -C trimming $@ $(TRIMMING_ARGS) -.PHONY: $(TESTS) $(addprefix revise-, $(TESTS)) relocatedepot revise-relocatedepot embedding gcext clangsa clean +.PHONY: $(TESTS) $(addprefix revise-, $(TESTS)) relocatedepot revise-relocatedepot embedding gcext trimming clangsa clean diff --git a/test/trimming/Makefile b/test/trimming/Makefile new file mode 100644 index 0000000000000..c6e105d637013 --- /dev/null +++ b/test/trimming/Makefile @@ -0,0 +1,55 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# This Makefile template requires the following variables to be set +# in the environment or on the command-line: +# JULIA: path to julia[.exe] executable +# BIN: binary build directory + +ifndef JULIA + $(error "Please pass JULIA=[path of target julia binary], or set as environment variable!") +endif +ifndef BIN + $(error "Please pass BIN=[path of build directory], or set as environment variable!") +endif + +#============================================================================= +# location of test source +SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +JULIAHOME := $(abspath $(SRCDIR)/../..) +BUILDSCRIPT := $(BIN)/../share/julia/juliac-buildscript.jl +include $(JULIAHOME)/Make.inc + +# get the executable suffix, if any +EXE := $(suffix $(abspath $(JULIA))) + +# get compiler and linker flags. (see: `contrib/julia-config.jl`) +JULIA_CONFIG := $(JULIA) -e 'include(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "julia-config.jl"))' -- +CPPFLAGS_ADD := +CFLAGS_ADD = $(shell $(JULIA_CONFIG) --cflags) +LDFLAGS_ADD = -lm $(shell $(JULIA_CONFIG) --ldflags --ldlibs) -ljulia-internal + +#============================================================================= + +release: hello$(EXE) + +hello.o: $(SRCDIR)/hello.jl $(BUILDSCRIPT) + $(JULIA) -t 1 -J $(BIN)/../lib/julia/sys.so --startup-file=no --history-file=no --output-o $@ --output-incremental=no --strip-ir --strip-metadata --trim $(BUILDSCRIPT) $(SRCDIR)/hello.jl --output-exe true + +init.o: $(SRCDIR)/init.c + $(CC) -c -o $@ $< $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS) + +hello$(EXE): hello.o init.o + $(CC) -o $@ $(WHOLE_ARCHIVE) hello.o $(NO_WHOLE_ARCHIVE) init.o $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS) $(LDFLAGS_ADD) $(LDFLAGS) + +check: hello$(EXE) + $(JULIA) --depwarn=error $(SRCDIR)/../runtests.jl $(SRCDIR)/trimming + +clean: + -rm -f hello$(EXE) init.o hello.o + +.PHONY: release clean check + +# Makefile debugging trick: +# call print-VARIABLE to see the runtime value of any variable +print-%: + @echo '$*=$($*)' diff --git a/test/trimming/hello.jl b/test/trimming/hello.jl new file mode 100644 index 0000000000000..307bf820f325b --- /dev/null +++ b/test/trimming/hello.jl @@ -0,0 +1,6 @@ +module MyApp +Base.@ccallable function main()::Cint + println(Core.stdout, "Hello, world!") + return 0 +end +end diff --git a/test/trimming/init.c b/test/trimming/init.c new file mode 100644 index 0000000000000..ea1b02f8e5c8f --- /dev/null +++ b/test/trimming/init.c @@ -0,0 +1,9 @@ +#include + +__attribute__((constructor)) void static_init(void) +{ + if (jl_is_initialized()) + return; + julia_init(JL_IMAGE_IN_MEMORY); + jl_exception_clear(); +} diff --git a/test/trimming/trimming.jl b/test/trimming/trimming.jl new file mode 100644 index 0000000000000..dfacae7f8e531 --- /dev/null +++ b/test/trimming/trimming.jl @@ -0,0 +1,7 @@ +using Test + +exe_path = joinpath(@__DIR__, "hello"*splitext(Base.julia_exename())[2]) + +@test readchomp(`$exe_path`) == "Hello, world!" + +@test filesize(exe_path) < filesize(unsafe_string(Base.JLOptions().image_file))/10 From 17445fe752b7b99633ca306af0981baca9f66bda Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sun, 29 Sep 2024 11:41:59 +0200 Subject: [PATCH 274/548] fix rawbigints OOB issues (#55917) Fixes issues introduced in #50691 and found in #55906: * use `@inbounds` and `@boundscheck` macros in rawbigints, for catching OOB with `--check-bounds=yes` * fix OOB in `truncate` --- base/rawbigints.jl | 31 ++++++++++++++++++++++--------- test/mpfr.jl | 9 +++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/base/rawbigints.jl b/base/rawbigints.jl index 6508bea05be0f..a9bb18e163e2d 100644 --- a/base/rawbigints.jl +++ b/base/rawbigints.jl @@ -21,14 +21,21 @@ reversed_index(n::Int, i::Int) = n - i - 1 reversed_index(x, i::Int, v::Val) = reversed_index(elem_count(x, v), i)::Int split_bit_index(x::RawBigInt, i::Int) = divrem(i, word_length(x), RoundToZero) +function get_elem_words_raw(x::RawBigInt{T}, i::Int) where {T} + @boundscheck if (i < 0) || (elem_count(x, Val(:words)) ≤ i) + throw(BoundsError(x, i)) + end + d = x.d + j = i + 1 + (GC.@preserve d unsafe_load(Ptr{T}(pointer(d)), j))::T +end + """ `i` is the zero-based index of the wanted word in `x`, starting from the less significant words. """ -function get_elem(x::RawBigInt{T}, i::Int, ::Val{:words}, ::Val{:ascending}) where {T} - # `i` must be non-negative and less than `x.word_count` - d = x.d - (GC.@preserve d unsafe_load(Ptr{T}(pointer(d)), i + 1))::T +function get_elem(x::RawBigInt, i::Int, ::Val{:words}, ::Val{:ascending}) + @inbounds @inline get_elem_words_raw(x, i) end function get_elem(x, i::Int, v::Val, ::Val{:descending}) @@ -96,7 +103,8 @@ end """ Returns an integer of type `R`, consisting of the `len` most -significant bits of `x`. +significant bits of `x`. If there are less than `len` bits in `x`, +the least significant bits are zeroed. """ function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} ret = zero(R) @@ -104,17 +112,22 @@ function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} word_count, bit_count_in_word = split_bit_index(x, len) k = word_length(x) vals = (Val(:words), Val(:descending)) + lenx = elem_count(x, first(vals)) for w ∈ 0:(word_count - 1) ret <<= k - word = get_elem(x, w, vals...) - ret |= R(word) + if w < lenx + word = get_elem(x, w, vals...) + ret |= R(word) + end end if !iszero(bit_count_in_word) ret <<= bit_count_in_word - wrd = get_elem(x, word_count, vals...) - ret |= R(wrd >>> (k - bit_count_in_word)) + if word_count < lenx + wrd = get_elem(x, word_count, vals...) + ret |= R(wrd >>> (k - bit_count_in_word)) + end end end ret::R diff --git a/test/mpfr.jl b/test/mpfr.jl index 9a9698ba72c2c..63da732df1c09 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -1088,3 +1088,12 @@ end clear_flags() end end + +@testset "RawBigInt truncation OOB read" begin + @testset "T: $T" for T ∈ (UInt8, UInt16, UInt32, UInt64, UInt128) + v = Base.RawBigInt{T}("a"^sizeof(T), 1) + @testset "bit_count: $bit_count" for bit_count ∈ (0:10:80) + @test Base.truncated(UInt128, v, bit_count) isa Any + end + end +end From 4da067167fc414ea4329be3b4fdc516914e102cd Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 30 Sep 2024 12:52:59 +0200 Subject: [PATCH 275/548] prevent loading other extensions when precompiling an extension (#55589) The current way of loading extensions when precompiling an extension very easily leads to cycles. For example, if you have more than one extension and you happen to transitively depend on the triggers of one of your extensions you will immediately hit a cycle where the extensions will try to load each other indefinitely. This is an issue because you cannot directly influence your transitive dependency graph so from this p.o.v the current system of loading extension is "unsound". The test added here checks this scenario and we can now precompile and load it without any warnings or issues. Would have made https://github.com/JuliaLang/julia/issues/55517 a non issue. Fixes https://github.com/JuliaLang/julia/issues/55557 --------- Co-authored-by: KristofferC --- base/loading.jl | 16 ++++--- base/precompilation.jl | 47 +------------------ test/loading.jl | 13 +++++ .../Extensions/CyclicExtensions/Manifest.toml | 21 +++++++++ .../Extensions/CyclicExtensions/Project.toml | 13 +++++ .../Extensions/CyclicExtensions/ext/ExtA.jl | 6 +++ .../Extensions/CyclicExtensions/ext/ExtB.jl | 6 +++ .../CyclicExtensions/src/CyclicExtensions.jl | 7 +++ 8 files changed, 76 insertions(+), 53 deletions(-) create mode 100644 test/project/Extensions/CyclicExtensions/Manifest.toml create mode 100644 test/project/Extensions/CyclicExtensions/Project.toml create mode 100644 test/project/Extensions/CyclicExtensions/ext/ExtA.jl create mode 100644 test/project/Extensions/CyclicExtensions/ext/ExtB.jl create mode 100644 test/project/Extensions/CyclicExtensions/src/CyclicExtensions.jl diff --git a/base/loading.jl b/base/loading.jl index cf7e41a0b5b2b..fbf6bb4af50aa 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1387,7 +1387,9 @@ function run_module_init(mod::Module, i::Int=1) end function run_package_callbacks(modkey::PkgId) - run_extension_callbacks(modkey) + if !precompiling_extension + run_extension_callbacks(modkey) + end assert_havelock(require_lock) unlock(require_lock) try @@ -2843,7 +2845,7 @@ end const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, - concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout) + concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) @@ -2912,7 +2914,7 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: write(io.in, """ empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated Base.track_nested_precomp($precomp_stack) - Base.precompiling_extension = $(loading_extension) + Base.precompiling_extension = $(loading_extension | isext) Base.precompiling_package = true Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) @@ -2970,18 +2972,18 @@ This can be used to reduce package load times. Cache files are stored in `DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) for important notes. """ -function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}()) +function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) @nospecialize internal_stderr internal_stdout path = locate_package(pkg) path === nothing && throw(ArgumentError("$(repr("text/plain", pkg)) not found during precompilation")) - return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons) + return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, isext) end const MAX_NUM_PRECOMPILE_FILES = Ref(10) function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout, keep_loaded_modules::Bool = true; flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), - reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}()) + reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) @nospecialize internal_stderr internal_stdout # decide where to put the resulting cache file @@ -3021,7 +3023,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in close(tmpio_o) close(tmpio_so) end - p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout) + p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout, isext) if success(p) if cache_objects diff --git a/base/precompilation.jl b/base/precompilation.jl index d3f076633f386..b351ce67cfbad 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -435,51 +435,6 @@ function precompilepkgs(pkgs::Vector{String}=String[]; # consider exts of direct deps to be direct deps so that errors are reported append!(direct_deps, keys(filter(d->last(d) in keys(env.project_deps), exts))) - # An extension effectively depends on another extension if it has all the the - # dependencies of that other extension - function expand_dependencies(depsmap) - function visit!(visited, node, all_deps) - if node in visited - return - end - push!(visited, node) - for dep in get(Set{Base.PkgId}, depsmap, node) - if !(dep in all_deps) - push!(all_deps, dep) - visit!(visited, dep, all_deps) - end - end - end - - depsmap_transitive = Dict{Base.PkgId, Set{Base.PkgId}}() - for package in keys(depsmap) - # Initialize a set to keep track of all dependencies for 'package' - all_deps = Set{Base.PkgId}() - visited = Set{Base.PkgId}() - visit!(visited, package, all_deps) - # Update depsmap with the complete set of dependencies for 'package' - depsmap_transitive[package] = all_deps - end - return depsmap_transitive - end - - depsmap_transitive = expand_dependencies(depsmap) - - for (_, extensions_1) in pkg_exts_map - for extension_1 in extensions_1 - deps_ext_1 = depsmap_transitive[extension_1] - for (_, extensions_2) in pkg_exts_map - for extension_2 in extensions_2 - extension_1 == extension_2 && continue - deps_ext_2 = depsmap_transitive[extension_2] - if issubset(deps_ext_2, deps_ext_1) - push!(depsmap[extension_1], extension_2) - end - end - end - end - end - @debug "precompile: deps collected" # this loop must be run after the full depsmap has been populated for (pkg, pkg_exts) in pkg_exts_map @@ -852,7 +807,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do Base.with_logger(Base.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session - Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags) + Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags, isext = haskey(exts, pkg)) end end if ret isa Base.PrecompilableError diff --git a/test/loading.jl b/test/loading.jl index bdaca7f9dc69e..b66fd632f23fa 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1155,6 +1155,19 @@ end finally copy!(LOAD_PATH, old_load_path) end + + # Extension with cycles in dependencies + code = """ + using CyclicExtensions + Base.get_extension(CyclicExtensions, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(CyclicExtensions, :ExtB) isa Module || error("expected extension to load") + CyclicExtensions.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "CyclicExtensions") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) + @test occursin("Hello Cycles!", String(read(cmd))) + finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/CyclicExtensions/Manifest.toml b/test/project/Extensions/CyclicExtensions/Manifest.toml new file mode 100644 index 0000000000000..a506825cf7995 --- /dev/null +++ b/test/project/Extensions/CyclicExtensions/Manifest.toml @@ -0,0 +1,21 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.4" +manifest_format = "2.0" +project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" + +[[deps.ExtDep]] +deps = ["SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.ExtDep2]] +path = "../ExtDep2" +uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/CyclicExtensions/Project.toml b/test/project/Extensions/CyclicExtensions/Project.toml new file mode 100644 index 0000000000000..08d539dcc40ae --- /dev/null +++ b/test/project/Extensions/CyclicExtensions/Project.toml @@ -0,0 +1,13 @@ +name = "CyclicExtensions" +uuid = "17d4f0df-b55c-4714-ac4b-55fa23f7355c" +version = "0.1.0" + +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" + +[weakdeps] +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" + +[extensions] +ExtA = ["SomePackage"] +ExtB = ["SomePackage"] diff --git a/test/project/Extensions/CyclicExtensions/ext/ExtA.jl b/test/project/Extensions/CyclicExtensions/ext/ExtA.jl new file mode 100644 index 0000000000000..fa0c0961633cb --- /dev/null +++ b/test/project/Extensions/CyclicExtensions/ext/ExtA.jl @@ -0,0 +1,6 @@ +module ExtA + +using CyclicExtensions +using SomePackage + +end diff --git a/test/project/Extensions/CyclicExtensions/ext/ExtB.jl b/test/project/Extensions/CyclicExtensions/ext/ExtB.jl new file mode 100644 index 0000000000000..8f6da556d39b8 --- /dev/null +++ b/test/project/Extensions/CyclicExtensions/ext/ExtB.jl @@ -0,0 +1,6 @@ +module ExtB + +using CyclicExtensions +using SomePackage + +end diff --git a/test/project/Extensions/CyclicExtensions/src/CyclicExtensions.jl b/test/project/Extensions/CyclicExtensions/src/CyclicExtensions.jl new file mode 100644 index 0000000000000..f1c2ec2077562 --- /dev/null +++ b/test/project/Extensions/CyclicExtensions/src/CyclicExtensions.jl @@ -0,0 +1,7 @@ +module CyclicExtensions + +using ExtDep + +greet() = print("Hello Cycles!") + +end # module CyclicExtensions From 2a2878c143b87e5184565c895d090aab6e9017e9 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:02:59 -0400 Subject: [PATCH 276/548] TOML: Avoid type-pirating `Base.TOML.Parser` (#55892) Since stdlibs can be duplicated but Base never is, `Base.require_stdlib` makes type piracy even more complicated than it normally would be. To adapt, this changes `TOML.Parser` to be a type defined by the TOML stdlib, so that we can define methods on it without committing type-piracy and avoid problems like Pkg.jl#4017 Resolves https://github.com/JuliaLang/Pkg.jl/issues/4017#issuecomment-2377589989 --- stdlib/TOML/src/TOML.jl | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/stdlib/TOML/src/TOML.jl b/stdlib/TOML/src/TOML.jl index 94d2808c0bc24..b37a5ca83c251 100644 --- a/stdlib/TOML/src/TOML.jl +++ b/stdlib/TOML/src/TOML.jl @@ -25,7 +25,7 @@ module Internals end # https://github.com/JuliaLang/julia/issues/36605 -readstring(f::AbstractString) = isfile(f) ? read(f, String) : error(repr(f), ": No such file") +_readstring(f::AbstractString) = isfile(f) ? read(f, String) : error(repr(f), ": No such file") """ Parser() @@ -36,12 +36,14 @@ explicitly create a `Parser` but instead one directly use use will however reuse some internal data structures which can be beneficial for performance if a larger number of small files are parsed. """ -const Parser = Internals.Parser +struct Parser + _p::Internals.Parser{Dates} +end # Dates-enabled constructors -Parser() = Parser{Dates}() -Parser(io::IO) = Parser{Dates}(io) -Parser(str::String; filepath=nothing) = Parser{Dates}(str; filepath) +Parser() = Parser(Internals.Parser{Dates}()) +Parser(io::IO) = Parser(Internals.Parser{Dates}(io)) +Parser(str::String; filepath=nothing) = Parser(Internals.Parser{Dates}(str; filepath)) """ parsefile(f::AbstractString) @@ -53,9 +55,9 @@ 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(Internals.Parser{Dates}(_readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = - Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) + Internals.parse(Internals.reinit!(p._p, _readstring(f); filepath=abspath(f))) """ tryparsefile(f::AbstractString) @@ -67,9 +69,9 @@ 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(Internals.Parser{Dates}(_readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = - Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) + Internals.tryparse(Internals.reinit!(p._p, _readstring(f); filepath=abspath(f))) """ parse(x::Union{AbstractString, IO}) @@ -80,10 +82,11 @@ Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ +parse(p::Parser) = Internals.parse(p._p) parse(str::AbstractString) = - Internals.parse(Parser(String(str))) + Internals.parse(Internals.Parser{Dates}(String(str))) parse(p::Parser, str::AbstractString) = - Internals.parse(Internals.reinit!(p, String(str))) + Internals.parse(Internals.reinit!(p._p, String(str))) parse(io::IO) = parse(read(io, String)) parse(p::Parser, io::IO) = parse(p, read(io, String)) @@ -96,10 +99,11 @@ Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ +tryparse(p::Parser) = Internals.tryparse(p._p) tryparse(str::AbstractString) = - Internals.tryparse(Parser(String(str))) + Internals.tryparse(Internals.Parser{Dates}(String(str))) tryparse(p::Parser, str::AbstractString) = - Internals.tryparse(Internals.reinit!(p, String(str))) + Internals.tryparse(Internals.reinit!(p._p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) tryparse(p::Parser, io::IO) = tryparse(p, read(io, String)) @@ -131,4 +135,17 @@ supported type. """ const print = Internals.Printer.print +public Parser, parsefile, tryparsefile, parse, tryparse, ParserError, print + +# These methods are private Base interfaces, but we do our best to support them over +# the TOML stdlib types anyway to minimize downstream breakage. +Base.TOMLCache(p::Parser) = Base.TOMLCache(p._p, Dict{String, Base.CachedTOMLDict}()) +Base.TOMLCache(p::Parser, d::Base.CachedTOMLDict) = Base.TOMLCache(p._p, d) +Base.TOMLCache(p::Parser, d::Dict{String, Dict{String, Any}}) = Base.TOMLCache(p._p, d) + +Internals.reinit!(p::Parser, str::String; filepath::Union{Nothing, String}=nothing) = + Internals.reinit!(p._p, str; filepath) +Internals.parse(p::Parser) = Internals.parse(p._p) +Internals.tryparse(p::Parser) = Internals.tryparse(p._p) + end From e500754118c64ecc16836f426c251582fddbffb5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 25 Sep 2024 11:22:23 -0400 Subject: [PATCH 277/548] [FileWatching] fix PollingFileWatcher design and add workaround for a stat bug What started as an innocent fix for a stat bug on Apple (#48667) turned into a full blown investigation into the design problems with the libuv backend for PollingFileWatcher, and writing my own implementation of it instead which could avoid those singled-threaded concurrency bugs. --- base/libuv.jl | 8 +- base/reflection.jl | 3 +- base/stat.jl | 111 +++++------ src/sys.c | 1 - stdlib/FileWatching/src/FileWatching.jl | 240 ++++++++++++++---------- stdlib/FileWatching/test/runtests.jl | 9 +- test/file.jl | 10 + 7 files changed, 215 insertions(+), 167 deletions(-) diff --git a/base/libuv.jl b/base/libuv.jl index 3c9f79dfa7b2c..306854e9f4436 100644 --- a/base/libuv.jl +++ b/base/libuv.jl @@ -26,10 +26,10 @@ for r in uv_req_types @eval const $(Symbol("_sizeof_", lowercase(string(r)))) = uv_sizeof_req($r) end -uv_handle_data(handle) = ccall(:jl_uv_handle_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) -uv_req_data(handle) = ccall(:jl_uv_req_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) -uv_req_set_data(req, data) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Any), req, data) -uv_req_set_data(req, data::Ptr{Cvoid}) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), req, data) +uv_handle_data(handle) = ccall(:uv_handle_get_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) +uv_req_data(handle) = ccall(:uv_req_get_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) +uv_req_set_data(req, data) = ccall(:uv_req_set_data, Cvoid, (Ptr{Cvoid}, Any), req, data) +uv_req_set_data(req, data::Ptr{Cvoid}) = ccall(:uv_handle_set_data, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), req, data) macro handle_as(hand, typ) return quote diff --git a/base/reflection.jl b/base/reflection.jl index fe48b6f9aa6b9..be0209872db34 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -964,7 +964,7 @@ use it in the following manner to summarize information about a struct: julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)]; julia> structinfo(Base.Filesystem.StatStruct) -13-element Vector{Tuple{UInt64, Symbol, Type}}: +14-element Vector{Tuple{UInt64, Symbol, Type}}: (0x0000000000000000, :desc, Union{RawFD, String}) (0x0000000000000008, :device, UInt64) (0x0000000000000010, :inode, UInt64) @@ -978,6 +978,7 @@ julia> structinfo(Base.Filesystem.StatStruct) (0x0000000000000050, :blocks, Int64) (0x0000000000000058, :mtime, Float64) (0x0000000000000060, :ctime, Float64) + (0x0000000000000068, :ioerrno, Int32) ``` """ fieldoffset(x::DataType, idx::Integer) = (@_foldable_meta; ccall(:jl_get_field_offset, Csize_t, (Any, Cint), x, idx)) diff --git a/base/stat.jl b/base/stat.jl index 506b5644dccbc..c6fb239a96404 100644 --- a/base/stat.jl +++ b/base/stat.jl @@ -63,6 +63,7 @@ struct StatStruct blocks :: Int64 mtime :: Float64 ctime :: Float64 + ioerrno :: Int32 end @eval function Base.:(==)(x::StatStruct, y::StatStruct) # do not include `desc` in equality or hash @@ -80,22 +81,23 @@ end end) end -StatStruct() = StatStruct("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -StatStruct(buf::Union{Vector{UInt8},Ptr{UInt8}}) = StatStruct("", buf) -StatStruct(desc::Union{AbstractString, OS_HANDLE}, buf::Union{Vector{UInt8},Ptr{UInt8}}) = StatStruct( +StatStruct() = StatStruct("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Base.UV_ENOENT) +StatStruct(buf::Union{Memory{UInt8},Vector{UInt8},Ptr{UInt8}}, ioerrno::Int32) = StatStruct("", buf, ioerrno) +StatStruct(desc::Union{AbstractString, OS_HANDLE}, buf::Union{Memory{UInt8},Vector{UInt8},Ptr{UInt8}}, ioerrno::Int32) = StatStruct( desc isa OS_HANDLE ? desc : String(desc), - ccall(:jl_stat_dev, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_ino, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_mode, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_nlink, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_uid, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_gid, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_rdev, UInt32, (Ptr{UInt8},), buf), - ccall(:jl_stat_size, UInt64, (Ptr{UInt8},), buf), - ccall(:jl_stat_blksize, UInt64, (Ptr{UInt8},), buf), - ccall(:jl_stat_blocks, UInt64, (Ptr{UInt8},), buf), - ccall(:jl_stat_mtime, Float64, (Ptr{UInt8},), buf), - ccall(:jl_stat_ctime, Float64, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_dev, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_ino, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_mode, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_nlink, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_uid, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_gid, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt32) : ccall(:jl_stat_rdev, UInt32, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt64) : ccall(:jl_stat_size, UInt64, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt64) : ccall(:jl_stat_blksize, UInt64, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(UInt64) : ccall(:jl_stat_blocks, UInt64, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(Float64) : ccall(:jl_stat_mtime, Float64, (Ptr{UInt8},), buf), + ioerrno != 0 ? zero(Float64) : ccall(:jl_stat_ctime, Float64, (Ptr{UInt8},), buf), + ioerrno ) function iso_datetime_with_relative(t, tnow) @@ -130,35 +132,41 @@ end function show_statstruct(io::IO, st::StatStruct, oneline::Bool) print(io, oneline ? "StatStruct(" : "StatStruct for ") show(io, st.desc) - oneline || print(io, "\n ") - print(io, " size: ", st.size, " bytes") - oneline || print(io, "\n") - print(io, " device: ", st.device) - oneline || print(io, "\n ") - print(io, " inode: ", st.inode) - oneline || print(io, "\n ") - print(io, " mode: 0o", string(filemode(st), base = 8, pad = 6), " (", filemode_string(st), ")") - oneline || print(io, "\n ") - print(io, " nlink: ", st.nlink) - oneline || print(io, "\n ") - print(io, " uid: $(st.uid)") - username = getusername(st.uid) - username === nothing || print(io, " (", username, ")") - oneline || print(io, "\n ") - print(io, " gid: ", st.gid) - groupname = getgroupname(st.gid) - groupname === nothing || print(io, " (", groupname, ")") - oneline || print(io, "\n ") - print(io, " rdev: ", st.rdev) - oneline || print(io, "\n ") - print(io, " blksz: ", st.blksize) - oneline || print(io, "\n") - print(io, " blocks: ", st.blocks) - tnow = round(UInt, time()) - oneline || print(io, "\n ") - print(io, " mtime: ", iso_datetime_with_relative(st.mtime, tnow)) - oneline || print(io, "\n ") - print(io, " ctime: ", iso_datetime_with_relative(st.ctime, tnow)) + code = st.ioerrno + if code != 0 + print(io, oneline ? " " : "\n ") + print(io, Base.uverrorname(code), ": ", Base.struverror(code)) + else + oneline || print(io, "\n ") + print(io, " size: ", st.size, " bytes") + oneline || print(io, "\n") + print(io, " device: ", st.device) + oneline || print(io, "\n ") + print(io, " inode: ", st.inode) + oneline || print(io, "\n ") + print(io, " mode: 0o", string(filemode(st), base = 8, pad = 6), " (", filemode_string(st), ")") + oneline || print(io, "\n ") + print(io, " nlink: ", st.nlink) + oneline || print(io, "\n ") + print(io, " uid: $(st.uid)") + username = getusername(st.uid) + username === nothing || print(io, " (", username, ")") + oneline || print(io, "\n ") + print(io, " gid: ", st.gid) + groupname = getgroupname(st.gid) + groupname === nothing || print(io, " (", groupname, ")") + oneline || print(io, "\n ") + print(io, " rdev: ", st.rdev) + oneline || print(io, "\n ") + print(io, " blksz: ", st.blksize) + oneline || print(io, "\n") + print(io, " blocks: ", st.blocks) + tnow = round(UInt, time()) + oneline || print(io, "\n ") + print(io, " mtime: ", iso_datetime_with_relative(st.mtime, tnow)) + oneline || print(io, "\n ") + print(io, " ctime: ", iso_datetime_with_relative(st.ctime, tnow)) + end oneline && print(io, ")") return nothing end @@ -168,18 +176,13 @@ show(io::IO, ::MIME"text/plain", st::StatStruct) = show_statstruct(io, st, false # stat & lstat functions +checkstat(s::StatStruct) = Int(s.ioerrno) in (0, Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL) ? s : uv_error(string("stat(", repr(s.desc), ")"), s.ioerrno) + macro stat_call(sym, arg1type, arg) return quote - stat_buf = zeros(UInt8, Int(ccall(:jl_sizeof_stat, Int32, ()))) + stat_buf = fill!(Memory{UInt8}(undef, Int(ccall(:jl_sizeof_stat, Int32, ()))), 0x00) r = ccall($(Expr(:quote, sym)), Int32, ($(esc(arg1type)), Ptr{UInt8}), $(esc(arg)), stat_buf) - if !(r in (0, Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL)) - uv_error(string("stat(", repr($(esc(arg))), ")"), r) - end - st = StatStruct($(esc(arg)), stat_buf) - if ispath(st) != (r == 0) - error("stat returned zero type for a valid path") - end - return st + return checkstat(StatStruct($(esc(arg)), stat_buf, r)) end end @@ -334,7 +337,7 @@ Return `true` if a valid filesystem entity exists at `path`, otherwise returns `false`. This is the generalization of [`isfile`](@ref), [`isdir`](@ref) etc. """ -ispath(st::StatStruct) = filemode(st) & 0xf000 != 0x0000 +ispath(st::StatStruct) = st.ioerrno == 0 function ispath(path::String) # We use `access()` and `F_OK` to determine if a given path exists. `F_OK` comes from `unistd.h`. F_OK = 0x00 diff --git a/src/sys.c b/src/sys.c index b54edc32b32b6..fa9054bb93e9a 100644 --- a/src/sys.c +++ b/src/sys.c @@ -102,7 +102,6 @@ JL_DLLEXPORT int32_t jl_nb_available(ios_t *s) // --- dir/file stuff --- -JL_DLLEXPORT int jl_sizeof_uv_fs_t(void) { return sizeof(uv_fs_t); } JL_DLLEXPORT char *jl_uv_fs_t_ptr(uv_fs_t *req) { return (char*)req->ptr; } JL_DLLEXPORT char *jl_uv_fs_t_path(uv_fs_t *req) { return (char*)req->path; } diff --git a/stdlib/FileWatching/src/FileWatching.jl b/stdlib/FileWatching/src/FileWatching.jl index 0c987ad01c828..4ea6fcedd59bb 100644 --- a/stdlib/FileWatching/src/FileWatching.jl +++ b/stdlib/FileWatching/src/FileWatching.jl @@ -22,11 +22,11 @@ export trymkpidlock import Base: @handle_as, wait, close, eventloop, notify_error, IOError, - _sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close, uv_error, _UVError, - iolock_begin, iolock_end, associate_julia_struct, disassociate_julia_struct, - preserve_handle, unpreserve_handle, isreadable, iswritable, isopen, - |, getproperty, propertynames -import Base.Filesystem.StatStruct + uv_req_data, uv_req_set_data, associate_julia_struct, disassociate_julia_struct, + _sizeof_uv_poll, _sizeof_uv_fs, _sizeof_uv_fs_event, _uv_hook_close, uv_error, _UVError, + iolock_begin, iolock_end, preserve_handle, unpreserve_handle, + isreadable, iswritable, isopen, |, getproperty, propertynames +import Base.Filesystem: StatStruct, uv_fs_req_cleanup if Sys.iswindows() import Base.WindowsRawSocket end @@ -126,31 +126,30 @@ mutable struct FolderMonitor end end +# this is similar to uv_fs_poll, but strives to avoid the design mistakes that make it unsuitable for any usable purpose +# https://github.com/libuv/libuv/issues/4543 mutable struct PollingFileWatcher - @atomic handle::Ptr{Cvoid} file::String - interval::UInt32 - notify::Base.ThreadSynchronizer - active::Bool - curr_error::Int32 - curr_stat::StatStruct + interval::Float64 + const notify::Base.ThreadSynchronizer # lock protects all fields which can be changed (including interval and file, if you really must) + timer::Union{Nothing,Timer} + const stat_req::Memory{UInt8} + active::Bool # whether there is already an uv_fspollcb in-flight, so to speak + closed::Bool # whether the user has explicitly destroyed this + ioerrno::Int32 # the stat errno as of the last result + prev_stat::StatStruct # the stat as of the last successful result PollingFileWatcher(file::AbstractString, interval::Float64=5.007) = PollingFileWatcher(String(file), interval) function PollingFileWatcher(file::String, interval::Float64=5.007) # same default as nodejs - handle = Libc.malloc(_sizeof_uv_fs_poll) - this = new(handle, file, round(UInt32, interval * 1000), Base.ThreadSynchronizer(), false, 0, StatStruct()) - associate_julia_struct(handle, this) - iolock_begin() - err = ccall(:uv_fs_poll_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), handle) - if err != 0 - Libc.free(handle) - throw(_UVError("PollingFileWatcher", err)) - end - finalizer(uvfinalize, this) - iolock_end() + stat_req = Memory{UInt8}(undef, Int(_sizeof_uv_fs)) + this = new(file, interval, Base.ThreadSynchronizer(), nothing, stat_req, false, false, 0, StatStruct()) + uv_req_set_data(stat_req, this) + wait(this) # initialize with the current stat before return return this end end +Base.stat(pfw::PollingFileWatcher) = Base.checkstat(@lock pfw.notify pfw.prev_stat) + mutable struct _FDWatcher @atomic handle::Ptr{Cvoid} fdnum::Int # this is NOT the file descriptor @@ -327,7 +326,7 @@ function close(t::FDWatcher) close(t.watcher, mask) end -function uvfinalize(uv::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) +function uvfinalize(uv::Union{FileMonitor, FolderMonitor}) iolock_begin() if uv.handle != C_NULL disassociate_julia_struct(uv) # close (and free) without notify @@ -336,7 +335,7 @@ function uvfinalize(uv::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) iolock_end() end -function close(t::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) +function close(t::Union{FileMonitor, FolderMonitor}) iolock_begin() if t.handle != C_NULL ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) @@ -344,6 +343,21 @@ function close(t::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) iolock_end() end +function close(pfw::PollingFileWatcher) + timer = nothing + lock(pfw.notify) + try + pfw.closed = true + notify(pfw.notify, false) + timer = pfw.timer + pfw.timer = nothing + finally + unlock(pfw.notify) + end + timer === nothing || close(timer) + nothing +end + function _uv_hook_close(uv::_FDWatcher) # fyi: jl_atexit_hook can cause this to get called too Libc.free(@atomicswap :monotonic uv.handle = C_NULL) @@ -351,18 +365,6 @@ function _uv_hook_close(uv::_FDWatcher) nothing end -function _uv_hook_close(uv::PollingFileWatcher) - lock(uv.notify) - try - uv.active = false - Libc.free(@atomicswap :monotonic uv.handle = C_NULL) - notify(uv.notify, StatStruct()) - finally - unlock(uv.notify) - end - nothing -end - function _uv_hook_close(uv::FileMonitor) lock(uv.notify) try @@ -388,7 +390,7 @@ end isopen(fm::FileMonitor) = fm.handle != C_NULL isopen(fm::FolderMonitor) = fm.handle != C_NULL -isopen(pfw::PollingFileWatcher) = pfw.handle != C_NULL +isopen(pfw::PollingFileWatcher) = !pfw.closed isopen(pfw::_FDWatcher) = pfw.refcount != (0, 0) isopen(pfw::FDWatcher) = !pfw.mask.timedout @@ -449,21 +451,50 @@ function uv_pollcb(handle::Ptr{Cvoid}, status::Int32, events::Int32) nothing end -function uv_fspollcb(handle::Ptr{Cvoid}, status::Int32, prev::Ptr, curr::Ptr) - t = @handle_as handle PollingFileWatcher - old_status = t.curr_error - t.curr_error = status - if status == 0 - t.curr_stat = StatStruct(convert(Ptr{UInt8}, curr)) - end - if status == 0 || status != old_status - prev_stat = StatStruct(convert(Ptr{UInt8}, prev)) - lock(t.notify) - try - notify(t.notify, prev_stat) - finally - unlock(t.notify) +function uv_fspollcb(req::Ptr{Cvoid}) + pfw = unsafe_pointer_to_objref(uv_req_data(req))::PollingFileWatcher + pfw.active = false + unpreserve_handle(pfw) + @assert pointer(pfw.stat_req) == req + r = Int32(ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req)) + statbuf = ccall(:uv_fs_get_statbuf, Ptr{UInt8}, (Ptr{Cvoid},), req) + curr_stat = StatStruct(pfw.file, statbuf, r) + uv_fs_req_cleanup(req) + lock(pfw.notify) + try + if !isempty(pfw.notify) # discard the update if nobody watching + if pfw.ioerrno != r || (r == 0 && pfw.prev_stat != curr_stat) + if r == 0 + pfw.prev_stat = curr_stat + end + pfw.ioerrno = r + notify(pfw.notify, true) + end + pfw.timer = Timer(pfw.interval) do t + # async task + iolock_begin() + lock(pfw.notify) + try + if pfw.timer === t # use identity check to test if this callback is stale by the time we got the lock + pfw.timer = nothing + @assert !pfw.active + if isopen(pfw) && !isempty(pfw.notify) + preserve_handle(pfw) + err = ccall(:uv_fs_stat, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb) + err == 0 || notify(pfw.notify, _UVError("PollingFileWatcher (start)", err), error=true) # likely just ENOMEM + pfw.active = true + end + end + finally + unlock(pfw.notify) + end + iolock_end() + nothing + end end + finally + unlock(pfw.notify) end nothing end @@ -475,7 +506,7 @@ global uv_jl_fseventscb_folder::Ptr{Cvoid} function __init__() global uv_jl_pollcb = @cfunction(uv_pollcb, Cvoid, (Ptr{Cvoid}, Cint, Cint)) - global uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Cvoid})) + global uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid},)) global uv_jl_fseventscb_file = @cfunction(uv_fseventscb_file, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) global uv_jl_fseventscb_folder = @cfunction(uv_fseventscb_folder, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) @@ -504,35 +535,6 @@ function start_watching(t::_FDWatcher) nothing end -function start_watching(t::PollingFileWatcher) - iolock_begin() - t.handle == C_NULL && throw(ArgumentError("PollingFileWatcher is closed")) - if !t.active - uv_error("PollingFileWatcher (start)", - ccall(:uv_fs_poll_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, UInt32), - t.handle, uv_jl_fspollcb::Ptr{Cvoid}, t.file, t.interval)) - t.active = true - end - iolock_end() - nothing -end - -function stop_watching(t::PollingFileWatcher) - iolock_begin() - lock(t.notify) - try - if t.active && isempty(t.notify) - t.active = false - uv_error("PollingFileWatcher (stop)", - ccall(:uv_fs_poll_stop, Int32, (Ptr{Cvoid},), t.handle)) - end - finally - unlock(t.notify) - end - iolock_end() - nothing -end - function start_watching(t::FileMonitor) iolock_begin() t.handle == C_NULL && throw(ArgumentError("FileMonitor is closed")) @@ -640,28 +642,65 @@ end function wait(pfw::PollingFileWatcher) iolock_begin() - preserve_handle(pfw) lock(pfw.notify) - local prevstat + prevstat = pfw.prev_stat + havechange = false + timer = nothing try - start_watching(pfw) + # we aren't too strict about the first interval after `wait`, but rather always + # check right away to see if it had immediately changed again, and then repeatedly + # after interval again until success + pfw.closed && throw(ArgumentError("PollingFileWatcher is closed")) + timer = pfw.timer + pfw.timer = nothing # disable Timer callback + # start_watching + if !pfw.active + preserve_handle(pfw) + err = ccall(:uv_fs_stat, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb) + err == 0 || uv_error("PollingFileWatcher (start)", err) # likely just ENOMEM + pfw.active = true + end iolock_end() - prevstat = wait(pfw.notify)::StatStruct + havechange = wait(pfw.notify)::Bool unlock(pfw.notify) iolock_begin() - lock(pfw.notify) - finally - unlock(pfw.notify) - unpreserve_handle(pfw) + catch + # stop_watching: cleanup any timers from before or after starting this wait before it failed, if there are no other watchers + latetimer = nothing + try + if isempty(pfw.notify) + latetimer = pfw.timer + pfw.timer = nothing + end + finally + unlock(pfw.notify) + end + if timer !== nothing || latetimer !== nothing + iolock_end() + timer === nothing || close(timer) + latetimer === nothing || close(latetimer) + iolock_begin() + end + rethrow() end - stop_watching(pfw) iolock_end() - if pfw.handle == C_NULL + timer === nothing || close(timer) # cleanup resources so we don't hang on exit + if !havechange # user canceled by calling close return prevstat, EOFError() - elseif pfw.curr_error != 0 - return prevstat, _UVError("PollingFileWatcher", pfw.curr_error) + end + # grab the most up-to-date stat result as of this time, even if it was a bit newer than the notify call + lock(pfw.notify) + currstat = pfw.prev_stat + ioerrno = pfw.ioerrno + unlock(pfw.notify) + if ioerrno == 0 + @assert currstat.ioerrno == 0 + return prevstat, currstat + elseif ioerrno in (Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL) + return prevstat, StatStruct(pfw.file, Ptr{UInt8}(0), ioerrno) else - return prevstat, pfw.curr_stat + return prevstat, _UVError("PollingFileWatcher", ioerrno) end end @@ -880,9 +919,9 @@ The `previous` status is always a `StatStruct`, but it may have all of the field The `current` status object may be a `StatStruct`, an `EOFError` (indicating the timeout elapsed), or some other `Exception` subtype (if the `stat` operation failed - for example, if the path does not exist). -To determine when a file was modified, compare `current isa StatStruct && mtime(prev) != mtime(current)` to detect -notification of changes. However, using [`watch_file`](@ref) for this operation is preferred, since -it is more reliable and efficient, although in some situations it may not be available. +To determine when a file was modified, compare `!(current isa StatStruct && prev == current)` to detect +notification of changes to the mtime or inode. However, using [`watch_file`](@ref) for this operation +is preferred, since it is more reliable and efficient, although in some situations it may not be available. """ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::Real=-1) pfw = PollingFileWatcher(s, Float64(interval_seconds)) @@ -893,12 +932,7 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R close(pfw) end end - statdiff = wait(pfw) - if isa(statdiff[2], IOError) - # file didn't initially exist, continue watching for it to be created (or the error to change) - statdiff = wait(pfw) - end - return statdiff + return wait(pfw) finally close(pfw) @isdefined(timer) && close(timer) diff --git a/stdlib/FileWatching/test/runtests.jl b/stdlib/FileWatching/test/runtests.jl index 2592aea024386..c9d7a4317fd08 100644 --- a/stdlib/FileWatching/test/runtests.jl +++ b/stdlib/FileWatching/test/runtests.jl @@ -2,6 +2,7 @@ using Test, FileWatching using Base: uv_error, Experimental +using Base.Filesystem: StatStruct @testset "FileWatching" begin @@ -218,7 +219,7 @@ function test_timeout(tval) @async test_file_poll(channel, 10, tval) tr = take!(channel) end - @test tr[1] === Base.Filesystem.StatStruct() && tr[2] === EOFError() + @test ispath(tr[1]::StatStruct) && tr[2] === EOFError() @test tval <= t_elapsed end @@ -231,7 +232,7 @@ function test_touch(slval) write(f, "Hello World\n") close(f) tr = take!(channel) - @test ispath(tr[1]) && ispath(tr[2]) + @test ispath(tr[1]::StatStruct) && ispath(tr[2]::StatStruct) fetch(t) end @@ -435,8 +436,8 @@ end @test_throws(Base._UVError("FolderMonitor (start)", Base.UV_ENOENT), watch_folder("____nonexistent_file", 10)) @test(@elapsed( - @test(poll_file("____nonexistent_file", 1, 3.1) === - (Base.Filesystem.StatStruct(), EOFError()))) > 3) + @test(poll_file("____nonexistent_file", 1, 3.1) == + (StatStruct(), EOFError()))) > 3) unwatch_folder(dir) @test isempty(FileWatching.watched_folders) diff --git a/test/file.jl b/test/file.jl index de258c92e02bc..a4262c4eaaa21 100644 --- a/test/file.jl +++ b/test/file.jl @@ -2128,6 +2128,16 @@ Base.joinpath(x::URI50890) = URI50890(x.f) @test !isnothing(Base.Filesystem.getusername(s.uid)) @test !isnothing(Base.Filesystem.getgroupname(s.gid)) end + s = Base.Filesystem.StatStruct() + stat_show_str = sprint(show, s) + stat_show_str_multi = sprint(show, MIME("text/plain"), s) + @test startswith(stat_show_str, "StatStruct(\"\" ENOENT: ") && endswith(stat_show_str, ")") + @test startswith(stat_show_str_multi, "StatStruct for \"\"\n ENOENT: ") && !endswith(stat_show_str_multi, r"\s") + s = Base.Filesystem.StatStruct("my/test", Ptr{UInt8}(0), Int32(Base.UV_ENOTDIR)) + stat_show_str = sprint(show, s) + stat_show_str_multi = sprint(show, MIME("text/plain"), s) + @test startswith(stat_show_str, "StatStruct(\"my/test\" ENOTDIR: ") && endswith(stat_show_str, ")") + @test startswith(stat_show_str_multi, "StatStruct for \"my/test\"\n ENOTDIR: ") && !endswith(stat_show_str_multi, r"\s") end @testset "diskstat() works" begin From b6e0136466396bc781406c0ab2f036f64cc818d7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 26 Sep 2024 13:57:52 -0400 Subject: [PATCH 278/548] [FileWatching] fix FileMonitor similarly and improve pidfile reliability Previously pidfile used the same poll_interval as sleep to detect if this code made any concurrency mistakes, but we do not really need to do that once FileMonitor is fixed to be reliable in the presence of parallel concurrency (instead of using watch_file). --- stdlib/FileWatching/src/FileWatching.jl | 108 ++++++++++-------------- stdlib/FileWatching/src/pidfile.jl | 46 +++++++--- stdlib/FileWatching/test/runtests.jl | 11 +-- 3 files changed, 84 insertions(+), 81 deletions(-) diff --git a/stdlib/FileWatching/src/FileWatching.jl b/stdlib/FileWatching/src/FileWatching.jl index 4ea6fcedd59bb..b24f352943ec5 100644 --- a/stdlib/FileWatching/src/FileWatching.jl +++ b/stdlib/FileWatching/src/FileWatching.jl @@ -38,13 +38,13 @@ const UV_CHANGE = Int32(2) struct FileEvent renamed::Bool changed::Bool - timedout::Bool + timedout::Bool # aka canceled FileEvent(r::Bool, c::Bool, t::Bool) = new(r, c, t) end FileEvent() = FileEvent(false, false, true) FileEvent(flags::Integer) = FileEvent((flags & UV_RENAME) != 0, (flags & UV_CHANGE) != 0, - false) + iszero(flags)) |(a::FileEvent, b::FileEvent) = FileEvent(a.renamed | b.renamed, a.changed | b.changed, @@ -80,23 +80,26 @@ iswritable(f::FDEvent) = f.writable mutable struct FileMonitor @atomic handle::Ptr{Cvoid} - file::String - notify::Base.ThreadSynchronizer - events::Int32 - active::Bool + const file::String + const notify::Base.ThreadSynchronizer + events::Int32 # accumulator for events that occurred since the last wait call, similar to Event with autoreset + ioerrno::Int32 # record the error, if any occurs (unlikely) FileMonitor(file::AbstractString) = FileMonitor(String(file)) function FileMonitor(file::String) handle = Libc.malloc(_sizeof_uv_fs_event) - this = new(handle, file, Base.ThreadSynchronizer(), 0, false) + this = new(handle, file, Base.ThreadSynchronizer(), 0, 0) associate_julia_struct(handle, this) iolock_begin() err = ccall(:uv_fs_event_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), handle) if err != 0 Libc.free(handle) - throw(_UVError("FileMonitor", err)) + uv_error("FileMonitor", err) end - iolock_end() finalizer(uvfinalize, this) + uv_error("FileMonitor (start)", + ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), + this.handle, uv_jl_fseventscb_file::Ptr{Cvoid}, file, 0)) + iolock_end() return this end end @@ -104,8 +107,8 @@ end mutable struct FolderMonitor @atomic handle::Ptr{Cvoid} # notify::Channel{Any} # eltype = Union{Pair{String, FileEvent}, IOError} - notify::Base.ThreadSynchronizer - channel::Vector{Any} # eltype = Pair{String, FileEvent} + const notify::Base.ThreadSynchronizer + const channel::Vector{Any} # eltype = Pair{String, FileEvent} FolderMonitor(folder::AbstractString) = FolderMonitor(String(folder)) function FolderMonitor(folder::String) handle = Libc.malloc(_sizeof_uv_fs_event) @@ -152,9 +155,9 @@ Base.stat(pfw::PollingFileWatcher) = Base.checkstat(@lock pfw.notify pfw.prev_st mutable struct _FDWatcher @atomic handle::Ptr{Cvoid} - fdnum::Int # this is NOT the file descriptor + const fdnum::Int # this is NOT the file descriptor refcount::Tuple{Int, Int} - notify::Base.ThreadSynchronizer + const notify::Base.ThreadSynchronizer events::Int32 active::Tuple{Bool, Bool} @@ -275,7 +278,7 @@ end mutable struct FDWatcher # WARNING: make sure `close` has been manually called on this watcher before closing / destroying `fd` - watcher::_FDWatcher + const watcher::_FDWatcher mask::FDEvent function FDWatcher(fd::RawFD, readable::Bool, writable::Bool) return FDWatcher(fd, FDEvent(readable, writable, false, false)) @@ -368,9 +371,8 @@ end function _uv_hook_close(uv::FileMonitor) lock(uv.notify) try - uv.active = false Libc.free(@atomicswap :monotonic uv.handle = C_NULL) - notify(uv.notify, FileEvent()) + notify(uv.notify) finally unlock(uv.notify) end @@ -399,10 +401,12 @@ function uv_fseventscb_file(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, st lock(t.notify) try if status != 0 + t.ioerrno = status notify_error(t.notify, _UVError("FileMonitor", status)) - else - t.events |= events - notify(t.notify, FileEvent(events)) + uvfinalize(t) + elseif events != t.events + events = t.events |= events + notify(t.notify, all=false) end finally unlock(t.notify) @@ -535,35 +539,6 @@ function start_watching(t::_FDWatcher) nothing end -function start_watching(t::FileMonitor) - iolock_begin() - t.handle == C_NULL && throw(ArgumentError("FileMonitor is closed")) - if !t.active - uv_error("FileMonitor (start)", - ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), - t.handle, uv_jl_fseventscb_file::Ptr{Cvoid}, t.file, 0)) - t.active = true - end - iolock_end() - nothing -end - -function stop_watching(t::FileMonitor) - iolock_begin() - lock(t.notify) - try - if t.active && isempty(t.notify) - t.active = false - uv_error("FileMonitor (stop)", - ccall(:uv_fs_event_stop, Int32, (Ptr{Cvoid},), t.handle)) - end - finally - unlock(t.notify) - end - iolock_end() - nothing -end - # n.b. this _wait may return spuriously early with a timedout event function _wait(fdw::_FDWatcher, mask::FDEvent) iolock_begin() @@ -705,26 +680,23 @@ function wait(pfw::PollingFileWatcher) end function wait(m::FileMonitor) - iolock_begin() + m.handle == C_NULL && throw(EOFError()) preserve_handle(m) lock(m.notify) - local events try - start_watching(m) - iolock_end() - events = wait(m.notify)::FileEvent - events |= FileEvent(m.events) - m.events = 0 - unlock(m.notify) - iolock_begin() - lock(m.notify) + while true + m.handle == C_NULL && throw(EOFError()) + events = @atomicswap :not_atomic m.events = 0 + events == 0 || return FileEvent(events) + if m.ioerrno != 0 + uv_error("FileMonitor", m.ioerrno) + end + wait(m.notify) + end finally unlock(m.notify) unpreserve_handle(m) end - stop_watching(m) - iolock_end() - return events end function wait(m::FolderMonitor) @@ -743,6 +715,7 @@ function wait(m::FolderMonitor) end return evt::Pair{String, FileEvent} end +Base.take!(m::FolderMonitor) = wait(m) # Channel-like API """ @@ -823,7 +796,12 @@ function watch_file(s::String, timeout_s::Float64=-1.0) close(fm) end end - return wait(fm) + try + return wait(fm) + catch ex + ex isa EOFError && return FileEvent() + rethrow() + end finally close(fm) @isdefined(timer) && close(timer) @@ -851,7 +829,7 @@ This behavior of this function varies slightly across platforms. See """ watch_folder(s::AbstractString, timeout_s::Real=-1) = watch_folder(String(s), timeout_s) function watch_folder(s::String, timeout_s::Real=-1) - fm = get!(watched_folders, s) do + fm = @lock watched_folders get!(watched_folders[], s) do return FolderMonitor(s) end local timer @@ -898,12 +876,12 @@ It is not recommended to do this while another task is waiting for """ unwatch_folder(s::AbstractString) = unwatch_folder(String(s)) function unwatch_folder(s::String) - fm = pop!(watched_folders, s, nothing) + fm = @lock watched_folders pop!(watched_folders[], s, nothing) fm === nothing || close(fm) nothing end -const watched_folders = Dict{String, FolderMonitor}() +const watched_folders = Lockable(Dict{String, FolderMonitor}()) """ poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current) diff --git a/stdlib/FileWatching/src/pidfile.jl b/stdlib/FileWatching/src/pidfile.jl index 4c821a3d897e4..95b8f20face29 100644 --- a/stdlib/FileWatching/src/pidfile.jl +++ b/stdlib/FileWatching/src/pidfile.jl @@ -4,14 +4,14 @@ module Pidfile export mkpidlock, trymkpidlock using Base: - IOError, UV_EEXIST, UV_ESRCH, + IOError, UV_EEXIST, UV_ESRCH, UV_ENOENT, Process using Base.Filesystem: File, open, JL_O_CREAT, JL_O_RDWR, JL_O_RDONLY, JL_O_EXCL, rename, samefile, path_separator -using ..FileWatching: watch_file +using ..FileWatching: FileMonitor using Base.Sys: iswindows """ @@ -256,19 +256,43 @@ function open_exclusive(path::String; end end # fall-back: wait for the lock - + watch = Lockable(Core.Box(nothing)) while true - # start the file-watcher prior to checking for the pidfile existence - t = @async try - watch_file(path, poll_interval) + # now try again to create it + # try to start the file-watcher prior to checking for the pidfile existence + watch = try + FileMonitor(path) catch ex isa(ex, IOError) || rethrow(ex) - sleep(poll_interval) # if the watch failed, convert to just doing a sleep + ex.code != UV_ENOENT # if the file was deleted in the meantime, don't sleep at all, even if the lock fails + end + timeout = nothing + if watch isa FileMonitor && stale_age > 0 + let watch = watch + timeout = Timer(stale_age) do t + close(watch) + end + end + end + try + file = tryopen_exclusive(path, mode) + file === nothing || return file + if watch isa FileMonitor + try + Base.wait(watch) # will time-out after stale_age passes + catch ex + isa(ex, EOFError) || isa(ex, IOError) || rethrow(ex) + end + end + if watch === true # if the watch failed, convert to just doing a sleep + sleep(poll_interval) + end + finally + # something changed about the path, so watch is now possibly monitoring the wrong file handle + # it will need to be recreated just before the next tryopen_exclusive attempt + timeout isa Timer && close(timeout) + watch isa FileMonitor && close(watch) end - # now try again to create it - file = tryopen_exclusive(path, mode) - file === nothing || return file - Base.wait(t) # sleep for a bit before trying again if stale_age > 0 && stale_pidfile(path, stale_age, refresh) # if the file seems stale, try to remove it before attempting again # set stale_age to zero so we won't attempt again, even if the attempt fails diff --git a/stdlib/FileWatching/test/runtests.jl b/stdlib/FileWatching/test/runtests.jl index c9d7a4317fd08..11df8849048f8 100644 --- a/stdlib/FileWatching/test/runtests.jl +++ b/stdlib/FileWatching/test/runtests.jl @@ -169,12 +169,13 @@ file = joinpath(dir, "afile.txt") # initialize a watch_folder instance and create afile.txt function test_init_afile() - @test isempty(FileWatching.watched_folders) + watched_folders = FileWatching.watched_folders + @test @lock watched_folders isempty(watched_folders[]) @test(watch_folder(dir, 0) == ("" => FileWatching.FileEvent())) @test @elapsed(@test(watch_folder(dir, 0) == ("" => FileWatching.FileEvent()))) <= 0.5 - @test length(FileWatching.watched_folders) == 1 + @test @lock(watched_folders, length(FileWatching.watched_folders[])) == 1 @test unwatch_folder(dir) === nothing - @test isempty(FileWatching.watched_folders) + @test @lock watched_folders isempty(watched_folders[]) @test 0.002 <= @elapsed(@test(watch_folder(dir, 0.004) == ("" => FileWatching.FileEvent()))) @test 0.002 <= @elapsed(@test(watch_folder(dir, 0.004) == ("" => FileWatching.FileEvent()))) <= 0.5 @test unwatch_folder(dir) === nothing @@ -204,7 +205,7 @@ function test_init_afile() @test unwatch_folder(dir) === nothing @test(watch_folder(dir, 0) == ("" => FileWatching.FileEvent())) @test 0.9 <= @elapsed(@test(watch_folder(dir, 1) == ("" => FileWatching.FileEvent()))) - @test length(FileWatching.watched_folders) == 1 + @test @lock(watched_folders, length(FileWatching.watched_folders[])) == 1 nothing end @@ -440,7 +441,7 @@ end (StatStruct(), EOFError()))) > 3) unwatch_folder(dir) -@test isempty(FileWatching.watched_folders) +@test @lock FileWatching.watched_folders isempty(FileWatching.watched_folders[]) rm(file) rm(dir) From f8d17e7ad4857ba3164ca1c4df8d118dbf42b429 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 26 Sep 2024 15:04:26 -0400 Subject: [PATCH 279/548] [FileWatching] reorganize file and add docs --- stdlib/FileWatching/docs/src/index.md | 16 +- stdlib/FileWatching/src/FileWatching.jl | 386 +++++++++++++++--------- stdlib/FileWatching/test/runtests.jl | 6 +- 3 files changed, 248 insertions(+), 160 deletions(-) diff --git a/stdlib/FileWatching/docs/src/index.md b/stdlib/FileWatching/docs/src/index.md index 1b2212fcc5a28..15d4e39a45117 100644 --- a/stdlib/FileWatching/docs/src/index.md +++ b/stdlib/FileWatching/docs/src/index.md @@ -5,11 +5,17 @@ EditURL = "https://github.com/JuliaLang/julia/blob/master/stdlib/FileWatching/do # [File Events](@id lib-filewatching) ```@docs -FileWatching.poll_fd -FileWatching.poll_file -FileWatching.watch_file -FileWatching.watch_folder -FileWatching.unwatch_folder +poll_fd +poll_file +watch_file +watch_folder +unwatch_folder +``` +```@docs +FileMonitor +FolderMonitor +PollingFileWatcher +FDWatcher ``` # Pidfile diff --git a/stdlib/FileWatching/src/FileWatching.jl b/stdlib/FileWatching/src/FileWatching.jl index b24f352943ec5..7c743ce634193 100644 --- a/stdlib/FileWatching/src/FileWatching.jl +++ b/stdlib/FileWatching/src/FileWatching.jl @@ -6,7 +6,7 @@ Utilities for monitoring files and file descriptors for events. module FileWatching export - # one-shot API (returns results): + # one-shot API (returns results, race-y): watch_file, # efficient for small numbers of files watch_folder, # efficient for large numbers of files unwatch_folder, @@ -78,6 +78,134 @@ isreadable(f::FDEvent) = f.readable iswritable(f::FDEvent) = f.writable |(a::FDEvent, b::FDEvent) = FDEvent(getfield(a, :events) | getfield(b, :events)) +# Callback functions + +function uv_fseventscb_file(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) + t = @handle_as handle FileMonitor + lock(t.notify) + try + if status != 0 + t.ioerrno = status + notify_error(t.notify, _UVError("FileMonitor", status)) + uvfinalize(t) + elseif events != t.events + events = t.events |= events + notify(t.notify, all=false) + end + finally + unlock(t.notify) + end + nothing +end + +function uv_fseventscb_folder(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) + t = @handle_as handle FolderMonitor + lock(t.notify) + try + if status != 0 + notify_error(t.notify, _UVError("FolderMonitor", status)) + else + fname = (filename == C_NULL) ? "" : unsafe_string(convert(Cstring, filename)) + push!(t.channel, fname => FileEvent(events)) + notify(t.notify) + end + finally + unlock(t.notify) + end + nothing +end + +function uv_pollcb(handle::Ptr{Cvoid}, status::Int32, events::Int32) + t = @handle_as handle _FDWatcher + lock(t.notify) + try + if status != 0 + notify_error(t.notify, _UVError("FDWatcher", status)) + else + t.events |= events + if t.active[1] || t.active[2] + if isempty(t.notify) + # if we keep hearing about events when nobody appears to be listening, + # stop the poll to save cycles + t.active = (false, false) + ccall(:uv_poll_stop, Int32, (Ptr{Cvoid},), t.handle) + end + end + notify(t.notify, events) + end + finally + unlock(t.notify) + end + nothing +end + +function uv_fspollcb(req::Ptr{Cvoid}) + pfw = unsafe_pointer_to_objref(uv_req_data(req))::PollingFileWatcher + pfw.active = false + unpreserve_handle(pfw) + @assert pointer(pfw.stat_req) == req + r = Int32(ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req)) + statbuf = ccall(:uv_fs_get_statbuf, Ptr{UInt8}, (Ptr{Cvoid},), req) + curr_stat = StatStruct(pfw.file, statbuf, r) + uv_fs_req_cleanup(req) + lock(pfw.notify) + try + if !isempty(pfw.notify) # must discard the update if nobody watching + if pfw.ioerrno != r || (r == 0 && pfw.prev_stat != curr_stat) + if r == 0 + pfw.prev_stat = curr_stat + end + pfw.ioerrno = r + notify(pfw.notify, true) + end + pfw.timer = Timer(pfw.interval) do t + # async task + iolock_begin() + lock(pfw.notify) + try + if pfw.timer === t # use identity check to test if this callback is stale by the time we got the lock + pfw.timer = nothing + @assert !pfw.active + if isopen(pfw) && !isempty(pfw.notify) + preserve_handle(pfw) + uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid},)) + err = ccall(:uv_fs_stat, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb::Ptr{Cvoid}) + err == 0 || notify(pfw.notify, _UVError("PollingFileWatcher (start)", err), error=true) # likely just ENOMEM + pfw.active = true + end + end + finally + unlock(pfw.notify) + end + iolock_end() + nothing + end + end + finally + unlock(pfw.notify) + end + nothing +end + +# Types + +""" + FileMonitor(path::AbstractString) + +Watch file or directory `path` (which must exist) for changes until a change occurs. This +function does not poll the file system and instead uses platform-specific functionality to +receive notifications from the operating system (e.g. via inotify on Linux). See the NodeJS +documentation linked below for details. + +`fm = FileMonitor(path)` acts like an auto-reset Event, so `wait(fm)` blocks until there has +been at least one event in the file originally at the given path and then returns an object +with boolean fields `renamed`, `changed`, `timedout` summarizing all changes that have +occurred since the last call to `wait` returned. + +This behavior of this function varies slightly across platforms. See + for more detailed information. +""" mutable struct FileMonitor @atomic handle::Ptr{Cvoid} const file::String @@ -96,6 +224,7 @@ mutable struct FileMonitor uv_error("FileMonitor", err) end finalizer(uvfinalize, this) + uv_jl_fseventscb_file = @cfunction(uv_fseventscb_file, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) uv_error("FileMonitor (start)", ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), this.handle, uv_jl_fseventscb_file::Ptr{Cvoid}, file, 0)) @@ -104,6 +233,23 @@ mutable struct FileMonitor end end + +""" + FolderMonitor(folder::AbstractString) + +Watch a file or directory `path` for changes until a change has occurred. This function does +not poll the file system and instead uses platform-specific functionality to receive +notifications from the operating system (e.g. via inotify on Linux). See the NodeJS +documentation linked below for details. + +This acts similar to a Channel, so calling `take!` (or `wait`) blocks until some change has +occurred. The `wait` function will return a pair where the first field is the name of the +changed file (if available) and the second field is an object with boolean fields `renamed` +and `changed`, giving the event that occurred on it. + +This behavior of this function varies slightly across platforms. See + for more detailed information. +""" mutable struct FolderMonitor @atomic handle::Ptr{Cvoid} # notify::Channel{Any} # eltype = Union{Pair{String, FileEvent}, IOError} @@ -121,6 +267,7 @@ mutable struct FolderMonitor throw(_UVError("FolderMonitor", err)) end finalizer(uvfinalize, this) + uv_jl_fseventscb_folder = @cfunction(uv_fseventscb_folder, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) uv_error("FolderMonitor (start)", ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), handle, uv_jl_fseventscb_folder::Ptr{Cvoid}, folder, 0)) @@ -131,6 +278,28 @@ end # this is similar to uv_fs_poll, but strives to avoid the design mistakes that make it unsuitable for any usable purpose # https://github.com/libuv/libuv/issues/4543 +""" + PollingFileWatcher(path::AbstractString, interval_s::Real=5.007) + +Monitor a file for changes by polling `stat` every `interval_s` seconds until a change +occurs or `timeout_s` seconds have elapsed. The `interval_s` should be a long period; the +default is 5.007 seconds. Call `stat` on it to get the most recent, but old, result. + +This acts like an auto-reset Event, so calling `wait` blocks until the `stat` result has +changed since the previous value captured upon entry to the `wait` call. The `wait` function +will return a pair of status objects `(previous, current)` once any `stat` change is +detected since the previous time that `wait` was called. The `previous` status is always a +`StatStruct`, but it may have all of the fields zeroed (indicating the file didn't +previously exist, or wasn't previously accessible). + +The `current` status object may be a `StatStruct`, an `EOFError` (if the wait is canceled by +closing this object), or some other `Exception` subtype (if the `stat` operation failed: for +example, if the path is removed). Note that `stat` value may be outdated if the file has +changed again multiple times. + +Using [`FileMonitor`](@ref) for this operation is preferred, since it is more reliable and +efficient, although in some situations it may not be available. +""" mutable struct PollingFileWatcher file::String interval::Float64 @@ -151,8 +320,6 @@ mutable struct PollingFileWatcher end end -Base.stat(pfw::PollingFileWatcher) = Base.checkstat(@lock pfw.notify pfw.prev_stat) - mutable struct _FDWatcher @atomic handle::Ptr{Cvoid} const fdnum::Int # this is NOT the file descriptor @@ -276,6 +443,25 @@ mutable struct _FDWatcher end end +""" + FDWatcher(fd::Union{RawFD,WindowsRawSocket}, readable::Bool, writable::Bool) + +Monitor a file descriptor `fd` for changes in the read or write availability. + +The keyword arguments determine which of read and/or write status should be monitored; at +least one of them must be set to `true`. + +The returned value is an object with boolean fields `readable`, `writable`, and `timedout`, +giving the result of the polling. + +This acts like a level-set event, so calling `wait` blocks until one of those conditions is +met, but then continues to return without blocking until the condition is cleared (either +there is no more to read, or no more space in the write buffer, or both). + +!!! warning + You must call `close` manually, when finished with this object, before the fd + argument is closed. Failure to do so risks serious crashes. +""" mutable struct FDWatcher # WARNING: make sure `close` has been manually called on this watcher before closing / destroying `fd` const watcher::_FDWatcher @@ -396,148 +582,7 @@ isopen(pfw::PollingFileWatcher) = !pfw.closed isopen(pfw::_FDWatcher) = pfw.refcount != (0, 0) isopen(pfw::FDWatcher) = !pfw.mask.timedout -function uv_fseventscb_file(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) - t = @handle_as handle FileMonitor - lock(t.notify) - try - if status != 0 - t.ioerrno = status - notify_error(t.notify, _UVError("FileMonitor", status)) - uvfinalize(t) - elseif events != t.events - events = t.events |= events - notify(t.notify, all=false) - end - finally - unlock(t.notify) - end - nothing -end - -function uv_fseventscb_folder(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) - t = @handle_as handle FolderMonitor - lock(t.notify) - try - if status != 0 - notify_error(t.notify, _UVError("FolderMonitor", status)) - else - fname = (filename == C_NULL) ? "" : unsafe_string(convert(Cstring, filename)) - push!(t.channel, fname => FileEvent(events)) - notify(t.notify) - end - finally - unlock(t.notify) - end - nothing -end - -function uv_pollcb(handle::Ptr{Cvoid}, status::Int32, events::Int32) - t = @handle_as handle _FDWatcher - lock(t.notify) - try - if status != 0 - notify_error(t.notify, _UVError("FDWatcher", status)) - else - t.events |= events - if t.active[1] || t.active[2] - if isempty(t.notify) - # if we keep hearing about events when nobody appears to be listening, - # stop the poll to save cycles - t.active = (false, false) - ccall(:uv_poll_stop, Int32, (Ptr{Cvoid},), t.handle) - end - end - notify(t.notify, events) - end - finally - unlock(t.notify) - end - nothing -end - -function uv_fspollcb(req::Ptr{Cvoid}) - pfw = unsafe_pointer_to_objref(uv_req_data(req))::PollingFileWatcher - pfw.active = false - unpreserve_handle(pfw) - @assert pointer(pfw.stat_req) == req - r = Int32(ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req)) - statbuf = ccall(:uv_fs_get_statbuf, Ptr{UInt8}, (Ptr{Cvoid},), req) - curr_stat = StatStruct(pfw.file, statbuf, r) - uv_fs_req_cleanup(req) - lock(pfw.notify) - try - if !isempty(pfw.notify) # discard the update if nobody watching - if pfw.ioerrno != r || (r == 0 && pfw.prev_stat != curr_stat) - if r == 0 - pfw.prev_stat = curr_stat - end - pfw.ioerrno = r - notify(pfw.notify, true) - end - pfw.timer = Timer(pfw.interval) do t - # async task - iolock_begin() - lock(pfw.notify) - try - if pfw.timer === t # use identity check to test if this callback is stale by the time we got the lock - pfw.timer = nothing - @assert !pfw.active - if isopen(pfw) && !isempty(pfw.notify) - preserve_handle(pfw) - err = ccall(:uv_fs_stat, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), - eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb) - err == 0 || notify(pfw.notify, _UVError("PollingFileWatcher (start)", err), error=true) # likely just ENOMEM - pfw.active = true - end - end - finally - unlock(pfw.notify) - end - iolock_end() - nothing - end - end - finally - unlock(pfw.notify) - end - nothing -end - -global uv_jl_pollcb::Ptr{Cvoid} -global uv_jl_fspollcb::Ptr{Cvoid} -global uv_jl_fseventscb_file::Ptr{Cvoid} -global uv_jl_fseventscb_folder::Ptr{Cvoid} - -function __init__() - global uv_jl_pollcb = @cfunction(uv_pollcb, Cvoid, (Ptr{Cvoid}, Cint, Cint)) - global uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid},)) - global uv_jl_fseventscb_file = @cfunction(uv_fseventscb_file, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) - global uv_jl_fseventscb_folder = @cfunction(uv_fseventscb_folder, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) - - Base.mkpidlock_hook = mkpidlock - Base.trymkpidlock_hook = trymkpidlock - Base.parse_pidfile_hook = Pidfile.parse_pidfile - - nothing -end - -function start_watching(t::_FDWatcher) - iolock_begin() - t.handle == C_NULL && throw(ArgumentError("FDWatcher is closed")) - readable = t.refcount[1] > 0 - writable = t.refcount[2] > 0 - if t.active[1] != readable || t.active[2] != writable - # make sure the READABLE / WRITEABLE state is updated - uv_error("FDWatcher (start)", - ccall(:uv_poll_start, Int32, (Ptr{Cvoid}, Int32, Ptr{Cvoid}), - t.handle, - (readable ? UV_READABLE : 0) | (writable ? UV_WRITABLE : 0), - uv_jl_pollcb::Ptr{Cvoid})) - t.active = (readable, writable) - end - iolock_end() - nothing -end +Base.stat(pfw::PollingFileWatcher) = Base.checkstat(@lock pfw.notify pfw.prev_stat) # n.b. this _wait may return spuriously early with a timedout event function _wait(fdw::_FDWatcher, mask::FDEvent) @@ -549,7 +594,20 @@ function _wait(fdw::_FDWatcher, mask::FDEvent) if !isopen(fdw) # !open throw(EOFError()) elseif events.timedout - start_watching(fdw) # make sure the poll is active + fdw.handle == C_NULL && throw(ArgumentError("FDWatcher is closed")) + # start_watching to make sure the poll is active + readable = fdw.refcount[1] > 0 + writable = fdw.refcount[2] > 0 + if fdw.active[1] != readable || fdw.active[2] != writable + # make sure the READABLE / WRITEABLE state is updated + uv_jl_pollcb = @cfunction(uv_pollcb, Cvoid, (Ptr{Cvoid}, Cint, Cint)) + uv_error("FDWatcher (start)", + ccall(:uv_poll_start, Int32, (Ptr{Cvoid}, Int32, Ptr{Cvoid}), + fdw.handle, + (readable ? UV_READABLE : 0) | (writable ? UV_WRITABLE : 0), + uv_jl_pollcb::Ptr{Cvoid})) + fdw.active = (readable, writable) + end iolock_end() return FDEvent(wait(fdw.notify)::Int32) else @@ -631,8 +689,9 @@ function wait(pfw::PollingFileWatcher) # start_watching if !pfw.active preserve_handle(pfw) + uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid},)) err = ccall(:uv_fs_stat, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), - eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb) + eventloop(), pfw.stat_req, pfw.file, uv_jl_fspollcb::Ptr{Cvoid}) err == 0 || uv_error("PollingFileWatcher (start)", err) # likely just ENOMEM pfw.active = true end @@ -664,7 +723,8 @@ function wait(pfw::PollingFileWatcher) if !havechange # user canceled by calling close return prevstat, EOFError() end - # grab the most up-to-date stat result as of this time, even if it was a bit newer than the notify call + # grab the most up-to-date stat result as of this time, even if it was a bit newer than + # the notify call (unlikely, as there would need to be a concurrent call to wait) lock(pfw.notify) currstat = pfw.prev_stat ioerrno = pfw.ioerrno @@ -729,6 +789,10 @@ least one of them must be set to `true`. The returned value is an object with boolean fields `readable`, `writable`, and `timedout`, giving the result of the polling. + +This is a thin wrapper over calling `wait` on a [`FDWatcher`](@ref), which implements the +functionality but requires the user to call `close` manually when finished with it, or risk +serious crashes. """ function poll_fd(s::Union{RawFD, Sys.iswindows() ? WindowsRawSocket : Union{}}, timeout_s::Real=-1; readable=false, writable=false) mask = FDEvent(readable, writable, false, false) @@ -786,6 +850,15 @@ giving the result of watching the file. This behavior of this function varies slightly across platforms. See for more detailed information. + +This is a thin wrapper over calling `wait` on a [`FileMonitor`](@ref). This function has a +small race window between consecutive calls to `watch_file` where the file might change +without being detected. To avoid this race, use + + fm = FileMonitor(path) + wait(fm) + +directly, re-using the same `fm` each time you `wait`. """ function watch_file(s::String, timeout_s::Float64=-1.0) fm = FileMonitor(s) @@ -812,7 +885,7 @@ watch_file(s::AbstractString, timeout_s::Real=-1) = watch_file(String(s), Float6 """ watch_folder(path::AbstractString, timeout_s::Real=-1) -Watches a file or directory `path` for changes until a change has occurred or `timeout_s` +Watch a file or directory `path` for changes until a change has occurred or `timeout_s` seconds have elapsed. This function does not poll the file system and instead uses platform-specific functionality to receive notifications from the operating system (e.g. via inotify on Linux). See the NodeJS documentation linked below for details. @@ -826,6 +899,8 @@ giving the event. This behavior of this function varies slightly across platforms. See for more detailed information. + +This function is a thin wrapper over calling `wait` on a [`FolderMonitor`](@ref), with added timeout support. """ watch_folder(s::AbstractString, timeout_s::Real=-1) = watch_folder(String(s), timeout_s) function watch_folder(s::String, timeout_s::Real=-1) @@ -895,11 +970,15 @@ The `previous` status is always a `StatStruct`, but it may have all of the field (indicating the file didn't previously exist, or wasn't previously accessible). The `current` status object may be a `StatStruct`, an `EOFError` (indicating the timeout elapsed), -or some other `Exception` subtype (if the `stat` operation failed - for example, if the path does not exist). +or some other `Exception` subtype (if the `stat` operation failed: for example, if the path does not exist). To determine when a file was modified, compare `!(current isa StatStruct && prev == current)` to detect notification of changes to the mtime or inode. However, using [`watch_file`](@ref) for this operation is preferred, since it is more reliable and efficient, although in some situations it may not be available. + +This is a thin wrapper over calling `wait` on a [`PollingFileWatcher`](@ref), which implements +the functionality, but this function has a small race window between consecutive calls to +`poll_file` where the file might change without being detected. """ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::Real=-1) pfw = PollingFileWatcher(s, Float64(interval_seconds)) @@ -920,4 +999,11 @@ end include("pidfile.jl") import .Pidfile: mkpidlock, trymkpidlock +function __init__() + Base.mkpidlock_hook = mkpidlock + Base.trymkpidlock_hook = trymkpidlock + Base.parse_pidfile_hook = Pidfile.parse_pidfile + nothing +end + end diff --git a/stdlib/FileWatching/test/runtests.jl b/stdlib/FileWatching/test/runtests.jl index 11df8849048f8..def555154264d 100644 --- a/stdlib/FileWatching/test/runtests.jl +++ b/stdlib/FileWatching/test/runtests.jl @@ -452,10 +452,6 @@ rm(dir) include("pidfile.jl") end -@testset "Docstrings" begin - undoc = Docs.undocumented_names(FileWatching) - @test_broken isempty(undoc) - @test undoc == [:FDWatcher, :FileMonitor, :FolderMonitor, :PollingFileWatcher] -end +@test isempty(Docs.undocumented_names(FileWatching)) end # testset From bb25910328570835f6a2fdbb3b8ca93b14a65858 Mon Sep 17 00:00:00 2001 From: Kiran Pamnany Date: Mon, 30 Sep 2024 15:41:20 -0400 Subject: [PATCH 280/548] Add `--trace-dispatch` (#55848) --- NEWS.md | 1 + base/options.jl | 1 + doc/man/julia.1 | 4 ++ doc/src/manual/command-line-interface.md | 1 + src/gf.c | 57 +++++++++++++++++++++++- src/jloptions.c | 8 ++++ src/jloptions.h | 1 + src/jltypes.c | 2 +- src/julia.h | 8 +++- src/method.c | 2 +- src/staticdata.c | 2 +- src/staticdata_utils.c | 3 +- test/cmdlineargs.jl | 22 +++++++++ test/core.jl | 2 +- 14 files changed, 106 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index ca2bf1f615012..cc1bbc7449e5d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -59,6 +59,7 @@ variables. ([#53742]). * New `--trace-compile-timing` option to report how long each method reported by `--trace-compile` took to compile, in ms. ([#54662]) * `--trace-compile` now prints recompiled methods in yellow or with a trailing comment if color is not supported ([#55763]) +* New `--trace-dispatch` option to report methods that are dynamically dispatched ([#55848]). Multi-threading changes ----------------------- diff --git a/base/options.jl b/base/options.jl index 1de7a2acb1e06..f535c27d99122 100644 --- a/base/options.jl +++ b/base/options.jl @@ -34,6 +34,7 @@ struct JLOptions can_inline::Int8 polly::Int8 trace_compile::Ptr{UInt8} + trace_dispatch::Ptr{UInt8} fast_math::Int8 worker::Int8 cookie::Ptr{UInt8} diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 536a23bd37894..56cb690d66eeb 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -290,6 +290,10 @@ Methods that were recompiled are printed in yellow or with a trailing comment if --trace-compile-timing= If --trace-compile is enabled show how long each took to compile in ms +.TP +--trace-dispatch={stderr|name} +Print precompile statements for methods dispatched during execution or save to stderr or a path. + .TP -image-codegen Force generate code in imaging mode diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index ef20e51ea6e4e..5255720e55cd7 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -216,6 +216,7 @@ The following is a complete list of command-line switches available when launchi |`--output-incremental={yes\|no*}` |Generate an incremental output file (rather than complete)| |`--trace-compile={stderr\|name}` |Print precompile statements for methods compiled during execution or save to stderr or a path. Methods that were recompiled are printed in yellow or with a trailing comment if color is not supported| |`--trace-compile-timing` |If --trace-compile is enabled show how long each took to compile in ms| +|`--trace-dispatch={stderr\|name}` |Print precompile statements for methods dispatched during execution or save to stderr or a path.| |`--image-codegen` |Force generate code in imaging mode| |`--permalloc-pkgimg={yes\|no*}` |Copy the data section of package images into memory| |`--trim={no*|safe|unsafe|unsafe-warn}` |Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. The three non-default options differ in how they handle dynamic call sites. In safe mode, such sites result in compile-time errors. In unsafe mode, such sites are allowed but the resulting binary might be missing needed code and can throw runtime errors. With unsafe-warn, such sites will trigger warnings at compile-time and might error at runtime.| diff --git a/src/gf.c b/src/gf.c index 321711c839aa8..56ebe6fe2fa84 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2560,6 +2560,38 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila JL_UNLOCK(&precomp_statement_out_lock); } +jl_mutex_t dispatch_statement_out_lock; + +static void record_dispatch_statement(jl_method_instance_t *mi) +{ + static ios_t f_dispatch; + static JL_STREAM* s_dispatch = NULL; + jl_method_t *def = mi->def.method; + if (!jl_is_method(def)) + return; + + JL_LOCK(&dispatch_statement_out_lock); + if (s_dispatch == NULL) { + const char *t = jl_options.trace_dispatch; + if (!strncmp(t, "stderr", 6)) { + s_dispatch = JL_STDERR; + } + else { + if (ios_file(&f_dispatch, t, 1, 1, 1, 1) == NULL) + jl_errorf("cannot open dispatch statement file \"%s\" for writing", t); + s_dispatch = (JL_STREAM*) &f_dispatch; + } + } + if (!jl_has_free_typevars(mi->specTypes)) { + jl_printf(s_dispatch, "precompile("); + jl_static_show(s_dispatch, mi->specTypes); + jl_printf(s_dispatch, ")\n"); + if (s_dispatch != JL_STDERR) + ios_flush(&f_dispatch); + } + JL_UNLOCK(&dispatch_statement_out_lock); +} + // If waitcompile is 0, this will return NULL if compiling is on-going in the JIT. This is // useful for the JIT itself, since it just doesn't cause redundant work or missed updates, // but merely causes it to look into the current JIT worklist. @@ -3067,7 +3099,8 @@ static void jl_compile_now(jl_method_instance_t *mi) JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world) { size_t tworld = jl_typeinf_world; - jl_atomic_store_relaxed(&mi->precompiled, 1); + uint8_t miflags = jl_atomic_load_relaxed(&mi->flags) | JL_MI_FLAGS_MASK_PRECOMPILED; + jl_atomic_store_relaxed(&mi->flags, miflags); if (jl_generating_output()) { jl_compile_now(mi); // In addition to full compilation of the compilation-signature, if `types` is more specific (e.g. due to nospecialize), @@ -3082,7 +3115,8 @@ JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tuplet types2 = jl_type_intersection_env((jl_value_t*)types, (jl_value_t*)mi->def.method->sig, &tpenv2); jl_method_instance_t *mi2 = jl_specializations_get_linfo(mi->def.method, (jl_value_t*)types2, tpenv2); JL_GC_POP(); - jl_atomic_store_relaxed(&mi2->precompiled, 1); + miflags = jl_atomic_load_relaxed(&mi2->flags) | JL_MI_FLAGS_MASK_PRECOMPILED; + jl_atomic_store_relaxed(&mi2->flags, miflags); if (jl_rettype_inferred_native(mi2, world, world) == jl_nothing) (void)jl_type_infer(mi2, world, SOURCE_MODE_NOT_REQUIRED); if (jl_typeinf_func && jl_atomic_load_relaxed(&mi->def.method->primary_world) <= tworld) { @@ -3358,6 +3392,16 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t jl_method_error(F, args, nargs, world); // unreachable } + // mfunc is about to be dispatched + if (jl_options.trace_dispatch != NULL) { + uint8_t miflags = jl_atomic_load_relaxed(&mfunc->flags); + uint8_t was_dispatched = miflags & JL_MI_FLAGS_MASK_DISPATCHED; + if (!was_dispatched) { + miflags |= JL_MI_FLAGS_MASK_DISPATCHED; + jl_atomic_store_relaxed(&mfunc->flags, miflags); + record_dispatch_statement(mfunc); + } + } } #ifdef JL_TRACE @@ -3480,6 +3524,15 @@ jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, jl_value jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation } JL_GC_PROMISE_ROOTED(mfunc); + if (jl_options.trace_dispatch != NULL) { + uint8_t miflags = jl_atomic_load_relaxed(&mfunc->flags); + uint8_t was_dispatched = miflags & JL_MI_FLAGS_MASK_DISPATCHED; + if (!was_dispatched) { + miflags |= JL_MI_FLAGS_MASK_DISPATCHED; + jl_atomic_store_relaxed(&mfunc->flags, miflags); + record_dispatch_statement(mfunc); + } + } size_t world = jl_current_task->world_age; return _jl_invoke(gf, args, nargs - 1, mfunc, world); } diff --git a/src/jloptions.c b/src/jloptions.c index 530d5e2577a9a..35f0a76e3f6e7 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -77,6 +77,7 @@ JL_DLLEXPORT void jl_init_options(void) 1, // can_inline JL_OPTIONS_POLLY_ON, // polly NULL, // trace_compile + NULL, // trace_dispatch JL_OPTIONS_FAST_MATH_DEFAULT, 0, // worker NULL, // cookie @@ -294,6 +295,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_polly, opt_trace_compile, opt_trace_compile_timing, + opt_trace_dispatch, opt_math_mode, opt_worker, opt_bind_to, @@ -372,6 +374,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "polly", required_argument, 0, opt_polly }, { "trace-compile", required_argument, 0, opt_trace_compile }, { "trace-compile-timing", no_argument, 0, opt_trace_compile_timing }, + { "trace-dispatch", required_argument, 0, opt_trace_dispatch }, { "math-mode", required_argument, 0, opt_math_mode }, { "handle-signals", required_argument, 0, opt_handle_signals }, // hidden command line options @@ -828,6 +831,11 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) case opt_trace_compile_timing: jl_options.trace_compile_timing = 1; break; + case opt_trace_dispatch: + jl_options.trace_dispatch = strdup(optarg); + if (!jl_options.trace_dispatch) + jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + break; case opt_math_mode: if (!strcmp(optarg,"ieee")) jl_options.fast_math = JL_OPTIONS_FAST_MATH_OFF; diff --git a/src/jloptions.h b/src/jloptions.h index 3d7deedb59e15..e58797caace3c 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -38,6 +38,7 @@ typedef struct { int8_t can_inline; int8_t polly; const char *trace_compile; + const char *trace_dispatch; int8_t fast_math; int8_t worker; const char *cookie; diff --git a/src/jltypes.c b/src/jltypes.c index fbc8e9f7f7f16..11f1d11a14edc 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3617,7 +3617,7 @@ void jl_init_types(void) JL_GC_DISABLED "backedges", "cache", "cache_with_orig", - "precompiled"), + "flags"), jl_svec(7, jl_new_struct(jl_uniontype_type, jl_method_type, jl_module_type), jl_any_type, diff --git a/src/julia.h b/src/julia.h index 73b96cf0183d1..c6ff729a308eb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -410,8 +410,14 @@ struct _jl_method_instance_t { jl_array_t *backedges; // list of method-instances which call this method-instance; `invoke` records (invokesig, caller) pairs _Atomic(struct _jl_code_instance_t*) cache; uint8_t cache_with_orig; // !cache_with_specTypes - _Atomic(uint8_t) precompiled; // true if this instance was generated by an explicit `precompile(...)` call + + // flags for this method instance + // bit 0: generated by an explicit `precompile(...)` + // bit 1: dispatched + _Atomic(uint8_t) flags; }; +#define JL_MI_FLAGS_MASK_PRECOMPILED 0x01 +#define JL_MI_FLAGS_MASK_DISPATCHED 0x02 // OpaqueClosure typedef struct _jl_opaque_closure_t { diff --git a/src/method.c b/src/method.c index d4457b1549353..6aba60e7fe12c 100644 --- a/src/method.c +++ b/src/method.c @@ -629,7 +629,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_new_method_instance_uninit(void) mi->backedges = NULL; jl_atomic_store_relaxed(&mi->cache, NULL); mi->cache_with_orig = 0; - jl_atomic_store_relaxed(&mi->precompiled, 0); + jl_atomic_store_relaxed(&mi->flags, 0); return mi; } diff --git a/src/staticdata.c b/src/staticdata.c index f54cc9692eaea..aa9a16daab7a5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1718,7 +1718,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED else if (jl_is_method_instance(v)) { assert(f == s->s); jl_method_instance_t *newmi = (jl_method_instance_t*)&f->buf[reloc_offset]; - jl_atomic_store_relaxed(&newmi->precompiled, 0); + jl_atomic_store_relaxed(&newmi->flags, 0); } else if (jl_is_code_instance(v)) { assert(f == s->s); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index f39e5357c6782..81aed233af5c0 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -159,7 +159,8 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, if (jl_is_method(mod)) mod = ((jl_method_t*)mod)->module; assert(jl_is_module(mod)); - if (jl_atomic_load_relaxed(&mi->precompiled) || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes)) { + uint8_t is_precompiled = jl_atomic_load_relaxed(&mi->flags) & JL_MI_FLAGS_MASK_PRECOMPILED; + if (is_precompiled || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes)) { return 1; } if (!mi->backedges) { diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index c6720e23739d8..cc3f8950f0dc0 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -787,6 +787,17 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # tested in test/parallel.jl) @test errors_not_signals(`$exename --worker=true`) + # --trace-compile + let + io = IOBuffer() + v = writereadpipeline( + "foo(x) = begin Base.Experimental.@force_compile; x; end; foo(1)", + `$exename --trace-compile=stderr -i`, + stderr=io) + _stderr = String(take!(io)) + @test occursin("precompile(Tuple{typeof(Main.foo), Int", _stderr) + end + # --trace-compile-timing let io = IOBuffer() @@ -798,6 +809,17 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test occursin(" ms =# precompile(Tuple{typeof(Main.foo), Int", _stderr) end + # --trace-dispatch + let + io = IOBuffer() + v = writereadpipeline( + "foo(x) = begin Base.Experimental.@force_compile; x; end; foo(1)", + `$exename --trace-dispatch=stderr -i`, + stderr=io) + _stderr = String(take!(io)) + @test occursin("precompile(Tuple{typeof(Main.foo), Int", _stderr) + end + # test passing arguments mktempdir() do dir testfile, io = mktemp(dir) diff --git a/test/core.jl b/test/core.jl index d41a58a7ccb2e..1395817d8615e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -34,7 +34,7 @@ for (T, c) in ( (Core.CodeInfo, []), (Core.CodeInstance, [:next, :min_world, :max_world, :inferred, :debuginfo, :ipo_purity_bits, :invoke, :specptr, :specsigflags, :precompile]), (Core.Method, [:primary_world, :deleted_world]), - (Core.MethodInstance, [:cache, :precompiled]), + (Core.MethodInstance, [:cache, :flags]), (Core.MethodTable, [:defs, :leafcache, :cache, :max_args]), (Core.TypeMapEntry, [:next, :min_world, :max_world]), (Core.TypeMapLevel, [:arg1, :targ, :name1, :tname, :list, :any]), From a7c5056b722182adfd183fdc7bdfdef39cd8e28e Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 1 Oct 2024 01:41:23 +0200 Subject: [PATCH 281/548] relocation: account for trailing path separator in depot paths (#55355) Fixes #55340 --- base/loading.jl | 26 ++++++++++++++++---------- src/precompile.c | 17 +++++++++++++---- src/staticdata_utils.c | 20 ++++++++++++++------ test/relocatedepot.jl | 34 +++++++++++++++++++++++++++++----- 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index fbf6bb4af50aa..9080a2271fb27 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3165,16 +3165,9 @@ mutable struct CacheHeaderIncludes const modpath::Vector{String} # seemingly not needed in Base, but used by Revise end -function replace_depot_path(path::AbstractString) - for depot in DEPOT_PATH - !isdir(depot) && continue - - # Strip extraneous pathseps through normalization. - if isdirpath(depot) - depot = dirname(depot) - end - - if startswith(path, depot) +function replace_depot_path(path::AbstractString, depots::Vector{String}=normalize_depots_for_relocation()) + for depot in depots + if startswith(path, string(depot, Filesystem.pathsep())) || path == depot path = replace(path, depot => "@depot"; count=1) break end @@ -3182,6 +3175,19 @@ function replace_depot_path(path::AbstractString) return path end +function normalize_depots_for_relocation() + depots = String[] + sizehint!(depots, length(DEPOT_PATH)) + for d in DEPOT_PATH + isdir(d) || continue + if isdirpath(d) + d = dirname(d) + end + push!(depots, abspath(d)) + end + return depots +end + function restore_depot_path(path::AbstractString, depot::AbstractString) replace(path, r"^@depot" => depot; count=1) end diff --git a/src/precompile.c b/src/precompile.c index 5088d45a5ad74..c21cf5367fba6 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -39,9 +39,17 @@ void write_srctext(ios_t *f, jl_array_t *udeps, int64_t srctextpos) { static jl_value_t *replace_depot_func = NULL; if (!replace_depot_func) replace_depot_func = jl_get_global(jl_base_module, jl_symbol("replace_depot_path")); + static jl_value_t *normalize_depots_func = NULL; + if (!normalize_depots_func) + normalize_depots_func = jl_get_global(jl_base_module, jl_symbol("normalize_depots_for_relocation")); ios_t srctext; - jl_value_t *deptuple = NULL; - JL_GC_PUSH2(&deptuple, &udeps); + jl_value_t *deptuple = NULL, *depots = NULL; + JL_GC_PUSH3(&deptuple, &udeps, &depots); + jl_task_t *ct = jl_current_task; + size_t last_age = ct->world_age; + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + depots = jl_apply(&normalize_depots_func, 1); + ct->world_age = last_age; for (size_t i = 0; i < len; i++) { deptuple = jl_array_ptr_ref(udeps, i); jl_value_t *depmod = jl_fieldref(deptuple, 0); // module @@ -60,13 +68,14 @@ void write_srctext(ios_t *f, jl_array_t *udeps, int64_t srctextpos) { } jl_value_t **replace_depot_args; - JL_GC_PUSHARGS(replace_depot_args, 2); + JL_GC_PUSHARGS(replace_depot_args, 3); replace_depot_args[0] = replace_depot_func; replace_depot_args[1] = abspath; + replace_depot_args[2] = depots; jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - jl_value_t *depalias = (jl_value_t*)jl_apply(replace_depot_args, 2); + jl_value_t *depalias = (jl_value_t*)jl_apply(replace_depot_args, 3); ct->world_age = last_age; JL_GC_POP(); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 81aed233af5c0..8eb223d3cfbde 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -753,6 +753,16 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t* worklist, jl_array_t static jl_value_t *replace_depot_func = NULL; if (!replace_depot_func) replace_depot_func = jl_get_global(jl_base_module, jl_symbol("replace_depot_path")); + static jl_value_t *normalize_depots_func = NULL; + if (!normalize_depots_func) + normalize_depots_func = jl_get_global(jl_base_module, jl_symbol("normalize_depots_for_relocation")); + + jl_value_t *depots = NULL, *prefs_hash = NULL, *prefs_list = NULL; + JL_GC_PUSH2(&depots, &prefs_list); + last_age = ct->world_age; + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + depots = jl_apply(&normalize_depots_func, 1); + ct->world_age = last_age; // write a placeholder for total size so that we can quickly seek past all of the // dependencies if we don't need them @@ -765,13 +775,14 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t* worklist, jl_array_t if (replace_depot_func) { jl_value_t **replace_depot_args; - JL_GC_PUSHARGS(replace_depot_args, 2); + JL_GC_PUSHARGS(replace_depot_args, 3); replace_depot_args[0] = replace_depot_func; replace_depot_args[1] = deppath; + replace_depot_args[2] = depots; ct = jl_current_task; size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - deppath = (jl_value_t*)jl_apply(replace_depot_args, 2); + deppath = (jl_value_t*)jl_apply(replace_depot_args, 3); ct->world_age = last_age; JL_GC_POP(); } @@ -804,9 +815,6 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t* worklist, jl_array_t write_int32(s, 0); // terminator, for ease of reading // Calculate Preferences hash for current package. - jl_value_t *prefs_hash = NULL; - jl_value_t *prefs_list = NULL; - JL_GC_PUSH1(&prefs_list); if (jl_base_module) { // Toplevel module is the module we're currently compiling, use it to get our preferences hash jl_value_t * toplevel = (jl_value_t*)jl_get_global(jl_base_module, jl_symbol("__toplevel__")); @@ -853,7 +861,7 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t* worklist, jl_array_t write_int32(s, 0); write_uint64(s, 0); } - JL_GC_POP(); // for prefs_list + JL_GC_POP(); // for depots, prefs_list // write a dummy file position to indicate the beginning of the source-text pos = ios_pos(s); diff --git a/test/relocatedepot.jl b/test/relocatedepot.jl index 039d422c35e25..2ef6dec90dbc1 100644 --- a/test/relocatedepot.jl +++ b/test/relocatedepot.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + using Test @@ -26,16 +28,38 @@ end if !test_relocated_depot - @testset "insert @depot tag in path" begin + @testset "edge cases when inserting @depot tag in path" begin + # insert @depot only once for first match test_harness() do mktempdir() do dir pushfirst!(DEPOT_PATH, dir) - path = dir*dir - @test Base.replace_depot_path(path) == "@depot"*dir + if Sys.iswindows() + # dirs start with a drive letter instead of a path separator + path = dir*Base.Filesystem.pathsep()*dir + @test Base.replace_depot_path(path) == "@depot"*Base.Filesystem.pathsep()*dir + else + path = dir*dir + @test Base.replace_depot_path(path) == "@depot"*dir + end + end + + # 55340 + empty!(DEPOT_PATH) + mktempdir() do dir + jlrc = joinpath(dir, "julia-rc2") + jl = joinpath(dir, "julia") + mkdir(jl) + push!(DEPOT_PATH, jl) + @test Base.replace_depot_path(jl) == "@depot" + @test Base.replace_depot_path(string(jl,Base.Filesystem.pathsep())) == + string("@depot",Base.Filesystem.pathsep()) + @test Base.replace_depot_path(jlrc) != "@depot-rc2" + @test Base.replace_depot_path(jlrc) == jlrc end end + # deal with and without trailing path separators test_harness() do mktempdir() do dir pushfirst!(DEPOT_PATH, dir) @@ -43,9 +67,9 @@ if !test_relocated_depot if isdirpath(DEPOT_PATH[1]) DEPOT_PATH[1] = dirname(DEPOT_PATH[1]) # strip trailing pathsep end - tag = joinpath("@depot", "") # append a pathsep + tag = string("@depot", Base.Filesystem.pathsep()) @test startswith(Base.replace_depot_path(path), tag) - DEPOT_PATH[1] = joinpath(DEPOT_PATH[1], "") # append a pathsep + DEPOT_PATH[1] = string(DEPOT_PATH[1], Base.Filesystem.pathsep()) @test startswith(Base.replace_depot_path(path), tag) popfirst!(DEPOT_PATH) @test !startswith(Base.replace_depot_path(path), tag) From 32ad9e60347ed83efe3778fd6f7a2702aadb3cfe Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 30 Sep 2024 22:32:58 -0400 Subject: [PATCH 282/548] change compiler to be stackless (#55575) This change ensures the compiler uses very little stack, making it compatible with running on any arbitrary system stack size and depths much more reliably. It also could be further modified now to easily add various forms of pause-able/resumable inference, since there is no implicit state on the stack--everything is local and explicit now. Whereas before, less than 900 frames would crash in less than a second: ``` $ time ./julia -e 'f(::Val{N}) where {N} = N <= 0 ? 0 : f(Val(N - 1)); f(Val(1000))' Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable. Internal error: during type inference of f(Base.Val{1000}) Encountered stack overflow. This might be caused by recursion over very long tuples or argument lists. [23763] signal 6: Abort trap: 6 in expression starting at none:1 __pthread_kill at /usr/lib/system/libsystem_kernel.dylib (unknown line) Allocations: 1 (Pool: 1; Big: 0); GC: 0 Abort trap: 6 real 0m0.233s user 0m0.165s sys 0m0.049s ```` Now: it is effectively unlimited, as long as you are willing to wait for it: ``` $ time ./julia -e 'f(::Val{N}) where {N} = N <= 0 ? 0 : f(Val(N - 1)); f(Val(50000))' info: inference of f(Base.Val{50000}) from f(Base.Val{N}) where {N} exceeding 2500 frames (may be slow). info: inference of f(Base.Val{50000}) from f(Base.Val{N}) where {N} exceeding 5000 frames (may be slow). info: inference of f(Base.Val{50000}) from f(Base.Val{N}) where {N} exceeding 10000 frames (may be slow). info: inference of f(Base.Val{50000}) from f(Base.Val{N}) where {N} exceeding 20000 frames (may be slow). info: inference of f(Base.Val{50000}) from f(Base.Val{N}) where {N} exceeding 40000 frames (may be slow). real 7m4.988s $ time ./julia -e 'f(::Val{N}) where {N} = N <= 0 ? 0 : f(Val(N - 1)); f(Val(1000))' real 0m0.214s user 0m0.164s sys 0m0.044s $ time ./julia -e '@noinline f(::Val{N}) where {N} = N <= 0 ? GC.safepoint() : f(Val(N - 1)); f(Val(5000))' info: inference of f(Base.Val{5000}) from f(Base.Val{N}) where {N} exceeding 2500 frames (may be slow). info: inference of f(Base.Val{5000}) from f(Base.Val{N}) where {N} exceeding 5000 frames (may be slow). real 0m8.609s user 0m8.358s sys 0m0.240s ``` --- base/compiler/abstractinterpretation.jl | 1402 +++++++++++++---------- base/compiler/inferencestate.jl | 102 +- base/compiler/ssair/ir.jl | 1 + base/compiler/ssair/irinterp.jl | 38 +- base/compiler/ssair/verify.jl | 5 +- base/compiler/tfuncs.jl | 102 +- base/compiler/typeinfer.jl | 237 ++-- base/compiler/types.jl | 8 + base/reflection.jl | 2 +- test/compiler/AbstractInterpreter.jl | 9 +- test/compiler/inference.jl | 107 -- 11 files changed, 1048 insertions(+), 965 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 68b8394b72c3d..96355f2a6b5dd 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -47,223 +47,210 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), matches = find_method_matches(interp, argtypes, atype; max_methods) if isa(matches, FailedMethodMatch) add_remark!(interp, sv, matches.reason) - return CallMeta(Any, Any, Effects(), NoCallInfo()) + return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) end (; valid_worlds, applicable, info) = matches update_valid_age!(sv, valid_worlds) - napplicable = length(applicable) + + # final result + gfresult = Future{CallMeta}() + # intermediate work for computing gfresult rettype = exctype = Bottom edges = MethodInstance[] conditionals = nothing # keeps refinement information of call argument types when the return type is boolean - seen = 0 # number of signatures actually inferred + seenall = true const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available - multiple_matches = napplicable > 1 fargs = arginfo.fargs all_effects = EFFECTS_TOTAL slotrefinements = nothing # keeps refinement information on slot types obtained from call signature - for i in 1:napplicable - match = applicable[i]::MethodMatch - method = match.method - sig = match.spec_types - if bail_out_toplevel_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) - # only infer concrete call sites in top-level expressions - add_remark!(interp, sv, "Refusing to infer non-concrete call site in top-level expression") - break - end - this_rt = Bottom - this_exct = Bottom - splitunions = false - # TODO: this used to trigger a bug in inference recursion detection, and is unmaintained now - # sigtuple = unwrap_unionall(sig)::DataType - # splitunions = 1 < unionsplitcost(sigtuple.parameters) * napplicable <= InferenceParams(interp).max_union_splitting - if splitunions - splitsigs = switchtupleunion(sig) - for sig_n in splitsigs - result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, si, sv) - (; rt, exct, edge, effects, volatile_inf_result) = result + # split the for loop off into a function, so that we can pause and restart it at will + i::Int = 1 + f = Core.Box(f) + atype = Core.Box(atype) + function infercalls(interp, sv) + napplicable = length(applicable) + multiple_matches = napplicable > 1 + while i <= napplicable + match = applicable[i]::MethodMatch + method = match.method + sig = match.spec_types + if bail_out_toplevel_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) + # only infer concrete call sites in top-level expressions + add_remark!(interp, sv, "Refusing to infer non-concrete call site in top-level expression") + seenall = false + break + end + # TODO: this is unmaintained now as it didn't seem to improve things, though it does avoid hard-coding the union split at the higher level, + # it also can hurt infer-ability of some constrained parameter types (e.g. quacks like a duck) + # sigtuple = unwrap_unionall(sig)::DataType + # splitunions = 1 < unionsplitcost(sigtuple.parameters) * napplicable <= InferenceParams(interp).max_union_splitting + #if splitunions + # splitsigs = switchtupleunion(sig) + # for sig_n in splitsigs + # result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, si, sv)::Future + # handle1(...) + # end + #end + mresult = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv)::Future + function handle1(interp, sv) + local (; rt, exct, edge, effects, volatile_inf_result) = mresult[] + this_conditional = ignorelimited(rt) + this_rt = widenwrappedconditional(rt) + this_exct = exct + # try constant propagation with argtypes for this match + # this is in preparation for inlining, or improving the return result this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] this_arginfo = ArgInfo(fargs, this_argtypes) const_call_result = abstract_call_method_with_const_args(interp, - result, f, this_arginfo, si, match, sv) + mresult[], f.contents, this_arginfo, si, match, sv) const_result = volatile_inf_result if const_call_result !== nothing - if const_call_result.rt ⊑ₚ rt - rt = const_call_result.rt + this_const_conditional = ignorelimited(const_call_result.rt) + this_const_rt = widenwrappedconditional(const_call_result.rt) + if this_const_rt ⊑ₚ this_rt + # As long as the const-prop result we have is not *worse* than + # what we found out on types, we'd like to use it. Even if the + # end result is exactly equivalent, it is likely that the IR + # we produced while constproping is better than that with + # generic types. + # Return type of const-prop' inference can be wider than that of non const-prop' inference + # e.g. in cases when there are cycles but cached result is still accurate + this_conditional = this_const_conditional + this_rt = this_const_rt (; effects, const_result, edge) = const_call_result elseif is_better_effects(const_call_result.effects, effects) (; effects, const_result, edge) = const_call_result else add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") end - if const_call_result.exct ⋤ exct - (; exct, const_result, edge) = const_call_result + # Treat the exception type separately. Currently, constprop often cannot determine the exception type + # because consistent-cy does not apply to exceptions. + if const_call_result.exct ⋤ this_exct + this_exct = const_call_result.exct + (; const_result, edge) = const_call_result else add_remark!(interp, sv, "[constprop] Discarded exception type because result was wider than inference") end end + all_effects = merge_effects(all_effects, effects) if const_result !== nothing if const_results === nothing - const_results = fill!(Vector{Union{Nothing,ConstResult}}(undef, #=TODO=#napplicable), nothing) + const_results = fill!(Vector{Union{Nothing,ConstResult}}(undef, napplicable), nothing) end const_results[i] = const_result end edge === nothing || push!(edges, edge) - this_rt = this_rt ⊔ₚ rt - this_exct = this_exct ⊔ₚ exct - if bail_out_call(interp, this_rt, sv) - break + @assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context" + if can_propagate_conditional(this_conditional, argtypes) + # The only case where we need to keep this in rt is where + # we can directly propagate the conditional to a slot argument + # that is not one of our arguments, otherwise we keep all the + # relevant information in `conditionals` below. + this_rt = this_conditional end - end - this_conditional = ignorelimited(this_rt) - this_rt = widenwrappedconditional(this_rt) - else - result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv) - (; rt, exct, edge, effects, volatile_inf_result) = result - this_conditional = ignorelimited(rt) - this_rt = widenwrappedconditional(rt) - this_exct = exct - # try constant propagation with argtypes for this match - # this is in preparation for inlining, or improving the return result - this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] - this_arginfo = ArgInfo(fargs, this_argtypes) - const_call_result = abstract_call_method_with_const_args(interp, - result, f, this_arginfo, si, match, sv) - const_result = volatile_inf_result - if const_call_result !== nothing - this_const_conditional = ignorelimited(const_call_result.rt) - this_const_rt = widenwrappedconditional(const_call_result.rt) - if this_const_rt ⊑ₚ this_rt - # As long as the const-prop result we have is not *worse* than - # what we found out on types, we'd like to use it. Even if the - # end result is exactly equivalent, it is likely that the IR - # we produced while constproping is better than that with - # generic types. - # Return type of const-prop' inference can be wider than that of non const-prop' inference - # e.g. in cases when there are cycles but cached result is still accurate - this_conditional = this_const_conditional - this_rt = this_const_rt - (; effects, const_result, edge) = const_call_result - elseif is_better_effects(const_call_result.effects, effects) - (; effects, const_result, edge) = const_call_result - else - add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") + + rettype = rettype ⊔ₚ this_rt + exctype = exctype ⊔ₚ this_exct + if has_conditional(𝕃ₚ, sv) && this_conditional !== Bottom && is_lattice_bool(𝕃ₚ, rettype) && fargs !== nothing + if conditionals === nothing + conditionals = Any[Bottom for _ in 1:length(argtypes)], + Any[Bottom for _ in 1:length(argtypes)] + end + for i = 1:length(argtypes) + cnd = conditional_argtype(𝕃ᵢ, this_conditional, sig, argtypes, i) + conditionals[1][i] = conditionals[1][i] ⊔ᵢ cnd.thentype + conditionals[2][i] = conditionals[2][i] ⊔ᵢ cnd.elsetype + end end - # Treat the exception type separately. Currently, constprop often cannot determine the exception type - # because consistent-cy does not apply to exceptions. - if const_call_result.exct ⋤ this_exct - this_exct = const_call_result.exct - (; const_result, edge) = const_call_result - else - add_remark!(interp, sv, "[constprop] Discarded exception type because result was wider than inference") + if i < napplicable && bail_out_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) + add_remark!(interp, sv, "Call inference reached maximally imprecise information. Bailing on.") + seenall = false + i = napplicable # break in outer function end + i += 1 + return true end - all_effects = merge_effects(all_effects, effects) - if const_result !== nothing - if const_results === nothing - const_results = fill!(Vector{Union{Nothing,ConstResult}}(undef, napplicable), nothing) - end - const_results[i] = const_result + if isready(mresult) && handle1(interp, sv) + continue + else + push!(sv.tasks, handle1) + return false end - edge === nothing || push!(edges, edge) - end - @assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context" - seen += 1 + end # while - if can_propagate_conditional(this_conditional, argtypes) - # The only case where we need to keep this in rt is where - # we can directly propagate the conditional to a slot argument - # that is not one of our arguments, otherwise we keep all the - # relevant information in `conditionals` below. - this_rt = this_conditional + if const_results !== nothing + @assert napplicable == nmatches(info) == length(const_results) + info = ConstCallInfo(info, const_results) end - rettype = rettype ⊔ₚ this_rt - exctype = exctype ⊔ₚ this_exct - if has_conditional(𝕃ₚ, sv) && this_conditional !== Bottom && is_lattice_bool(𝕃ₚ, rettype) && fargs !== nothing - if conditionals === nothing - conditionals = Any[Bottom for _ in 1:length(argtypes)], - Any[Bottom for _ in 1:length(argtypes)] + if seenall + if !fully_covering(matches) || any_ambig(matches) + # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + all_effects = Effects(all_effects; nothrow=false) + exctype = exctype ⊔ₚ MethodError end - for i = 1:length(argtypes) - cnd = conditional_argtype(𝕃ᵢ, this_conditional, sig, argtypes, i) - conditionals[1][i] = conditionals[1][i] ⊔ᵢ cnd.thentype - conditionals[2][i] = conditionals[2][i] ⊔ᵢ cnd.elsetype + if sv isa InferenceState && fargs !== nothing + slotrefinements = collect_slot_refinements(𝕃ᵢ, applicable, argtypes, fargs, sv) end - end - if bail_out_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) - add_remark!(interp, sv, "Call inference reached maximally imprecise information. Bailing on.") - break - end - end - - if const_results !== nothing - @assert napplicable == nmatches(info) == length(const_results) - info = ConstCallInfo(info, const_results) - end - - if seen ≠ napplicable - # there is unanalyzed candidate, widen type and effects to the top - rettype = exctype = Any - all_effects = Effects() - else - if !fully_covering(matches) || any_ambig(matches) - # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - all_effects = Effects(all_effects; nothrow=false) - exctype = exctype ⊔ₚ MethodError - end - if sv isa InferenceState && fargs !== nothing - slotrefinements = collect_slot_refinements(𝕃ᵢ, applicable, argtypes, fargs, sv) - end - end - - rettype = from_interprocedural!(interp, rettype, sv, arginfo, conditionals) - - # Also considering inferring the compilation signature for this method, so - # it is available to the compiler in case it ends up needing it. - if (isa(sv, InferenceState) && infer_compilation_signature(interp) && - (1 == seen == napplicable) && rettype !== Any && rettype !== Bottom && - !is_removable_if_unused(all_effects)) - match = applicable[1]::MethodMatch - method = match.method - sig = match.spec_types - mi = specialize_method(match; preexisting=true) - if mi !== nothing && !const_prop_methodinstance_heuristic(interp, mi, arginfo, sv) - csig = get_compileable_sig(method, sig, match.sparams) - if csig !== nothing && csig !== sig - abstract_call_method(interp, method, csig, match.sparams, multiple_matches, StmtInfo(false), sv) + else + # there is unanalyzed candidate, widen type and effects to the top + rettype = exctype = Any + all_effects = Effects() + end + + rettype = from_interprocedural!(interp, rettype, sv, arginfo, conditionals) + + # Also considering inferring the compilation signature for this method, so + # it is available to the compiler in case it ends up needing it. + if (isa(sv, InferenceState) && infer_compilation_signature(interp) && + (seenall && 1 == napplicable) && rettype !== Any && rettype !== Bottom && + !is_removable_if_unused(all_effects)) + match = applicable[1]::MethodMatch + method = match.method + sig = match.spec_types + mi = specialize_method(match; preexisting=true) + if mi !== nothing && !const_prop_methodinstance_heuristic(interp, mi, arginfo, sv) + csig = get_compileable_sig(method, sig, match.sparams) + if csig !== nothing && csig !== sig + abstract_call_method(interp, method, csig, match.sparams, multiple_matches, StmtInfo(false), sv)::Future + end end end - end - if call_result_unused(si) && !(rettype === Bottom) - add_remark!(interp, sv, "Call result type was widened because the return value is unused") - # We're mainly only here because the optimizer might want this code, - # but we ourselves locally don't typically care about it locally - # (beyond checking if it always throws). - # So avoid adding an edge, since we don't want to bother attempting - # to improve our result even if it does change (to always throw), - # and avoid keeping track of a more complex result type. - rettype = Any - end - any_slot_refined = slotrefinements !== nothing - add_call_backedges!(interp, rettype, all_effects, any_slot_refined, edges, matches, atype, sv) - if isa(sv, InferenceState) - # TODO (#48913) implement a proper recursion handling for irinterp: - # This works just because currently the `:terminate` condition guarantees that - # irinterp doesn't fail into unresolved cycles, but it's not a good solution. - # We should revisit this once we have a better story for handling cycles in irinterp. - if !isempty(sv.pclimitations) # remove self, if present - delete!(sv.pclimitations, sv) - for caller in callers_in_cycle(sv) - delete!(sv.pclimitations, caller) + if call_result_unused(si) && !(rettype === Bottom) + add_remark!(interp, sv, "Call result type was widened because the return value is unused") + # We're mainly only here because the optimizer might want this code, + # but we ourselves locally don't typically care about it locally + # (beyond checking if it always throws). + # So avoid adding an edge, since we don't want to bother attempting + # to improve our result even if it does change (to always throw), + # and avoid keeping track of a more complex result type. + rettype = Any + end + any_slot_refined = slotrefinements !== nothing + add_call_backedges!(interp, rettype, all_effects, any_slot_refined, edges, matches, atype.contents, sv) + if isa(sv, InferenceState) + # TODO (#48913) implement a proper recursion handling for irinterp: + # This works just because currently the `:terminate` condition guarantees that + # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # We should revisit this once we have a better story for handling cycles in irinterp. + if !isempty(sv.pclimitations) # remove self, if present + delete!(sv.pclimitations, sv) + for caller in callers_in_cycle(sv) + delete!(sv.pclimitations, caller) + end end end - end - return CallMeta(rettype, exctype, all_effects, info, slotrefinements) + gfresult[] = CallMeta(rettype, exctype, all_effects, info, slotrefinements) + return true + end # infercalls + # start making progress on the first call + infercalls(interp, sv) || push!(sv.tasks, infercalls) + return gfresult end struct FailedMethodMatch @@ -607,9 +594,9 @@ function abstract_call_method(interp::AbstractInterpreter, hardlimit::Bool, si::StmtInfo, sv::AbsIntState) sigtuple = unwrap_unionall(sig) sigtuple isa DataType || - return MethodCallResult(Any, Any, false, false, nothing, Effects()) + return Future(MethodCallResult(Any, Any, false, false, nothing, Effects())) all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) || - return MethodCallResult(Union{}, Any, false, false, nothing, EFFECTS_THROWS) # catch bad type intersections early + return Future(MethodCallResult(Union{}, Any, false, false, nothing, EFFECTS_THROWS)) # catch bad type intersections early if is_nospecializeinfer(method) sig = get_nospecializeinfer_sig(method, sig, sparams) @@ -634,7 +621,7 @@ function abstract_call_method(interp::AbstractInterpreter, # we have a self-cycle in the call-graph, but not in the inference graph (typically): # break this edge now (before we record it) by returning early # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) - return MethodCallResult(Any, Any, true, true, nothing, Effects()) + return Future(MethodCallResult(Any, Any, true, true, nothing, Effects())) end topmost = nothing edgecycle = true @@ -689,7 +676,7 @@ function abstract_call_method(interp::AbstractInterpreter, # since it's very unlikely that we'll try to inline this, # or want make an invoke edge to its calling convention return type. # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) - return MethodCallResult(Any, Any, true, true, nothing, Effects()) + return Future(MethodCallResult(Any, Any, true, true, nothing, Effects())) end add_remark!(interp, sv, washardlimit ? RECURSION_MSG_HARDLIMIT : RECURSION_MSG) # TODO (#48913) implement a proper recursion handling for irinterp: @@ -745,31 +732,7 @@ function abstract_call_method(interp::AbstractInterpreter, sparams = recomputed[2]::SimpleVector end - (; rt, exct, edge, effects, volatile_inf_result) = typeinf_edge(interp, method, sig, sparams, sv) - - if edge === nothing - edgecycle = edgelimited = true - end - - # we look for the termination effect override here as well, since the :terminates effect - # may have been tainted due to recursion at this point even if it's overridden - if is_effect_overridden(sv, :terminates_globally) - # this frame is known to terminate - effects = Effects(effects, terminates=true) - elseif is_effect_overridden(method, :terminates_globally) - # this edge is known to terminate - effects = Effects(effects; terminates=true) - elseif edgecycle - # Some sort of recursion was detected. - if edge !== nothing && !edgelimited && !is_edge_recursed(edge, sv) - # no `MethodInstance` cycles -- don't taint :terminate - else - # we cannot guarantee that the call will terminate - effects = Effects(effects; terminates=false) - end - end - - return MethodCallResult(rt, exct, edgecycle, edgelimited, edge, effects, volatile_inf_result) + return typeinf_edge(interp, method, sig, sparams, sv, edgecycle, edgelimited) end function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState, @@ -1331,7 +1294,7 @@ const_prop_result(inf_result::InferenceResult) = inf_result.ipo_effects, inf_result.linfo) # return cached result of constant analysis -return_cached_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) = +return_localcache_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) = const_prop_result(inf_result) function compute_forwarded_argtypes(interp::AbstractInterpreter, arginfo::ArgInfo, sv::AbsIntState) @@ -1361,7 +1324,7 @@ function const_prop_call(interp::AbstractInterpreter, return nothing end @assert inf_result.linfo === mi "MethodInstance for cached inference result does not match" - return return_cached_result(interp, inf_result, sv) + return return_localcache_result(interp, inf_result, sv) end overridden_by_const = falses(length(argtypes)) for i = 1:length(argtypes) @@ -1375,7 +1338,7 @@ function const_prop_call(interp::AbstractInterpreter, end # perform fresh constant prop' inf_result = InferenceResult(mi, argtypes, overridden_by_const) - frame = InferenceState(inf_result, #=cache_mode=#:local, interp) + frame = InferenceState(inf_result, #=cache_mode=#:local, interp) # TODO: this should also be converted to a stackless Future if frame === nothing add_remark!(interp, sv, "[constprop] Could not retrieve the source") return nothing # this is probably a bad generated function (unsound), but just ignore it @@ -1517,9 +1480,9 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) widet = typ.typ if isa(widet, DataType) if widet.name === Tuple.name - return AbstractIterationResult(typ.fields, nothing) + return Future(AbstractIterationResult(typ.fields, nothing)) elseif widet.name === _NAMEDTUPLE_NAME - return AbstractIterationResult(typ.fields, nothing) + return Future(AbstractIterationResult(typ.fields, nothing)) end end end @@ -1527,7 +1490,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) if isa(typ, Const) val = typ.val if isa(val, SimpleVector) || isa(val, Tuple) || isa(val, NamedTuple) - return AbstractIterationResult(Any[ Const(val[i]) for i in 1:length(val) ], nothing) # avoid making a tuple Generator here! + return Future(AbstractIterationResult(Any[ Const(val[i]) for i in 1:length(val) ], nothing)) # avoid making a tuple Generator here! end end @@ -1544,18 +1507,18 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) # refine the Union to remove elements that are not valid tags for objects filter!(@nospecialize(x) -> valid_as_lattice(x, true), utis) if length(utis) == 0 - return AbstractIterationResult(Any[], nothing) # oops, this statement was actually unreachable + return Future(AbstractIterationResult(Any[], nothing)) # oops, this statement was actually unreachable elseif length(utis) == 1 tti = utis[1] tti0 = rewrap_unionall(tti, tti0) else if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) - return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing, Effects())) end ltp = length((utis[1]::DataType).parameters) for t in utis if length((t::DataType).parameters) != ltp - return AbstractIterationResult(Any[Vararg{Any}], nothing) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing)) end end result = Any[ Union{} for _ in 1:ltp ] @@ -1566,14 +1529,14 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) result[j] = tmerge(result[j], rewrap_unionall(tps[j], tti0)) end end - return AbstractIterationResult(result, nothing) + return Future(AbstractIterationResult(result, nothing)) end end if tti0 <: Tuple if isa(tti0, DataType) - return AbstractIterationResult(Any[ p for p in tti0.parameters ], nothing) + return Future(AbstractIterationResult(Any[ p for p in tti0.parameters ], nothing)) elseif !isa(tti, DataType) - return AbstractIterationResult(Any[Vararg{Any}], nothing) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing)) else len = length(tti.parameters) last = tti.parameters[len] @@ -1586,17 +1549,17 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) elts[len] = Vararg{elts[len]} end end - return AbstractIterationResult(elts, nothing) + return Future(AbstractIterationResult(elts, nothing)) end elseif tti0 === SimpleVector - return AbstractIterationResult(Any[Vararg{Any}], nothing) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing)) elseif tti0 === Any - return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing, Effects())) elseif tti0 <: Array || tti0 <: GenericMemory if eltype(tti0) === Union{} - return AbstractIterationResult(Any[], nothing) + return Future(AbstractIterationResult(Any[], nothing)) end - return AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing) + return Future(AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing)) else return abstract_iteration(interp, itft, typ, sv) end @@ -1607,95 +1570,144 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n if isa(itft, Const) iteratef = itft.val else - return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) + return Future(AbstractIterationResult(Any[Vararg{Any}], nothing, Effects())) end @assert !isvarargtype(itertype) - call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[itft, itertype]), StmtInfo(true), sv) - stateordonet = call.rt - info = call.info - # Return Bottom if this is not an iterator. - # WARNING: Changes to the iteration protocol must be reflected here, - # this is not just an optimization. - # TODO: this doesn't realize that Array, GenericMemory, SimpleVector, Tuple, and NamedTuple do not use the iterate protocol - stateordonet === Bottom && return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(CallMeta[CallMeta(Bottom, Any, call.effects, info)], true)) - valtype = statetype = Bottom - ret = Any[] - calls = CallMeta[call] - stateordonet_widened = widenconst(stateordonet) - 𝕃ᵢ = typeinf_lattice(interp) - # Try to unroll the iteration up to max_tuple_splat, which covers any finite - # length iterators, or interesting prefix - while true - if stateordonet_widened === Nothing - return AbstractIterationResult(ret, AbstractIterationInfo(calls, true)) - end - if Nothing <: stateordonet_widened || length(ret) >= InferenceParams(interp).max_tuple_splat - break - end - if !isa(stateordonet_widened, DataType) || !(stateordonet_widened <: Tuple) || isvatuple(stateordonet_widened) || length(stateordonet_widened.parameters) != 2 - break - end - nstatetype = getfield_tfunc(𝕃ᵢ, stateordonet, Const(2)) - # If there's no new information in this statetype, don't bother continuing, - # the iterator won't be finite. - if ⊑(𝕃ᵢ, nstatetype, statetype) - return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), EFFECTS_THROWS) - end - valtype = getfield_tfunc(𝕃ᵢ, stateordonet, Const(1)) - push!(ret, valtype) - statetype = nstatetype - call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), StmtInfo(true), sv) - stateordonet = call.rt + iterateresult = Future{AbstractIterationResult}() + call1future = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[itft, itertype]), StmtInfo(true), sv)::Future + function inferiterate(interp, sv) + call1 = call1future[] + stateordonet = call1.rt + # Return Bottom if this is not an iterator. + # WARNING: Changes to the iteration protocol must be reflected here, + # this is not just an optimization. + # TODO: this doesn't realize that Array, GenericMemory, SimpleVector, Tuple, and NamedTuple do not use the iterate protocol + if stateordonet === Bottom + iterateresult[] = AbstractIterationResult(Any[Bottom], AbstractIterationInfo(CallMeta[CallMeta(Bottom, Any, call1.effects, call1.info)], true)) + return true + end stateordonet_widened = widenconst(stateordonet) - push!(calls, call) - end - # From here on, we start asking for results on the widened types, rather than - # the precise (potentially const) state type - # statetype and valtype are reinitialized in the first iteration below from the - # (widened) stateordonet, which has not yet been fully analyzed in the loop above - valtype = statetype = Bottom - may_have_terminated = Nothing <: stateordonet_widened - while valtype !== Any - nounion = typeintersect(stateordonet_widened, Tuple{Any,Any}) - if nounion !== Union{} && !isa(nounion, DataType) - # nounion is of a type we cannot handle - valtype = Any - break - end - if nounion === Union{} || (nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype) - # reached a fixpoint or iterator failed/gave invalid answer - if !hasintersect(stateordonet_widened, Nothing) - # ... but cannot terminate - if !may_have_terminated - # ... and cannot have terminated prior to this loop - return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), Effects()) - else - # iterator may have terminated prior to this loop, but not during it - valtype = Bottom + calls = CallMeta[call1] + valtype = statetype = Bottom + ret = Any[] + 𝕃ᵢ = typeinf_lattice(interp) + may_have_terminated = false + local call2future::Future{CallMeta} + + nextstate::UInt8 = 0x0 + function inferiterate_2arg(interp, sv) + if nextstate === 0x1 + nextstate = 0xff + @goto state1 + elseif nextstate === 0x2 + nextstate = 0xff + @goto state2 + else + @assert nextstate === 0x0 + nextstate = 0xff + end + + # Try to unroll the iteration up to max_tuple_splat, which covers any finite + # length iterators, or interesting prefix + while true + if stateordonet_widened === Nothing + iterateresult[] = AbstractIterationResult(ret, AbstractIterationInfo(calls, true)) + return true + end + if Nothing <: stateordonet_widened || length(ret) >= InferenceParams(interp).max_tuple_splat + break + end + if !isa(stateordonet_widened, DataType) || !(stateordonet_widened <: Tuple) || isvatuple(stateordonet_widened) || length(stateordonet_widened.parameters) != 2 + break + end + nstatetype = getfield_tfunc(𝕃ᵢ, stateordonet, Const(2)) + # If there's no new information in this statetype, don't bother continuing, + # the iterator won't be finite. + if ⊑(𝕃ᵢ, nstatetype, statetype) + iterateresult[] = AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), EFFECTS_THROWS) + return true + end + valtype = getfield_tfunc(𝕃ᵢ, stateordonet, Const(1)) + push!(ret, valtype) + statetype = nstatetype + call2future = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), StmtInfo(true), sv)::Future + if !isready(call2future) + nextstate = 0x1 + return false + @label state1 + end + let call = call2future[] + push!(calls, call) + stateordonet = call.rt + stateordonet_widened = widenconst(stateordonet) end end - break - end - valtype = tmerge(valtype, nounion.parameters[1]) - statetype = tmerge(statetype, nounion.parameters[2]) - call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), StmtInfo(true), sv) - push!(calls, call) - stateordonet = call.rt - stateordonet_widened = widenconst(stateordonet) - end - if valtype !== Union{} - push!(ret, Vararg{valtype}) + # From here on, we start asking for results on the widened types, rather than + # the precise (potentially const) state type + # statetype and valtype are reinitialized in the first iteration below from the + # (widened) stateordonet, which has not yet been fully analyzed in the loop above + valtype = statetype = Bottom + may_have_terminated = Nothing <: stateordonet_widened + while valtype !== Any + nounion = typeintersect(stateordonet_widened, Tuple{Any,Any}) + if nounion !== Union{} && !isa(nounion, DataType) + # nounion is of a type we cannot handle + valtype = Any + break + end + if nounion === Union{} || (nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype) + # reached a fixpoint or iterator failed/gave invalid answer + if !hasintersect(stateordonet_widened, Nothing) + # ... but cannot terminate + if may_have_terminated + # ... and iterator may have terminated prior to this loop, but not during it + valtype = Bottom + else + # ... or cannot have terminated prior to this loop + iterateresult[] = AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), Effects()) + return true + end + end + break + end + valtype = tmerge(valtype, nounion.parameters[1]) + statetype = tmerge(statetype, nounion.parameters[2]) + call2future = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[Const(iteratef), itertype, statetype]), StmtInfo(true), sv)::Future + if !isready(call2future) + nextstate = 0x2 + return false + @label state2 + end + let call = call2future[] + push!(calls, call) + stateordonet = call.rt + stateordonet_widened = widenconst(stateordonet) + end + end + if valtype !== Union{} + push!(ret, Vararg{valtype}) + end + iterateresult[] = AbstractIterationResult(ret, AbstractIterationInfo(calls, false)) + return true + end # inferiterate_2arg + # continue making progress as much as possible, on iterate(arg, state) + inferiterate_2arg(interp, sv) || push!(sv.tasks, inferiterate_2arg) + return true + end # inferiterate + # continue making progress as soon as possible, on iterate(arg) + if !(isready(call1future) && inferiterate(interp, sv)) + push!(sv.tasks, inferiterate) end - return AbstractIterationResult(ret, AbstractIterationInfo(calls, false)) + return iterateresult end # do apply(af, fargs...), where af is a function value function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState, max_methods::Int=get_max_methods(interp, sv)) - itft = argtype_by_index(argtypes, 2) + itft = Core.Box(argtype_by_index(argtypes, 2)) aft = argtype_by_index(argtypes, 3) - (itft === Bottom || aft === Bottom) && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + (itft.contents === Bottom || aft === Bottom) && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) aargtypes = argtype_tail(argtypes, 4) aftw = widenconst(aft) if !isa(aft, Const) && !isa(aft, PartialOpaque) && (!isType(aftw) || has_free_typevars(aftw)) @@ -1703,100 +1715,155 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: add_remark!(interp, sv, "Core._apply_iterate called on a function of a non-concrete type") # bail now, since it seems unlikely that abstract_call will be able to do any better after splitting # this also ensures we don't call abstract_call_gf_by_type below on an IntrinsicFunction or Builtin - return CallMeta(Any, Any, Effects(), NoCallInfo()) + return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) end end res = Union{} - nargs = length(aargtypes) splitunions = 1 < unionsplitcost(typeinf_lattice(interp), aargtypes) <= InferenceParams(interp).max_apply_union_enum - ctypes = [Any[aft]] - infos = Vector{MaybeAbstractIterationInfo}[MaybeAbstractIterationInfo[]] - effects = EFFECTS_TOTAL - for i = 1:nargs - ctypes´ = Vector{Any}[] - infos′ = Vector{MaybeAbstractIterationInfo}[] - for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) - if !isvarargtype(ti) - (;cti, info, ai_effects) = precise_container_type(interp, itft, ti, sv) - else - (;cti, info, ai_effects) = precise_container_type(interp, itft, unwrapva(ti), sv) - # We can't represent a repeating sequence of the same types, - # so tmerge everything together to get one type that represents - # everything. - argt = cti[end] - if isvarargtype(argt) - argt = unwrapva(argt) + ctypes::Vector{Vector{Any}} = [Any[aft]] + infos::Vector{Vector{MaybeAbstractIterationInfo}} = Vector{MaybeAbstractIterationInfo}[MaybeAbstractIterationInfo[]] + all_effects::Effects = EFFECTS_TOTAL + retinfos = ApplyCallInfo[] + retinfo = UnionSplitApplyCallInfo(retinfos) + exctype = Union{} + ctypes´ = Vector{Any}[] + infos´ = Vector{MaybeAbstractIterationInfo}[] + local ti, argtypesi + local ctfuture::Future{AbstractIterationResult} + local callfuture::Future{CallMeta} + + applyresult = Future{CallMeta}() + # split the rest into a resumable state machine + i::Int = 1 + j::Int = 1 + nextstate::UInt8 = 0x0 + function infercalls(interp, sv) + # n.b. Remember that variables will lose their values across restarts, + # so be sure to manually hoist any values that must be preserved and do + # not rely on program order. + # This is a little more complex than the closure continuations often used elsewhere, but avoids needing to manage all of that indentation + if nextstate === 0x1 + nextstate = 0xff + @goto state1 + elseif nextstate === 0x2 + nextstate = 0xff + @goto state2 + elseif nextstate === 0x3 + nextstate = 0xff + @goto state3 + else + @assert nextstate === 0x0 + nextstate = 0xff + end + while i <= length(aargtypes) + argtypesi = (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) + i += 1 + j = 1 + while j <= length(argtypesi) + ti = argtypesi[j] + j += 1 + if !isvarargtype(ti) + ctfuture = precise_container_type(interp, itft.contents, ti, sv)::Future + if !isready(ctfuture) + nextstate = 0x1 + return false + @label state1 + end + (;cti, info, ai_effects) = ctfuture[] + else + ctfuture = precise_container_type(interp, itft.contents, unwrapva(ti), sv)::Future + if !isready(ctfuture) + nextstate = 0x2 + return false + @label state2 + end + (;cti, info, ai_effects) = ctfuture[] + # We can't represent a repeating sequence of the same types, + # so tmerge everything together to get one type that represents + # everything. + argt = cti[end] + if isvarargtype(argt) + argt = unwrapva(argt) + end + for k in 1:(length(cti)-1) + argt = tmerge(argt, cti[k]) + end + cti = Any[Vararg{argt}] end - for i in 1:(length(cti)-1) - argt = tmerge(argt, cti[i]) + all_effects = merge_effects(all_effects, ai_effects) + if info !== nothing + for call in info.each + all_effects = merge_effects(all_effects, call.effects) + end + end + if any(@nospecialize(t) -> t === Bottom, cti) + continue + end + for k = 1:length(ctypes) + ct = ctypes[k] + if isvarargtype(ct[end]) + # This is vararg, we're not gonna be able to do any inlining, + # drop the info + info = nothing + tail = tuple_tail_elem(typeinf_lattice(interp), unwrapva(ct[end]), cti) + push!(ctypes´, push!(ct[1:(end - 1)], tail)) + else + push!(ctypes´, append!(ct[:], cti)) + end + push!(infos´, push!(copy(infos[k]), info)) end - cti = Any[Vararg{argt}] end - effects = merge_effects(effects, ai_effects) - if info !== nothing - for call in info.each - effects = merge_effects(effects, call.effects) + # swap for the new array and empty the temporary one + ctypes´, ctypes = ctypes, ctypes´ + infos´, infos = infos, infos´ + empty!(ctypes´) + empty!(infos´) + end + all_effects.nothrow || (exctype = Any) + + i = 1 + while i <= length(ctypes) + ct = ctypes[i] + lct = length(ct) + # truncate argument list at the first Vararg + for k = 1:lct-1 + cti = ct[k] + if isvarargtype(cti) + ct[k] = tuple_tail_elem(typeinf_lattice(interp), unwrapva(cti), ct[(k+1):lct]) + resize!(ct, k) + break end end - if any(@nospecialize(t) -> t === Bottom, cti) - continue + callfuture = abstract_call(interp, ArgInfo(nothing, ct), si, sv, max_methods)::Future + if !isready(callfuture) + nextstate = 0x3 + return false + @label state3 end - for j = 1:length(ctypes) - ct = ctypes[j]::Vector{Any} - if isvarargtype(ct[end]) - # This is vararg, we're not gonna be able to do any inlining, - # drop the info - info = nothing - tail = tuple_tail_elem(typeinf_lattice(interp), unwrapva(ct[end]), cti) - push!(ctypes´, push!(ct[1:(end - 1)], tail)) - else - push!(ctypes´, append!(ct[:], cti)) + let (; info, rt, exct, effects) = callfuture[] + push!(retinfos, ApplyCallInfo(info, infos[i])) + res = tmerge(typeinf_lattice(interp), res, rt) + exctype = tmerge(typeinf_lattice(interp), exctype, exct) + all_effects = merge_effects(all_effects, effects) + if i < length(ctypes) && bail_out_apply(interp, InferenceLoopState(ctypes[i], res, all_effects), sv) + add_remark!(interp, sv, "_apply_iterate inference reached maximally imprecise information. Bailing on.") + # there is unanalyzed candidate, widen type and effects to the top + let retinfo = NoCallInfo() # NOTE this is necessary to prevent the inlining processing + applyresult[] = CallMeta(Any, Any, Effects(), retinfo) + return true + end end - push!(infos′, push!(copy(infos[j]), info)) end + i += 1 end - ctypes = ctypes´ - infos = infos′ - end - retinfos = ApplyCallInfo[] - retinfo = UnionSplitApplyCallInfo(retinfos) - napplicable = length(ctypes) - seen = 0 - exct = effects.nothrow ? Union{} : Any - for i = 1:napplicable - ct = ctypes[i] - arginfo = infos[i] - lct = length(ct) - # truncate argument list at the first Vararg - for i = 1:lct-1 - cti = ct[i] - if isvarargtype(cti) - ct[i] = tuple_tail_elem(typeinf_lattice(interp), unwrapva(cti), ct[(i+1):lct]) - resize!(ct, i) - break - end - end - call = abstract_call(interp, ArgInfo(nothing, ct), si, sv, max_methods) - seen += 1 - push!(retinfos, ApplyCallInfo(call.info, arginfo)) - res = tmerge(typeinf_lattice(interp), res, call.rt) - exct = tmerge(typeinf_lattice(interp), exct, call.exct) - effects = merge_effects(effects, call.effects) - if bail_out_apply(interp, InferenceLoopState(ct, res, effects), sv) - add_remark!(interp, sv, "_apply_iterate inference reached maximally imprecise information. Bailing on.") - break - end - end - if seen ≠ napplicable - # there is unanalyzed candidate, widen type and effects to the top - res = Any - exct = Any - effects = Effects() - retinfo = NoCallInfo() # NOTE this is necessary to prevent the inlining processing + # TODO: Add a special info type to capture all the iteration info. + # For now, only propagate info if we don't also union-split the iteration + applyresult[] = CallMeta(res, exctype, all_effects, retinfo) + return true end - # TODO: Add a special info type to capture all the iteration info. - # For now, only propagate info if we don't also union-split the iteration - return CallMeta(res, exct, effects, retinfo) + # start making progress on the first call + infercalls(interp, sv) || push!(sv.tasks, infercalls) + return applyresult end function argtype_by_index(argtypes::Vector{Any}, i::Int) @@ -2135,66 +2202,69 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt argtypes = arginfo.argtypes ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) - ft === Bottom && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + ft === Bottom && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) - isexact || return CallMeta(Any, Any, Effects(), NoCallInfo()) + isexact || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) unwrapped = unwrap_unionall(types) - types === Bottom && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + types === Bottom && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) if !(unwrapped isa DataType && unwrapped.name === Tuple.name) - return CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo()) + return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) end argtype = argtypes_to_type(argtype_tail(argtypes, 4)) nargtype = typeintersect(types, argtype) - nargtype === Bottom && return CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo()) - nargtype isa DataType || return CallMeta(Any, Any, Effects(), NoCallInfo()) # other cases are not implemented below - isdispatchelem(ft) || return CallMeta(Any, Any, Effects(), NoCallInfo()) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below + nargtype === Bottom && return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) + nargtype isa DataType || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # other cases are not implemented below + isdispatchelem(ft) || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below ft = ft::DataType lookupsig = rewrap_unionall(Tuple{ft, unwrapped.parameters...}, types)::Type nargtype = Tuple{ft, nargtype.parameters...} argtype = Tuple{ft, argtype.parameters...} match, valid_worlds = findsup(lookupsig, method_table(interp)) - match === nothing && return CallMeta(Any, Any, Effects(), NoCallInfo()) + match === nothing && return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) update_valid_age!(sv, valid_worlds) method = match.method tienv = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector - ti = tienv[1]; env = tienv[2]::SimpleVector - result = abstract_call_method(interp, method, ti, env, false, si, sv) - (; rt, exct, edge, effects, volatile_inf_result) = result + ti = tienv[1] + env = tienv[2]::SimpleVector + mresult = abstract_call_method(interp, method, ti, env, false, si, sv)::Future match = MethodMatch(ti, env, method, argtype <: method.sig) - res = nothing - sig = match.spec_types - argtypes′ = invoke_rewrite(argtypes) - fargs = arginfo.fargs - fargs′ = fargs === nothing ? nothing : invoke_rewrite(fargs) - arginfo = ArgInfo(fargs′, argtypes′) - # # typeintersect might have narrowed signature, but the accuracy gain doesn't seem worth the cost involved with the lattice comparisons - # for i in 1:length(argtypes′) - # t, a = ti.parameters[i], argtypes′[i] - # argtypes′[i] = t ⊑ a ? t : a - # end - 𝕃ₚ = ipo_lattice(interp) - ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) - f = singleton_type(ft′) - invokecall = InvokeCall(types, lookupsig) - const_call_result = abstract_call_method_with_const_args(interp, - result, f, arginfo, si, match, sv, invokecall) - const_result = volatile_inf_result - if const_call_result !== nothing - if const_call_result.rt ⊑ rt - (; rt, effects, const_result, edge) = const_call_result + return Future{CallMeta}(mresult, interp, sv) do result, interp, sv + (; rt, exct, edge, effects, volatile_inf_result) = result + res = nothing + sig = match.spec_types + argtypes′ = invoke_rewrite(argtypes) + fargs = arginfo.fargs + fargs′ = fargs === nothing ? nothing : invoke_rewrite(fargs) + arginfo = ArgInfo(fargs′, argtypes′) + # # typeintersect might have narrowed signature, but the accuracy gain doesn't seem worth the cost involved with the lattice comparisons + # for i in 1:length(argtypes′) + # t, a = ti.parameters[i], argtypes′[i] + # argtypes′[i] = t ⊑ a ? t : a + # end + 𝕃ₚ = ipo_lattice(interp) + ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) + f = singleton_type(ft′) + invokecall = InvokeCall(types, lookupsig) + const_call_result = abstract_call_method_with_const_args(interp, + result, f, arginfo, si, match, sv, invokecall) + const_result = volatile_inf_result + if const_call_result !== nothing + if const_call_result.rt ⊑ rt + (; rt, effects, const_result, edge) = const_call_result + end + if const_call_result.exct ⋤ exct + (; exct, const_result, edge) = const_call_result + end end - if const_call_result.exct ⋤ exct - (; exct, const_result, edge) = const_call_result + rt = from_interprocedural!(interp, rt, sv, arginfo, sig) + info = InvokeCallInfo(match, const_result) + edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) + if !match.fully_covers + effects = Effects(effects; nothrow=false) + exct = exct ⊔ TypeError end + return CallMeta(rt, exct, effects, info) end - rt = from_interprocedural!(interp, rt, sv, arginfo, sig) - info = InvokeCallInfo(match, const_result) - edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) - if !match.fully_covers - effects = Effects(effects; nothrow=false) - exct = exct ⊔ TypeError - end - return CallMeta(rt, exct, effects, info) end function invoke_rewrite(xs::Vector{Any}) @@ -2207,10 +2277,12 @@ end function abstract_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState) if length(argtypes) == 3 finalizer_argvec = Any[argtypes[2], argtypes[3]] - call = abstract_call(interp, ArgInfo(nothing, finalizer_argvec), StmtInfo(false), sv, #=max_methods=#1) - return CallMeta(Nothing, Any, Effects(), FinalizerInfo(call.info, call.effects)) + call = abstract_call(interp, ArgInfo(nothing, finalizer_argvec), StmtInfo(false), sv, #=max_methods=#1)::Future + return Future{CallMeta}(call, interp, sv) do call, interp, sv + return CallMeta(Nothing, Any, Effects(), FinalizerInfo(call.info, call.effects)) + end end - return CallMeta(Nothing, Any, Effects(), NoCallInfo()) + return Future(CallMeta(Nothing, Any, Effects(), NoCallInfo())) end function abstract_throw(interp::AbstractInterpreter, argtypes::Vector{Any}, ::AbsIntState) @@ -2228,7 +2300,7 @@ function abstract_throw(interp::AbstractInterpreter, argtypes::Vector{Any}, ::Ab else exct = ArgumentError end - return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) + return Future(CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo())) end function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vector{Any}, ::AbsIntState) @@ -2240,7 +2312,7 @@ function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vecto ⊔ = join(typeinf_lattice(interp)) MethodError ⊔ ArgumentError end - return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) + return Future(CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo())) end # call where the function is known exactly @@ -2285,60 +2357,70 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), end end end - return CallMeta(rt, exct, effects, NoCallInfo(), refinements) + return Future(CallMeta(rt, exct, effects, NoCallInfo(), refinements)) elseif isa(f, Core.OpaqueClosure) # calling an OpaqueClosure about which we have no information returns no information - return CallMeta(typeof(f).parameters[2], Any, Effects(), NoCallInfo()) + return Future(CallMeta(typeof(f).parameters[2], Any, Effects(), NoCallInfo())) elseif f === TypeVar && !isvarargtype(argtypes[end]) # Manually look through the definition of TypeVar to # make sure to be able to get `PartialTypeVar`s out. - 2 ≤ la ≤ 4 || return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) - n = argtypes[2] - ub_var = Const(Any) - lb_var = Const(Union{}) - if la == 4 - ub_var = argtypes[4] - lb_var = argtypes[3] - elseif la == 3 - ub_var = argtypes[3] - end + 2 ≤ la ≤ 4 || return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) # make sure generic code is prepared for inlining if needed later - call = let T = Any[Type{TypeVar}, Any, Any, Any] + let T = Any[Type{TypeVar}, Any, Any, Any] resize!(T, la) atype = Tuple{T...} T[1] = Const(TypeVar) - abstract_call_gf_by_type(interp, f, ArgInfo(nothing, T), si, atype, sv, max_methods) - end - pT = typevar_tfunc(𝕃ᵢ, n, lb_var, ub_var) - typevar_argtypes = Any[n, lb_var, ub_var] - effects = builtin_effects(𝕃ᵢ, Core._typevar, typevar_argtypes, pT) - if effects.nothrow - exct = Union{} - else - exct = builtin_exct(𝕃ᵢ, Core._typevar, typevar_argtypes, pT) + let call = abstract_call_gf_by_type(interp, f, ArgInfo(nothing, T), si, atype, sv, max_methods)::Future + return Future{CallMeta}(call, interp, sv) do call, interp, sv + n = argtypes[2] + ub_var = Const(Any) + lb_var = Const(Union{}) + if la == 4 + ub_var = argtypes[4] + lb_var = argtypes[3] + elseif la == 3 + ub_var = argtypes[3] + end + pT = typevar_tfunc(𝕃ᵢ, n, lb_var, ub_var) + typevar_argtypes = Any[n, lb_var, ub_var] + effects = builtin_effects(𝕃ᵢ, Core._typevar, typevar_argtypes, pT) + if effects.nothrow + exct = Union{} + else + exct = builtin_exct(𝕃ᵢ, Core._typevar, typevar_argtypes, pT) + end + return CallMeta(pT, exct, effects, call.info) + end + end end - return CallMeta(pT, exct, effects, call.info) elseif f === UnionAll - call = abstract_call_gf_by_type(interp, f, ArgInfo(nothing, Any[Const(UnionAll), Any, Any]), si, Tuple{Type{UnionAll}, Any, Any}, sv, max_methods) - return abstract_call_unionall(interp, argtypes, call) + let call = abstract_call_gf_by_type(interp, f, ArgInfo(nothing, Any[Const(UnionAll), Any, Any]), si, Tuple{Type{UnionAll}, Any, Any}, sv, max_methods)::Future + return Future{CallMeta}(call, interp, sv) do call, interp, sv + return abstract_call_unionall(interp, argtypes, call) + end + end elseif f === Tuple && la == 2 aty = argtypes[2] ty = isvarargtype(aty) ? unwrapva(aty) : widenconst(aty) if !isconcretetype(ty) - return CallMeta(Tuple, Any, EFFECTS_UNKNOWN, NoCallInfo()) + return Future(CallMeta(Tuple, Any, EFFECTS_UNKNOWN, NoCallInfo())) end elseif is_return_type(f) return return_type_tfunc(interp, argtypes, si, sv) elseif la == 3 && f === Core.:(!==) # mark !== as exactly a negated call to === - call = abstract_call_gf_by_type(interp, f, ArgInfo(fargs, Any[Const(f), Any, Any]), si, Tuple{typeof(f), Any, Any}, sv, max_methods) - rty = abstract_call_known(interp, (===), arginfo, si, sv, max_methods).rt - if isa(rty, Conditional) - return CallMeta(Conditional(rty.slot, rty.elsetype, rty.thentype), Bottom, EFFECTS_TOTAL, NoCallInfo()) # swap if-else - elseif isa(rty, Const) - return CallMeta(Const(rty.val === false), Bottom, EFFECTS_TOTAL, MethodResultPure()) - end - return call + let callfuture = abstract_call_gf_by_type(interp, f, ArgInfo(fargs, Any[Const(f), Any, Any]), si, Tuple{typeof(f), Any, Any}, sv, max_methods)::Future, + rtfuture = abstract_call_known(interp, (===), arginfo, si, sv, max_methods)::Future + return Future{CallMeta}(isready(callfuture) && isready(rtfuture), interp, sv) do interp, sv + local rty = rtfuture[].rt + if isa(rty, Conditional) + return CallMeta(Conditional(rty.slot, rty.elsetype, rty.thentype), Bottom, EFFECTS_TOTAL, NoCallInfo()) # swap if-else + elseif isa(rty, Const) + return CallMeta(Const(rty.val === false), Bottom, EFFECTS_TOTAL, MethodResultPure()) + end + return callfuture[] + end + end elseif la == 3 && f === Core.:(>:) # mark issupertype as a exact alias for issubtype # swap T1 and T2 arguments and call <: @@ -2350,12 +2432,12 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] return abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods) elseif la == 2 && f === Core.typename - return CallMeta(typename_static(argtypes[2]), Bottom, EFFECTS_TOTAL, MethodResultPure()) + return Future(CallMeta(typename_static(argtypes[2]), Bottom, EFFECTS_TOTAL, MethodResultPure())) elseif f === Core._hasmethod - return _hasmethod_tfunc(interp, argtypes, sv) + return Future(_hasmethod_tfunc(interp, argtypes, sv)) end atype = argtypes_to_type(argtypes) - return abstract_call_gf_by_type(interp, f, arginfo, si, atype, sv, max_methods) + return abstract_call_gf_by_type(interp, f, arginfo, si, atype, sv, max_methods)::Future end function abstract_call_opaque_closure(interp::AbstractInterpreter, @@ -2364,40 +2446,44 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, tt = closure.typ ocargsig = rewrap_unionall((unwrap_unionall(tt)::DataType).parameters[1], tt) ocargsig′ = unwrap_unionall(ocargsig) - ocargsig′ isa DataType || return CallMeta(Any, Any, Effects(), NoCallInfo()) + ocargsig′ isa DataType || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) ocsig = rewrap_unionall(Tuple{Tuple, ocargsig′.parameters...}, ocargsig) - hasintersect(sig, ocsig) || return CallMeta(Union{}, Union{MethodError,TypeError}, EFFECTS_THROWS, NoCallInfo()) + hasintersect(sig, ocsig) || return Future(CallMeta(Union{}, Union{MethodError,TypeError}, EFFECTS_THROWS, NoCallInfo())) ocmethod = closure.source::Method - result = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) - (; rt, exct, edge, effects, volatile_inf_result) = result match = MethodMatch(sig, Core.svec(), ocmethod, sig <: ocsig) - 𝕃ₚ = ipo_lattice(interp) - ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) - const_result = volatile_inf_result - if !result.edgecycle - const_call_result = abstract_call_method_with_const_args(interp, result, - nothing, arginfo, si, match, sv) - if const_call_result !== nothing - if const_call_result.rt ⊑ rt - (; rt, effects, const_result, edge) = const_call_result - end - if const_call_result.exct ⋤ exct - (; exct, const_result, edge) = const_call_result + mresult = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) + ocsig_box = Core.Box(ocsig) + return Future{CallMeta}(mresult, interp, sv) do result, interp, sv + (; rt, exct, edge, effects, volatile_inf_result, edgecycle) = result + 𝕃ₚ = ipo_lattice(interp) + ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) + const_result = volatile_inf_result + if !edgecycle + const_call_result = abstract_call_method_with_const_args(interp, result, + nothing, arginfo, si, match, sv) + if const_call_result !== nothing + if const_call_result.rt ⊑ rt + (; rt, effects, const_result, edge) = const_call_result + end + if const_call_result.exct ⋤ exct + (; exct, const_result, edge) = const_call_result + end end end - end - if check # analyze implicit type asserts on argument and return type - rty = (unwrap_unionall(tt)::DataType).parameters[2] - rty = rewrap_unionall(rty isa TypeVar ? rty.ub : rty, tt) - if !(rt ⊑ rty && sig ⊑ ocsig) - effects = Effects(effects; nothrow=false) - exct = exct ⊔ TypeError + if check # analyze implicit type asserts on argument and return type + ftt = closure.typ + rty = (unwrap_unionall(ftt)::DataType).parameters[2] + rty = rewrap_unionall(rty isa TypeVar ? rty.ub : rty, ftt) + if !(rt ⊑ rty && sig ⊑ ocsig_box.contents) + effects = Effects(effects; nothrow=false) + exct = exct ⊔ TypeError + end end + rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) + info = OpaqueClosureCallInfo(match, const_result) + edge !== nothing && add_backedge!(sv, edge) + return CallMeta(rt, exct, effects, info) end - rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) - info = OpaqueClosureCallInfo(match, const_result) - edge !== nothing && add_backedge!(sv, edge) - return CallMeta(rt, exct, effects, info) end function most_general_argtypes(closure::PartialOpaque) @@ -2422,17 +2508,17 @@ function abstract_call_unknown(interp::AbstractInterpreter, @nospecialize(ft), wft = widenconst(ft) if hasintersect(wft, Builtin) add_remark!(interp, sv, "Could not identify method table for call") - return CallMeta(Any, Any, Effects(), NoCallInfo()) + return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) elseif hasintersect(wft, Core.OpaqueClosure) uft = unwrap_unionall(wft) if isa(uft, DataType) - return CallMeta(rewrap_unionall(uft.parameters[2], wft), Any, Effects(), NoCallInfo()) + return Future(CallMeta(rewrap_unionall(uft.parameters[2], wft), Any, Effects(), NoCallInfo())) end - return CallMeta(Any, Any, Effects(), NoCallInfo()) + return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) end # non-constant function, but the number of arguments is known and the `f` is not a builtin or intrinsic atype = argtypes_to_type(arginfo.argtypes) - return abstract_call_gf_by_type(interp, nothing, arginfo, si, atype, sv, max_methods) + return abstract_call_gf_by_type(interp, nothing, arginfo, si, atype, sv, max_methods)::Future end # call where the function is any lattice element @@ -2503,7 +2589,7 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::U # this may be the wrong world for the call, # but some of the result is likely to be valid anyways # and that may help generate better codegen - abstract_call(interp, ArgInfo(nothing, at), StmtInfo(false), sv) + abstract_call(interp, ArgInfo(nothing, at), StmtInfo(false), sv)::Future rt = e.args[1] isa(rt, Type) || (rt = Any) return RTEffects(rt, Any, EFFECTS_UNKNOWN) @@ -2544,6 +2630,7 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, sv::AbsI # TODO: We still have non-linearized cglobal @assert e.args[1] === Core.tuple || e.args[1] === GlobalRef(Core, :tuple) else + @assert e.head !== :(=) # Some of our tests expect us to handle invalid IR here and error later # - permit that for now. # @assert false "Unexpected EXPR head in value position" @@ -2592,8 +2679,13 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::Infere add_curr_ssaflag!(sv, IR_FLAG_UNUSED) end si = StmtInfo(!unused) - call = abstract_call(interp, arginfo, si, sv) - sv.stmt_info[sv.currpc] = call.info + call = abstract_call(interp, arginfo, si, sv)::Future + Future{Nothing}(call, interp, sv) do call, interp, sv + # this only is needed for the side-effect, sequenced before any task tries to consume the return value, + # which this will do even without returning this Future + sv.stmt_info[sv.currpc] = call.info + nothing + end return call end @@ -2602,11 +2694,14 @@ function abstract_eval_call(interp::AbstractInterpreter, e::Expr, vtypes::Union{ ea = e.args argtypes = collect_argtypes(interp, ea, vtypes, sv) if argtypes === nothing - return RTEffects(Bottom, Any, Effects()) + return Future(RTEffects(Bottom, Any, Effects())) end arginfo = ArgInfo(ea, argtypes) - (; rt, exct, effects, refinements) = abstract_call(interp, arginfo, sv) - return RTEffects(rt, exct, effects, refinements) + call = abstract_call(interp, arginfo, sv)::Future + return Future{RTEffects}(call, interp, sv) do call, interp, sv + (; rt, exct, effects, refinements) = call + return RTEffects(rt, exct, effects, refinements) + end end function abstract_eval_new(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, @@ -2736,12 +2831,15 @@ function abstract_eval_new_opaque_closure(interp::AbstractInterpreter, e::Expr, argtypes = most_general_argtypes(rt) pushfirst!(argtypes, rt.env) callinfo = abstract_call_opaque_closure(interp, rt, - ArgInfo(nothing, argtypes), StmtInfo(true), sv, #=check=#false) - sv.stmt_info[sv.currpc] = OpaqueClosureCreateInfo(callinfo) + ArgInfo(nothing, argtypes), StmtInfo(true), sv, #=check=#false)::Future + Future{Nothing}(callinfo, interp, sv) do callinfo, interp, sv + sv.stmt_info[sv.currpc] = OpaqueClosureCreateInfo(callinfo) + nothing + end end end end - return RTEffects(rt, Any, effects) + return Future(RTEffects(rt, Any, effects)) end function abstract_eval_copyast(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, @@ -2837,7 +2935,7 @@ function abstract_eval_static_parameter(::AbstractInterpreter, e::Expr, sv::AbsI end function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, - sv::AbsIntState) + sv::AbsIntState)::Future{RTEffects} ehead = e.head if ehead === :call return abstract_eval_call(interp, e, vtypes, sv) @@ -2935,43 +3033,7 @@ function stmt_taints_inbounds_consistency(sv::AbsIntState) return has_curr_ssaflag(sv, IR_FLAG_INBOUNDS) end -function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) - if !isa(e, Expr) - if isa(e, PhiNode) - add_curr_ssaflag!(sv, IR_FLAGS_REMOVABLE) - # Implement convergence for PhiNodes. In particular, PhiNodes need to tmerge over - # the incoming values from all iterations, but `abstract_eval_phi` will only tmerge - # over the first and last iterations. By tmerging in the current old_rt, we ensure that - # we will not lose an intermediate value. - rt = abstract_eval_phi(interp, e, vtypes, sv) - old_rt = sv.ssavaluetypes[sv.currpc] - rt = old_rt === NOT_FOUND ? rt : tmerge(typeinf_lattice(interp), old_rt, rt) - return RTEffects(rt, Union{}, EFFECTS_TOTAL) - end - (; rt, exct, effects, refinements) = abstract_eval_special_value(interp, e, vtypes, sv) - else - (; rt, exct, effects, refinements) = abstract_eval_statement_expr(interp, e, vtypes, sv) - if effects.noub === NOUB_IF_NOINBOUNDS - if has_curr_ssaflag(sv, IR_FLAG_INBOUNDS) - effects = Effects(effects; noub=ALWAYS_FALSE) - elseif !propagate_inbounds(sv) - # The callee read our inbounds flag, but unless we propagate inbounds, - # we ourselves don't read our parent's inbounds. - effects = Effects(effects; noub=ALWAYS_TRUE) - end - end - e = e::Expr - @assert !isa(rt, TypeVar) "unhandled TypeVar" - rt = maybe_singleton_const(rt) - if !isempty(sv.pclimitations) - if rt isa Const || rt === Union{} - empty!(sv.pclimitations) - else - rt = LimitedAccuracy(rt, sv.pclimitations) - sv.pclimitations = IdSet{InferenceState}() - end - end - end +function merge_override_effects!(interp::AbstractInterpreter, effects::Effects, sv::InferenceState) # N.B.: This only applies to the effects of the statement itself. # It is possible for arguments (GlobalRef/:static_parameter) to throw, # but these will be recomputed during SSA construction later. @@ -2979,8 +3041,11 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), effects = override_effects(effects, override) set_curr_ssaflag!(sv, flags_for_effects(effects), IR_FLAGS_EFFECTS) merge_effects!(interp, sv, effects) + return effects +end - return RTEffects(rt, exct, effects, refinements) +function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) + @assert !isa(e, Union{Expr, PhiNode, NewvarNode}) end function override_effects(effects::Effects, override::EffectsOverride) @@ -3226,60 +3291,6 @@ function handle_control_backedge!(interp::AbstractInterpreter, frame::InferenceS return nothing end -struct BasicStmtChange - changes::Union{Nothing,StateUpdate} - rt::Any # extended lattice element or `nothing` - `nothing` if this statement may not be used as an SSA Value - exct::Any - # TODO effects::Effects - refinements # ::Union{Nothing,SlotRefinement,Vector{Any}} - function BasicStmtChange(changes::Union{Nothing,StateUpdate}, rt::Any, exct::Any, - refinements=nothing) - @nospecialize rt exct refinements - return new(changes, rt, exct, refinements) - end -end - -@inline function abstract_eval_basic_statement(interp::AbstractInterpreter, - @nospecialize(stmt), pc_vartable::VarTable, frame::InferenceState) - if isa(stmt, NewvarNode) - changes = StateUpdate(stmt.slot, VarState(Bottom, true)) - return BasicStmtChange(changes, nothing, Union{}) - elseif !isa(stmt, Expr) - (; rt, exct) = abstract_eval_statement(interp, stmt, pc_vartable, frame) - return BasicStmtChange(nothing, rt, exct) - end - changes = nothing - hd = stmt.head - if hd === :(=) - (; rt, exct, refinements) = abstract_eval_statement(interp, stmt.args[2], pc_vartable, frame) - if rt === Bottom - return BasicStmtChange(nothing, Bottom, exct, refinements) - end - lhs = stmt.args[1] - if isa(lhs, SlotNumber) - changes = StateUpdate(lhs, VarState(rt, false)) - elseif isa(lhs, GlobalRef) - handle_global_assignment!(interp, frame, lhs, rt) - elseif !isa(lhs, SSAValue) - merge_effects!(interp, frame, EFFECTS_UNKNOWN) - end - return BasicStmtChange(changes, rt, exct, refinements) - elseif hd === :method - fname = stmt.args[1] - if isa(fname, SlotNumber) - changes = StateUpdate(fname, VarState(Any, false)) - end - return BasicStmtChange(changes, nothing, Union{}) - elseif (hd === :code_coverage_effect || ( - hd !== :boundscheck && # :boundscheck can be narrowed to Bool - is_meta_expr(stmt))) - return BasicStmtChange(nothing, Nothing, Bottom) - else - (; rt, exct, refinements) = abstract_eval_statement(interp, stmt, pc_vartable, frame) - return BasicStmtChange(nothing, rt, exct, refinements) - end -end - function update_bbstate!(𝕃ᵢ::AbstractLattice, frame::InferenceState, bb::Int, vartable::VarTable) bbtable = frame.bb_vartables[bb] if bbtable === nothing @@ -3379,27 +3390,45 @@ function update_cycle_worklists!(callback, frame::InferenceState) end # make as much progress on `frame` as possible (without handling cycles) -function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) +struct CurrentState + result::Future + currstate::VarTable + bbstart::Int + bbend::Int + CurrentState(result::Future, currstate::VarTable, bbstart::Int, bbend::Int) = new(result, currstate, bbstart, bbend) + CurrentState() = new() +end +function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextresult::CurrentState) @assert !is_inferred(frame) W = frame.ip ssavaluetypes = frame.ssavaluetypes bbs = frame.cfg.blocks nbbs = length(bbs) 𝕃ᵢ = typeinf_lattice(interp) - + states = frame.bb_vartables currbb = frame.currbb + currpc = frame.currpc + + if isdefined(nextresult, :result) + # for reasons that are fairly unclear, some state is arbitrarily on the stack instead in the InferenceState as normal + bbstart = nextresult.bbstart + bbend = nextresult.bbend + currstate = nextresult.currstate + @goto injectresult + end + if currbb != 1 currbb = frame.currbb = _bits_findnext(W.bits, 1)::Int # next basic block end - - states = frame.bb_vartables currstate = copy(states[currbb]::VarTable) while currbb <= nbbs delete!(W, currbb) bbstart = first(bbs[currbb].stmts) bbend = last(bbs[currbb].stmts) - for currpc in bbstart:bbend + currpc = bbstart - 1 + while currpc < bbend + currpc += 1 frame.currpc = currpc empty_backedges!(frame, currpc) stmt = frame.src.code[currpc] @@ -3511,14 +3540,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) return caller.ssavaluetypes[caller_pc] !== Any end end - ssavaluetypes[frame.currpc] = Any + ssavaluetypes[currpc] = Any @goto find_next_bb elseif isa(stmt, EnterNode) ssavaluetypes[currpc] = Any add_curr_ssaflag!(frame, IR_FLAG_NOTHROW) if isdefined(stmt, :scope) scopet = abstract_eval_value(interp, stmt.scope, currstate, frame) - handler = gethandler(frame, frame.currpc+1)::TryCatchFrame + handler = gethandler(frame, currpc + 1)::TryCatchFrame @assert handler.scopet !== nothing if !⊑(𝕃ᵢ, scopet, handler.scopet) handler.scopet = tmerge(𝕃ᵢ, scopet, handler.scopet) @@ -3537,8 +3566,91 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) # Fall through terminator - treat as regular stmt end # Process non control-flow statements - (; changes, rt, exct, refinements) = abstract_eval_basic_statement(interp, - stmt, currstate, frame) + @assert isempty(frame.tasks) + rt = nothing + exct = Bottom + changes = nothing + refinements = nothing + effects = nothing + if isa(stmt, NewvarNode) + changes = StateUpdate(stmt.slot, VarState(Bottom, true)) + elseif isa(stmt, PhiNode) + add_curr_ssaflag!(frame, IR_FLAGS_REMOVABLE) + # Implement convergence for PhiNodes. In particular, PhiNodes need to tmerge over + # the incoming values from all iterations, but `abstract_eval_phi` will only tmerge + # over the first and last iterations. By tmerging in the current old_rt, we ensure that + # we will not lose an intermediate value. + rt = abstract_eval_phi(interp, stmt, currstate, frame) + old_rt = frame.ssavaluetypes[currpc] + rt = old_rt === NOT_FOUND ? rt : tmerge(typeinf_lattice(interp), old_rt, rt) + else + lhs = nothing + if isexpr(stmt, :(=)) + lhs = stmt.args[1] + stmt = stmt.args[2] + end + if !isa(stmt, Expr) + (; rt, exct, effects, refinements) = abstract_eval_special_value(interp, stmt, currstate, frame) + else + hd = stmt.head + if hd === :method + fname = stmt.args[1] + if isa(fname, SlotNumber) + changes = StateUpdate(fname, VarState(Any, false)) + end + elseif (hd === :code_coverage_effect || ( + hd !== :boundscheck && # :boundscheck can be narrowed to Bool + is_meta_expr(stmt))) + rt = Nothing + else + result = abstract_eval_statement_expr(interp, stmt, currstate, frame)::Future + if !isready(result) || !isempty(frame.tasks) + return CurrentState(result, currstate, bbstart, bbend) + @label injectresult + # reload local variables + stmt = frame.src.code[currpc] + changes = nothing + lhs = nothing + if isexpr(stmt, :(=)) + lhs = stmt.args[1] + stmt = stmt.args[2] + end + result = nextresult.result::Future{RTEffects} + end + result = result[] + (; rt, exct, effects, refinements) = result + if effects.noub === NOUB_IF_NOINBOUNDS + if has_curr_ssaflag(frame, IR_FLAG_INBOUNDS) + effects = Effects(effects; noub=ALWAYS_FALSE) + elseif !propagate_inbounds(frame) + # The callee read our inbounds flag, but unless we propagate inbounds, + # we ourselves don't read our parent's inbounds. + effects = Effects(effects; noub=ALWAYS_TRUE) + end + end + @assert !isa(rt, TypeVar) "unhandled TypeVar" + rt = maybe_singleton_const(rt) + if !isempty(frame.pclimitations) + if rt isa Const || rt === Union{} + empty!(frame.pclimitations) + else + rt = LimitedAccuracy(rt, frame.pclimitations) + frame.pclimitations = IdSet{InferenceState}() + end + end + end + end + effects === nothing || merge_override_effects!(interp, effects, frame) + if lhs !== nothing && rt !== Bottom + if isa(lhs, SlotNumber) + changes = StateUpdate(lhs, VarState(rt, false)) + elseif isa(lhs, GlobalRef) + handle_global_assignment!(interp, frame, lhs, rt) + elseif !isa(lhs, SSAValue) + merge_effects!(interp, frame, EFFECTS_UNKNOWN) + end + end + end if !has_curr_ssaflag(frame, IR_FLAG_NOTHROW) if exct !== Union{} update_exc_bestguess!(interp, exct, frame) @@ -3601,7 +3713,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end end # while currbb <= nbbs - nothing + return CurrentState() end function apply_refinement!(𝕃ᵢ::AbstractLattice, slot::SlotNumber, @nospecialize(newtyp), @@ -3652,31 +3764,81 @@ function condition_object_change(currstate::VarTable, condt::Conditional, end # make as much progress on `frame` as possible (by handling cycles) -function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState) - typeinf_local(interp, frame) - @assert isempty(frame.ip) +warnlength::Int = 2500 +function typeinf(interp::AbstractInterpreter, frame::InferenceState) callstack = frame.callstack::Vector{AbsIntState} - frame.cycleid == length(callstack) && return true - - no_active_ips_in_callers = false - while true - # If the current frame is not the top part of a cycle, continue to the top of the cycle before resuming work - frame.cycleid == frame.frameid || return false - # If done, return and finalize this cycle - no_active_ips_in_callers && return true - # Otherwise, do at least one iteration over the entire current cycle - no_active_ips_in_callers = true - for i = reverse(frame.cycleid:length(callstack)) - caller = callstack[i]::InferenceState - if !isempty(caller.ip) - # Note that `typeinf_local(interp, caller)` can potentially modify the other frames - # `frame.cycleid`, which is why making incremental progress requires the - # outer while loop. - typeinf_local(interp, caller) - no_active_ips_in_callers = false - end - update_valid_age!(caller, frame.valid_worlds) + nextstates = CurrentState[] + takenext = frame.frameid + minwarn = warnlength + takeprev = 0 + while takenext >= frame.frameid + callee = takenext == 0 ? frame : callstack[takenext]::InferenceState + if !isempty(callstack) + if length(callstack) - frame.frameid >= minwarn + topmethod = callstack[1].linfo + topmethod.def isa Method || (topmethod = callstack[2].linfo) + print(Core.stderr, "info: inference of ", topmethod, " exceeding ", length(callstack), " frames (may be slow).\n") + minwarn *= 2 + end + topcallee = (callstack[end]::InferenceState) + if topcallee.cycleid != callee.cycleid + callee = topcallee + takenext = length(callstack) + end + end + nextstateid = takenext + 1 - frame.frameid + while length(nextstates) < nextstateid + push!(nextstates, CurrentState()) + end + if doworkloop(interp, callee) + # First drain the workloop. Note that since some scheduled work doesn't + # affect the result (e.g. cfunction or abstract_call_method on + # get_compileable_sig), but still must be finished up since it may see and + # change the local variables of the InferenceState at currpc, we do this + # even if the nextresult status is already completed. + continue + elseif isdefined(nextstates[nextstateid], :result) || !isempty(callee.ip) + # Next make progress on this frame + prev = length(callee.tasks) + 1 + nextstates[nextstateid] = typeinf_local(interp, callee, nextstates[nextstateid]) + reverse!(callee.tasks, prev) + elseif callee.cycleid == length(callstack) + # With no active ip's and no cycles, frame is done + finish_nocycle(interp, callee) + callee.frameid == 0 && break + takenext = length(callstack) + nextstateid = takenext + 1 - frame.frameid + #@assert length(nextstates) == nextstateid + 1 + #@assert all(i -> !isdefined(nextstates[i], :result), nextstateid+1:length(nextstates)) + resize!(nextstates, nextstateid) + elseif callee.cycleid == callee.frameid + # If the current frame is the top part of a cycle, check if the whole cycle + # is done, and if not, pick the next item to work on. + no_active_ips_in_cycle = true + for i = callee.cycleid:length(callstack) + caller = callstack[i]::InferenceState + @assert caller.cycleid == callee.cycleid + if !isempty(caller.tasks) || isdefined(nextstates[i+1-frame.frameid], :result) || !isempty(caller.ip) + no_active_ips_in_cycle = false + break + end + end + if no_active_ips_in_cycle + finish_cycle(interp, callstack, callee.cycleid) + end + takenext = length(callstack) + nextstateid = takenext + 1 - frame.frameid + if no_active_ips_in_cycle + #@assert all(i -> !isdefined(nextstates[i], :result), nextstateid+1:length(nextstates)) + resize!(nextstates, nextstateid) + else + #@assert length(nextstates) == nextstateid + end + else + # Continue to the next frame in this cycle + takenext = takenext - 1 end end - return true + #@assert all(nextresult -> !isdefined(nextresult, :result), nextstates) + return is_inferred(frame) end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 6953dea5b9bd7..05d95d1d5bdc7 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -251,6 +251,7 @@ mutable struct InferenceState stmt_info::Vector{CallInfo} #= intermediate states for interprocedural abstract interpretation =# + tasks::Vector{WorkThunk} pclimitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue limitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return cycle_backedges::Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller @@ -328,6 +329,7 @@ mutable struct InferenceState limitations = IdSet{InferenceState}() cycle_backedges = Vector{Tuple{InferenceState,Int}}() callstack = AbsIntState[] + tasks = WorkThunk[] valid_worlds = WorldRange(1, get_world_counter()) bestguess = Bottom @@ -351,7 +353,7 @@ mutable struct InferenceState this = new( mi, world, mod, sptypes, slottypes, src, cfg, method_info, currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, - pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, + tasks, pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, interp) @@ -800,6 +802,7 @@ mutable struct IRInterpretationState const ssa_refined::BitSet const lazyreachability::LazyCFGReachability valid_worlds::WorldRange + const tasks::Vector{WorkThunk} const edges::Vector{Any} callstack #::Vector{AbsIntState} frameid::Int @@ -825,10 +828,11 @@ mutable struct IRInterpretationState ssa_refined = BitSet() lazyreachability = LazyCFGReachability(ir) valid_worlds = WorldRange(min_world, max_world == typemax(UInt) ? get_world_counter() : max_world) + tasks = WorkThunk[] edges = Any[] callstack = AbsIntState[] return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, - ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0, 0) + ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0, 0) end end @@ -870,6 +874,7 @@ function print_callstack(frame::AbsIntState) print(frame_instance(sv)) is_cached(sv) || print(" [uncached]") sv.parentid == idx - 1 || print(" [parent=", sv.parentid, "]") + isempty(callers_in_cycle(sv)) || print(" [cycle=", sv.cycleid, "]") println() @assert sv.frameid == idx end @@ -994,7 +999,10 @@ of the same cycle, only if it is part of a cycle with multiple frames. function callers_in_cycle(sv::InferenceState) callstack = sv.callstack::Vector{AbsIntState} cycletop = cycleid = sv.cycleid - while cycletop < length(callstack) && (callstack[cycletop + 1]::InferenceState).cycleid == cycleid + while cycletop < length(callstack) + frame = callstack[cycletop + 1] + frame isa InferenceState || break + frame.cycleid == cycleid || break cycletop += 1 end return AbsIntCycle(callstack, cycletop == cycleid ? 0 : cycleid, cycletop) @@ -1054,6 +1062,7 @@ function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects:: effects = Effects(effects; effect_free=ALWAYS_TRUE) end caller.ipo_effects = merge_effects(caller.ipo_effects, effects) + nothing end merge_effects!(::AbstractInterpreter, ::IRInterpretationState, ::Effects) = return @@ -1116,3 +1125,90 @@ function get_max_methods_for_module(mod::Module) max_methods < 0 && return nothing return max_methods end + +""" + Future{T} + +Delayed return value for a value of type `T`, similar to RefValue{T}, but +explicitly represents completed as a `Bool` rather than as `isdefined`. +Set once with `f[] = v` and accessed with `f[]` afterwards. + +Can also be constructed with the `completed` flag value and a closure to +produce `x`, as well as the additional arguments to avoid always capturing the +same couple of values. +""" +struct Future{T} + later::Union{Nothing,RefValue{T}} + now::Union{Nothing,T} + Future{T}() where {T} = new{T}(RefValue{T}(), nothing) + Future{T}(x) where {T} = new{T}(nothing, x) + Future(x::T) where {T} = new{T}(nothing, x) +end +isready(f::Future) = f.later === nothing +getindex(f::Future{T}) where {T} = (later = f.later; later === nothing ? f.now::T : later[]) +setindex!(f::Future, v) = something(f.later)[] = v +convert(::Type{Future{T}}, x) where {T} = Future{T}(x) # support return type conversion +convert(::Type{Future{T}}, x::Future) where {T} = x::Future{T} +function Future{T}(f, immediate::Bool, interp::AbstractInterpreter, sv::AbsIntState) where {T} + if immediate + return Future{T}(f(interp, sv)) + else + @assert applicable(f, interp, sv) + result = Future{T}() + push!(sv.tasks, function (interp, sv) + result[] = f(interp, sv) + return true + end) + return result + end +end +function Future{T}(f, prev::Future{S}, interp::AbstractInterpreter, sv::AbsIntState) where {T, S} + later = prev.later + if later === nothing + return Future{T}(f(prev[], interp, sv)) + else + @assert Core._hasmethod(Tuple{Core.Typeof(f), S, typeof(interp), typeof(sv)}) + result = Future{T}() + push!(sv.tasks, function (interp, sv) + result[] = f(later[], interp, sv) # capture just later, instead of all of prev + return true + end) + return result + end +end + + +""" + doworkloop(args...) + +Run a tasks inside the abstract interpreter, returning false if there are none. +Tasks will be run in DFS post-order tree order, such that all child tasks will +be run in the order scheduled, prior to running any subsequent tasks. This +allows tasks to generate more child tasks, which will be run before anything else. +Each task will be run repeatedly when returning `false`, until it returns `true`. +""" +function doworkloop(interp::AbstractInterpreter, sv::AbsIntState) + tasks = sv.tasks + prev = length(tasks) + prev == 0 && return false + task = pop!(tasks) + completed = task(interp, sv) + tasks = sv.tasks # allow dropping gc root over the previous call + completed isa Bool || throw(TypeError(:return, "", Bool, task)) # print the task on failure as part of the error message, instead of just "@ workloop:line" + completed || push!(tasks, task) + # efficient post-order visitor: items pushed are executed in reverse post order such + # that later items are executed before earlier ones, but are fully executed + # (including any dependencies scheduled by them) before going on to the next item + reverse!(tasks, #=start=#prev) + return true +end + + +#macro workthunk(name::Symbol, body) +# name = esc(name) +# body = esc(body) +# return replace_linenums!( +# :(function $name($(esc(interp)), $(esc(sv))) +# $body +# end), __source__) +#end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 960da88ddffc8..fdcb4621c5c0f 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -1432,6 +1432,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr elseif isa(stmt, OldSSAValue) ssa_rename[idx] = ssa_rename[stmt.id] elseif isa(stmt, GotoNode) && cfg_transforms_enabled + stmt.label < 0 && (println(stmt); println(compact)) label = bb_rename_succ[stmt.label] @assert label > 0 ssa_rename[idx] = SSAValue(result_idx) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 1aeb87accbcd7..ca8ca770df413 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -51,8 +51,11 @@ end function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, irsv::IRInterpretationState) si = StmtInfo(true) # TODO better job here? - call = abstract_call(interp, arginfo, si, irsv) - irsv.ir.stmts[irsv.curridx][:info] = call.info + call = abstract_call(interp, arginfo, si, irsv)::Future + Future{Nothing}(call, interp, irsv) do call, interp, irsv + irsv.ir.stmts[irsv.curridx][:info] = call.info + nothing + end return call end @@ -143,7 +146,19 @@ function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction, head = stmt.head if (head === :call || head === :foreigncall || head === :new || head === :splatnew || head === :static_parameter || head === :isdefined || head === :boundscheck) - (; rt, effects) = abstract_eval_statement_expr(interp, stmt, nothing, irsv) + @assert isempty(irsv.tasks) # TODO: this whole function needs to be converted to a stackless design to be a valid AbsIntState, but this should work here for now + result = abstract_eval_statement_expr(interp, stmt, nothing, irsv) + reverse!(irsv.tasks) + while true + if length(irsv.callstack) > irsv.frameid + typeinf(interp, irsv.callstack[irsv.frameid + 1]) + elseif !doworkloop(interp, irsv) + break + end + end + @assert length(irsv.callstack) == irsv.frameid && isempty(irsv.tasks) + result isa Future && (result = result[]) + (; rt, effects) = result add_flag!(inst, flags_for_effects(effects)) elseif head === :invoke rt, (nothrow, noub) = abstract_eval_invoke_inst(interp, inst, irsv) @@ -293,7 +308,7 @@ function is_all_const_call(@nospecialize(stmt), interp::AbstractInterpreter, irs return true end -function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState; +function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState; externally_refined::Union{Nothing,BitSet} = nothing) (; ir, tpdum, ssa_refined) = irsv @@ -449,18 +464,3 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR return Pair{Any,Tuple{Bool,Bool}}(maybe_singleton_const(ultimate_rt), (nothrow, noub)) end - -function ir_abstract_constant_propagation(interp::NativeInterpreter, irsv::IRInterpretationState) - if __measure_typeinf__[] - inf_frame = Timings.InferenceFrameInfo(irsv.mi, irsv.world, VarState[], Any[], length(irsv.ir.argtypes)) - Timings.enter_new_timer(inf_frame) - ret = _ir_abstract_constant_propagation(interp, irsv) - append!(inf_frame.slottypes, irsv.ir.argtypes) - Timings.exit_current_timer(inf_frame) - return ret - else - return _ir_abstract_constant_propagation(interp, irsv) - end -end -ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState) = - _ir_abstract_constant_propagation(interp, irsv) diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index a4286177e93a4..268991282c483 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -1,9 +1,11 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license function maybe_show_ir(ir::IRCode) - if isdefined(Core, :Main) + if isdefined(Core, :Main) && isdefined(Core.Main, :Base) # ensure we use I/O that does not yield, as this gets called during compilation invokelatest(Core.Main.Base.show, Core.stdout, "text/plain", ir) + else + Core.show(ir) end end @@ -25,6 +27,7 @@ is_toplevel_expr_head(head::Symbol) = head === :global || head === :method || he is_value_pos_expr_head(head::Symbol) = head === :static_parameter function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, printed_use_idx::Int, print::Bool, isforeigncall::Bool, arg_idx::Int, allow_frontend_forms::Bool) if isa(op, SSAValue) + op.id > 0 || @verify_error "Def ($(op.id)) is invalid in final IR" if op.id > length(ir.stmts) def_bb = block_for_inst(ir.cfg, ir.new_nodes.info[op.id - length(ir.stmts)].pos) else diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index cc8ba227bd088..a6b7e53c6f320 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1383,10 +1383,10 @@ end nargs = length(argtypes) if !isempty(argtypes) && isvarargtype(argtypes[nargs]) - nargs - 1 <= maxargs || return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) - nargs + 1 >= op_argi || return CallMeta(Any, Any, Effects(), NoCallInfo()) + nargs - 1 <= maxargs || return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) + nargs + 1 >= op_argi || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) else - minargs <= nargs <= maxargs || return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) + minargs <= nargs <= maxargs || return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) end 𝕃ᵢ = typeinf_lattice(interp) if ff === modifyfield! @@ -1417,15 +1417,22 @@ end op = unwrapva(argtypes[op_argi]) v = unwrapva(argtypes[v_argi]) callinfo = abstract_call(interp, ArgInfo(nothing, Any[op, TF, v]), StmtInfo(true), sv, #=max_methods=#1) - TF2 = tmeet(callinfo.rt, widenconst(TF)) - if TF2 === Bottom - RT = Bottom - elseif isconcretetype(RT) && has_nontrivial_extended_info(𝕃ᵢ, TF2) # isconcrete condition required to form a PartialStruct - RT = PartialStruct(RT, Any[TF, TF2]) + TF = Core.Box(TF) + RT = Core.Box(RT) + return Future{CallMeta}(callinfo, interp, sv) do callinfo, interp, sv + TF = TF.contents + RT = RT.contents + TF2 = tmeet(callinfo.rt, widenconst(TF)) + if TF2 === Bottom + RT = Bottom + elseif isconcretetype(RT) && has_nontrivial_extended_info(𝕃ᵢ, TF2) # isconcrete condition required to form a PartialStruct + RT = PartialStruct(RT, Any[TF, TF2]) + end + info = ModifyOpInfo(callinfo.info) + return CallMeta(RT, Any, Effects(), info) end - info = ModifyOpInfo(callinfo.info) end - return CallMeta(RT, Any, Effects(), info) + return Future(CallMeta(RT, Any, Effects(), info)) end # we could use tuple_tfunc instead of widenconst, but `o` is mutable, so that is unlikely to be beneficial @@ -2895,17 +2902,17 @@ end function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) UNKNOWN = CallMeta(Type, Any, Effects(EFFECTS_THROWS; nortcall=false), NoCallInfo()) if !(2 <= length(argtypes) <= 3) - return UNKNOWN + return Future(UNKNOWN) end tt = widenslotwrapper(argtypes[end]) if !isa(tt, Const) && !(isType(tt) && !has_free_typevars(tt)) - return UNKNOWN + return Future(UNKNOWN) end af_argtype = isa(tt, Const) ? tt.val : (tt::DataType).parameters[1] if !isa(af_argtype, DataType) || !(af_argtype <: Tuple) - return UNKNOWN + return Future(UNKNOWN) end if length(argtypes) == 3 @@ -2918,7 +2925,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s end if !(isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || (isconcretetype(aft) && !(aft <: Builtin) && !iskindtype(aft))) - return UNKNOWN + return Future(UNKNOWN) end # effects are not an issue if we know this statement will get removed, but if it does not get removed, @@ -2926,7 +2933,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s RT_CALL_EFFECTS = Effects(EFFECTS_TOTAL; nortcall=false) if contains_is(argtypes_vec, Union{}) - return CallMeta(Const(Union{}), Union{}, RT_CALL_EFFECTS, NoCallInfo()) + return Future(CallMeta(Const(Union{}), Union{}, RT_CALL_EFFECTS, NoCallInfo())) end # Run the abstract_call without restricting abstract call @@ -2935,42 +2942,45 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s if isa(sv, InferenceState) old_restrict = sv.restrict_abstract_call_sites sv.restrict_abstract_call_sites = false - call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, #=max_methods=#-1) - sv.restrict_abstract_call_sites = old_restrict - else - call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, #=max_methods=#-1) - end - info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() - rt = widenslotwrapper(call.rt) - if isa(rt, Const) - # output was computed to be constant - return CallMeta(Const(typeof(rt.val)), Union{}, RT_CALL_EFFECTS, info) - end - rt = widenconst(rt) - if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) - # output cannot be improved so it is known for certain - return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) - elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) - # conservatively express uncertainty of this result - # in two ways: both as being a subtype of this, and - # because of LimitedAccuracy causes - return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) - elseif isa(tt, Const) || isconstType(tt) - # input arguments were known for certain - # XXX: this doesn't imply we know anything about rt - return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) - elseif isType(rt) - return CallMeta(Type{rt}, Union{}, RT_CALL_EFFECTS, info) - else - return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) + end + call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, #=max_methods=#-1) + tt = Core.Box(tt) + return Future{CallMeta}(call, interp, sv) do call, interp, sv + if isa(sv, InferenceState) + sv.restrict_abstract_call_sites = old_restrict + end + info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() + rt = widenslotwrapper(call.rt) + if isa(rt, Const) + # output was computed to be constant + return CallMeta(Const(typeof(rt.val)), Union{}, RT_CALL_EFFECTS, info) + end + rt = widenconst(rt) + if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) + # output cannot be improved so it is known for certain + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) + elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) + # conservatively express uncertainty of this result + # in two ways: both as being a subtype of this, and + # because of LimitedAccuracy causes + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) + elseif isa(tt.contents, Const) || isconstType(tt.contents) + # input arguments were known for certain + # XXX: this doesn't imply we know anything about rt + return CallMeta(Const(rt), Union{}, RT_CALL_EFFECTS, info) + elseif isType(rt) + return CallMeta(Type{rt}, Union{}, RT_CALL_EFFECTS, info) + else + return CallMeta(Type{<:rt}, Union{}, RT_CALL_EFFECTS, info) + end end end # a simplified model of abstract_call_gf_by_type for applicable function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState, max_methods::Int) - length(argtypes) < 2 && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) - isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo()) + length(argtypes) < 2 && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) + isvarargtype(argtypes[2]) && return Future(CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo())) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) matches = find_method_matches(interp, argtypes, atype; max_methods) @@ -2997,7 +3007,7 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, # added that did not intersect with any existing method add_uncovered_edges!(sv, matches, atype) end - return CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo()) + return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo())) end add_tfunc(applicable, 1, INT_INF, @nospecs((𝕃::AbstractLattice, f, args...)->Bool), 40) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 315a068e611fe..77a2e02129ce4 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -56,7 +56,7 @@ end Timing(mi_info, start_time, cur_start_time, time, children) = Timing(mi_info, start_time, cur_start_time, time, children, nothing) Timing(mi_info, start_time) = Timing(mi_info, start_time, start_time, UInt64(0), Timing[]) -_time_ns() = ccall(:jl_hrtime, UInt64, ()) # Re-implemented here because Base not yet available. +_time_ns() = ccall(:jl_hrtime, UInt64, ()) # We keep a stack of the Timings for each of the MethodInstances currently being timed. # Since type inference currently operates via a depth-first search (during abstract @@ -77,114 +77,14 @@ const ROOTmi = Core.Compiler.specialize_method( Empty out the previously recorded type inference timings (`Core.Compiler._timings`), and start the ROOT() timer again. `ROOT()` measures all time spent _outside_ inference. """ -function reset_timings() - empty!(_timings) - push!(_timings, Timing( - # The MethodInstance for ROOT(), and default empty values for other fields. - InferenceFrameInfo(ROOTmi, 0x0, Core.Compiler.VarState[], Any[Core.Const(ROOT)], 1), - _time_ns())) - return nothing -end -reset_timings() - -# (This is split into a function so that it can be called both in this module, at the top -# of `enter_new_timer()`, and once at the Very End of the operation, by whoever started -# the operation and called `reset_timings()`.) -# NOTE: the @inline annotations here are not to make it faster, but to reduce the gap between -# timer manipulations and the tasks we're timing. -@inline function close_current_timer() - stop_time = _time_ns() - parent_timer = _timings[end] - accum_time = stop_time - parent_timer.cur_start_time - - # Add in accum_time ("modify" the immutable struct) - @inbounds begin - _timings[end] = Timing( - parent_timer.mi_info, - parent_timer.start_time, - parent_timer.cur_start_time, - parent_timer.time + accum_time, - parent_timer.children, - parent_timer.bt, - ) - end - return nothing -end - -@inline function enter_new_timer(frame) - # Very first thing, stop the active timer: get the current time and add in the - # time since it was last started to its aggregate exclusive time. - close_current_timer() - - mi_info = _typeinf_identifier(frame) - - # Start the new timer right before returning - push!(_timings, Timing(mi_info, UInt64(0))) - len = length(_timings) - new_timer = @inbounds _timings[len] - # Set the current time _after_ appending the node, to try to exclude the - # overhead from measurement. - start = _time_ns() - - @inbounds begin - _timings[len] = Timing( - new_timer.mi_info, - start, - start, - new_timer.time, - new_timer.children, - ) - end - - return nothing -end - -# _expected_frame_ is not needed within this function; it is used in the `@assert`, to -# assert that indeed we are always returning to a parent after finishing all of its -# children (that is, asserting that inference proceeds via depth-first-search). -@inline function exit_current_timer(_expected_frame_) - # Finish the new timer - stop_time = _time_ns() - - expected_mi_info = _typeinf_identifier(_expected_frame_) - - # Grab the new timer again because it might have been modified in _timings - # (since it's an immutable struct) - # And remove it from the current timings stack - new_timer = pop!(_timings) - Core.Compiler.@assert new_timer.mi_info.mi === expected_mi_info.mi - - # Prepare to unwind one level of the stack and record in the parent - parent_timer = _timings[end] - - accum_time = stop_time - new_timer.cur_start_time - # Add in accum_time ("modify" the immutable struct) - new_timer = Timing( - new_timer.mi_info, - new_timer.start_time, - new_timer.cur_start_time, - new_timer.time + accum_time, - new_timer.children, - parent_timer.mi_info.mi === ROOTmi ? backtrace() : nothing, - ) - # Record the final timing with the original parent timer - push!(parent_timer.children, new_timer) - - # And finally restart the parent timer: - len = length(_timings) - @inbounds begin - _timings[len] = Timing( - parent_timer.mi_info, - parent_timer.start_time, - _time_ns(), - parent_timer.time, - parent_timer.children, - parent_timer.bt, - ) - end - - return nothing -end +function reset_timings() end +push!(_timings, Timing( + # The MethodInstance for ROOT(), and default empty values for other fields. + InferenceFrameInfo(ROOTmi, 0x0, Core.Compiler.VarState[], Any[Core.Const(ROOT)], 1), + _time_ns())) +function close_current_timer() end +function enter_new_timer(frame) end +function exit_current_timer(_expected_frame_) end end # module Timings @@ -194,19 +94,7 @@ end # module Timings If set to `true`, record per-method-instance timings within type inference in the Compiler. """ __set_measure_typeinf(onoff::Bool) = __measure_typeinf__[] = onoff -const __measure_typeinf__ = fill(false) - -# Wrapper around `_typeinf` that optionally records the exclusive time for each invocation. -function typeinf(interp::AbstractInterpreter, frame::InferenceState) - if __measure_typeinf__[] - Timings.enter_new_timer(frame) - v = _typeinf(interp, frame) - Timings.exit_current_timer(frame) - return v - else - return _typeinf(interp, frame) - end -end +const __measure_typeinf__ = RefValue{Bool}(false) function finish!(interp::AbstractInterpreter, caller::InferenceState; can_discard_trees::Bool=may_discard_trees(interp)) @@ -258,19 +146,6 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState; return nothing end -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.callstack::Vector{AbsIntState} - if length(frames) == frame.cycleid - finish_nocycle(interp, frame) - else - @assert frame.cycleid != 0 - finish_cycle(interp, frames, frame.cycleid) - end - return true -end - function finish_nocycle(::AbstractInterpreter, frame::InferenceState) finishinfer!(frame, frame.interp) opt = frame.result.src @@ -762,16 +637,11 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, add_cycle_backedge!(parent, child) parent.cycleid === ancestorid && break child = parent - parent = frame_parent(child) - while !isa(parent, InferenceState) - # XXX we may miss some edges here? - parent = frame_parent(parent::IRInterpretationState) - end + parent = frame_parent(child)::InferenceState end # ensure that walking the callstack has the same cycleid (DAG) for frame = reverse(ancestorid:length(frames)) - frame = frames[frame] - frame isa InferenceState || continue + frame = frames[frame]::InferenceState frame.cycleid == ancestorid && break @assert frame.cycleid > ancestorid frame.cycleid = ancestorid @@ -796,9 +666,9 @@ end # returned instead. function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::AbsIntState) # TODO (#48913) implement a proper recursion handling for irinterp: - # This works just because currently the `:terminate` condition guarantees that - # irinterp doesn't fail into unresolved cycles, but it's not a good solution. - # We should revisit this once we have a better story for handling cycles in irinterp. + # This works currently just because the irinterp code doesn't get used much with + # `@assume_effects`, so it never sees a cycle normally, but that may not be a sustainable solution. + parent isa InferenceState || return false frames = parent.callstack::Vector{AbsIntState} uncached = false for frame = reverse(1:length(frames)) @@ -837,15 +707,43 @@ struct EdgeCallResult end # return cached result of regular inference -function return_cached_result(::AbstractInterpreter, codeinst::CodeInstance, caller::AbsIntState) +function return_cached_result(interp::AbstractInterpreter, method::Method, codeinst::CodeInstance, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool) rt = cached_return_type(codeinst) effects = ipo_effects(codeinst) update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst))) - return EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects) + return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects), edgecycle, edgelimited)) +end + +function EdgeCall_to_MethodCall_Result(interp::AbstractInterpreter, sv::AbsIntState, method::Method, result::EdgeCallResult, edgecycle::Bool, edgelimited::Bool) + (; rt, exct, edge, effects, volatile_inf_result) = result + + if edge === nothing + edgecycle = edgelimited = true + end + + # we look for the termination effect override here as well, since the :terminates effect + # may have been tainted due to recursion at this point even if it's overridden + if is_effect_overridden(sv, :terminates_globally) + # this frame is known to terminate + effects = Effects(effects, terminates=true) + elseif is_effect_overridden(method, :terminates_globally) + # this edge is known to terminate + effects = Effects(effects; terminates=true) + elseif edgecycle + # Some sort of recursion was detected. + if edge !== nothing && !edgelimited && !is_edge_recursed(edge, sv) + # no `MethodInstance` cycles -- don't taint :terminate + else + # we cannot guarantee that the call will terminate + effects = Effects(effects; terminates=false) + end + end + + return MethodCallResult(rt, exct, edgecycle, edgelimited, edge, effects, volatile_inf_result) end # compute (and cache) an inferred AST and return the current best estimate of the result type -function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState) +function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool) mi = specialize_method(method, atype, sparams)::MethodInstance cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default force_inline = is_stmt_inline(get_curr_ssaflag(caller)) @@ -859,13 +757,13 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize cache_mode = CACHE_MODE_VOLATILE else @assert codeinst.def === mi "MethodInstance for cached edge does not match" - return return_cached_result(interp, codeinst, caller) + return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited) end end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_output(#=incremental=#false) add_remark!(interp, caller, "[typeinf_edge] Inference is disabled for the target module") - return EdgeCallResult(Any, Any, nothing, Effects()) + return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) end if !is_cached(caller) && frame_parent(caller) === nothing # this caller exists to return to the user @@ -886,7 +784,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize cache_mode = CACHE_MODE_VOLATILE else @assert codeinst.def === mi "MethodInstance for cached edge does not match" - return return_cached_result(interp, codeinst, caller) + return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited) end end end @@ -902,31 +800,40 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize if cache_mode == CACHE_MODE_GLOBAL engine_reject(interp, ci) end - return EdgeCallResult(Any, Any, nothing, Effects()) + return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) end assign_parentchild!(frame, caller) - typeinf(interp, frame) - update_valid_age!(caller, frame.valid_worlds) - isinferred = is_inferred(frame) - edge = isinferred ? mi : nothing - 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, so we can use this local result destructively - volatile_inf_result = isinferred ? VolatileInferenceResult(result) : nothing - return EdgeCallResult(frame.bestguess, exc_bestguess, edge, effects, volatile_inf_result) + # the actual inference task for this edge is going to be scheduled within `typeinf_local` via the callstack queue + # while splitting off the rest of the work for this caller into a separate workq thunk + let mresult = Future{MethodCallResult}() + push!(caller.tasks, function get_infer_result(interp, caller) + update_valid_age!(caller, frame.valid_worlds) + local isinferred = is_inferred(frame) + local edge = isinferred ? mi : nothing + local effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects + adjust_effects(effects_for_cycle(frame.ipo_effects), method) + local 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, so we can use this local result destructively + local volatile_inf_result = isinferred ? VolatileInferenceResult(result) : nothing + local edgeresult = EdgeCallResult(frame.bestguess, exc_bestguess, edge, effects, volatile_inf_result) + mresult[] = EdgeCall_to_MethodCall_Result(interp, caller, method, edgeresult, edgecycle, edgelimited) + return true + end) + return mresult + end elseif frame === true # unresolvable cycle add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle") - return EdgeCallResult(Any, Any, nothing, Effects()) + return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) end # return the current knowledge about this cycle frame = frame::InferenceState update_valid_age!(caller, frame.valid_worlds) 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) + edgeresult = EdgeCallResult(frame.bestguess, exc_bestguess, nothing, effects) + return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, edgeresult, edgecycle, edgelimited)) end # The `:terminates` effect bit must be conservatively tainted unless recursion cycle has diff --git a/base/compiler/types.jl b/base/compiler/types.jl index b475e360dac02..c51785f23ea29 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -1,4 +1,12 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# + +const WorkThunk = Any +# #@eval struct WorkThunk +# thunk::Core.OpaqueClosure{Tuple{Vector{Tasks}}, Bool} +# WorkThunk(work) = new($(Expr(:opaque_closure, :(Tuple{Vector{Tasks}}), :Bool, :Bool, :((tasks) -> work(tasks))))) # @opaque Vector{Tasks}->Bool (tasks)->work(tasks) +# end +# (p::WorkThunk)() = p.thunk() """ AbstractInterpreter diff --git a/base/reflection.jl b/base/reflection.jl index fe48b6f9aa6b9..df29b9a5b1a4e 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -2447,7 +2447,7 @@ true ``` """ function hasmethod(@nospecialize(f), @nospecialize(t)) - return Core._hasmethod(f, t isa Type ? t : to_tuple_type(t)) + return Core._hasmethod(signature_type(f, t)) end function Core.kwcall(kwargs::NamedTuple, ::typeof(hasmethod), @nospecialize(f), @nospecialize(t)) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index bab4fe02a5168..009128b289ade 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -415,10 +415,13 @@ function CC.abstract_call(interp::NoinlineInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Int) ret = @invoke CC.abstract_call(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Int) - if sv.mod in noinline_modules(interp) - return CC.CallMeta(ret.rt, ret.exct, ret.effects, NoinlineCallInfo(ret.info)) + return CC.Future{CC.CallMeta}(ret, interp, sv) do ret, interp, sv + if sv.mod in noinline_modules(interp) + (;rt, exct, effects, info) = ret + return CC.CallMeta(rt, exct, effects, NoinlineCallInfo(info)) + end + return ret end - return ret end function CC.src_inlining_policy(interp::NoinlineInterpreter, @nospecialize(src), @nospecialize(info::CallInfo), stmt_flag::UInt32) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 46009e0790942..7c7726413004a 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -3887,113 +3887,6 @@ f_apply_cglobal(args...) = cglobal(args...) f37532(T, x) = (Core.bitcast(Ptr{T}, x); x) @test Base.return_types(f37532, Tuple{Any, Int}) == Any[Int] -# PR #37749 -# Helper functions for Core.Compiler.Timings. These are normally accessed via a package - -# usually (SnoopCompileCore). -function time_inference(f) - Core.Compiler.Timings.reset_timings() - Core.Compiler.__set_measure_typeinf(true) - f() - Core.Compiler.__set_measure_typeinf(false) - Core.Compiler.Timings.close_current_timer() - return Core.Compiler.Timings._timings[1] -end -function depth(t::Core.Compiler.Timings.Timing) - maximum(depth.(t.children), init=0) + 1 -end -function flatten_times(t::Core.Compiler.Timings.Timing) - collect(Iterators.flatten([(t.time => t.mi_info,), flatten_times.(t.children)...])) -end -# Some very limited testing of timing the type inference (#37749). -@testset "Core.Compiler.Timings" begin - # Functions that call each other - @eval module M1 - i(x) = x+5 - i2(x) = x+2 - h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2 - g(y::Integer, x) = h(Any[y]) + Int(x) - end - timing1 = time_inference() do - @eval M1.g(2, 3.0) - end - @test occursin(r"Core.Compiler.Timings.Timing\(InferenceFrameInfo for Core.Compiler.Timings.ROOT\(\)\) with \d+ children", sprint(show, timing1)) - # The last two functions to be inferred should be `i` and `i2`, inferred at runtime with - # their concrete types. - @test sort([mi_info.mi.def.name for (time,mi_info) in flatten_times(timing1)[end-1:end]]) == [:i, :i2] - @test all(child->isa(child.bt, Vector), timing1.children) - @test all(child->child.bt===nothing, timing1.children[1].children) - # Test the stacktrace - @test isa(stacktrace(timing1.children[1].bt), Vector{Base.StackTraces.StackFrame}) - # Test that inference has cached some of the Method Instances - timing2 = time_inference() do - @eval M1.g(2, 3.0) - end - @test length(flatten_times(timing2)) < length(flatten_times(timing1)) - # Printing of InferenceFrameInfo for mi.def isa Module - @eval module M2 - i(x) = x+5 - i2(x) = x+2 - h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2 - g(y::Integer, x) = h(Any[y]) + Int(x) - end - # BEGIN LINE NUMBER SENSITIVITY (adjust the line offset below as needed) - timingmod = time_inference() do - @eval @testset "Outer" begin - @testset "Inner" begin - for i = 1:2 M2.g(2, 3.0) end - end - end - end - @test occursin("thunk from $(@__MODULE__) starting at $(@__FILE__):$((@__LINE__) - 6)", string(timingmod.children)) - # END LINE NUMBER SENSITIVITY - - # Recursive function - @eval module _Recursive f(n::Integer) = n == 0 ? 0 : f(n-1) + 1 end - timing = time_inference() do - @eval _Recursive.f(Base.inferencebarrier(5)) - end - @test 2 <= depth(timing) <= 3 # root -> f (-> +) - @test 2 <= length(flatten_times(timing)) <= 3 # root, f, + - - # Functions inferred with multiple constants - @eval module C - i(x) = x === 0 ? 0 : 1 / x - a(x) = i(0) * i(x) - b() = i(0) * i(1) * i(0) - function loopc(n) - s = 0 - for i = 1:n - s += i - end - return s - end - call_loopc() = loopc(5) - myfloor(::Type{T}, x) where T = floor(T, x) - d(x) = myfloor(Int16, x) - end - timing = time_inference() do - @eval C.a(2) - @eval C.b() - @eval C.call_loopc() - @eval C.d(3.2) - end - ft = flatten_times(timing) - @test !isempty(ft) - str = sprint(show, ft) - @test occursin("InferenceFrameInfo for /(1::$Int, ::$Int)", str) # inference constants - @test occursin("InferenceFrameInfo for Core.Compiler.Timings.ROOT()", str) # qualified - # loopc has internal slots, check constant printing in this case - sel = filter(ti -> ti.second.mi.def.name === :loopc, ft) - ifi = sel[end].second - @test length(ifi.slottypes) > ifi.nargs - str = sprint(show, sel) - @test occursin("InferenceFrameInfo for $(@__MODULE__).C.loopc(5::$Int)", str) - # check that types aren't double-printed as `T::Type{T}` - sel = filter(ti -> ti.second.mi.def.name === :myfloor, ft) - str = sprint(show, sel) - @test occursin("InferenceFrameInfo for $(@__MODULE__).C.myfloor(::Type{Int16}, ::Float64)", str) -end - # issue #37638 @test only(Base.return_types(() -> (nothing, Any[]...)[2])) isa Type From 1bd610f9ab9dd6e2145d1731c0fb8f7e84208876 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:26:56 +0900 Subject: [PATCH 283/548] optimizer: simplify the finalizer inlining pass a bit (#55934) Minor adjustments have been made to the algorithm of the finalizer inlining pass. Previously, it required that the finalizer registration dominate all uses, but this is not always necessary as far as the finalizer inlining point dominates all the uses. So the check has been relaxed. Other minor fixes have been made as well, but their importance is low. --- base/compiler/optimize.jl | 2 +- base/compiler/ssair/inlining.jl | 1 - base/compiler/ssair/passes.jl | 103 +++++++++++++------------------- test/compiler/inline.jl | 2 - 4 files changed, 42 insertions(+), 66 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 6b0cf981930ad..1971b47323f5d 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -647,7 +647,7 @@ end function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState) if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending) ir = sv.ir - nargs = length(ir.argtypes) + nargs = let def = sv.result.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp)) argescapes = EscapeAnalysis.ArgEscapeCache(estate) stack_analysis_result!(sv.result, argescapes) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 727e015b67062..9f250b156cd2f 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1597,7 +1597,6 @@ function handle_finalizer_call!(ir::IRCode, idx::Int, stmt::Expr, info::Finalize push!(stmt.args, item1.invoke) elseif isa(item1, ConstantCase) push!(stmt.args, nothing) - push!(stmt.args, item1.val) end end return nothing diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 37d79e2bd7b0c..3981f7382d707 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1564,10 +1564,12 @@ end is_nothrow(ir::IRCode, ssa::SSAValue) = has_flag(ir[ssa], IR_FLAG_NOTHROW) -function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Union{Nothing,Int} = nothing) +function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Int) worklist = Int[from_bb] visited = BitSet(from_bb) - if to_bb !== nothing + if to_bb == from_bb + return visited + else push!(visited, to_bb) end function visit!(bb::Int) @@ -1582,100 +1584,78 @@ function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Union{Nothing,Int} = no return visited end -function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse::SSADefUse, +function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, defuse::SSADefUse, inlining::InliningState, lazydomtree::LazyDomtree, lazypostdomtree::LazyPostDomtree, @nospecialize(info::CallInfo)) # For now, require that: # 1. The allocation dominates the finalizer registration - # 2. The finalizer registration dominates all uses reachable from the - # finalizer registration. - # 3. The insertion block for the finalizer is the post-dominator of all - # uses and the finalizer registration block. The insertion block must - # be dominated by the finalizer registration block. - # 4. The path from the finalizer registration to the finalizer inlining + # 2. The insertion block for the finalizer is the post-dominator of all + # uses (including the finalizer registration). + # 3. The path from the finalizer registration to the finalizer inlining # location is nothrow # - # TODO: We could relax item 3, by inlining the finalizer multiple times. + # TODO: We could relax the check 2, by inlining the finalizer multiple times. # Check #1: The allocation dominates the finalizer registration domtree = get!(lazydomtree) finalizer_bb = block_for_inst(ir, finalizer_idx) - alloc_bb = block_for_inst(ir, idx) + alloc_bb = block_for_inst(ir, alloc_idx) dominates(domtree, alloc_bb, finalizer_bb) || return nothing - bb_insert_block::Int = finalizer_bb - bb_insert_idx::Union{Int,Nothing} = finalizer_idx - function note_block_use!(usebb::Int, useidx::Int) - new_bb_insert_block = nearest_common_dominator(get!(lazypostdomtree), - bb_insert_block, usebb) - if new_bb_insert_block == bb_insert_block && bb_insert_idx !== nothing - bb_insert_idx = max(bb_insert_idx::Int, useidx) - elseif new_bb_insert_block == usebb - bb_insert_idx = useidx + # Check #2: The insertion block for the finalizer is the post-dominator of all uses + insert_bb::Int = finalizer_bb + insert_idx::Union{Int,Nothing} = finalizer_idx + function note_defuse!(x::Union{Int,SSAUse}) + defuse_idx = x isa SSAUse ? x.idx : x + defuse_idx == finalizer_idx && return nothing + defuse_bb = block_for_inst(ir, defuse_idx) + new_insert_bb = nearest_common_dominator(get!(lazypostdomtree), + insert_bb, defuse_bb) + if new_insert_bb == insert_bb && insert_idx !== nothing + insert_idx = max(insert_idx::Int, defuse_idx) + elseif new_insert_bb == defuse_bb + insert_idx = defuse_idx else - bb_insert_idx = nothing + insert_idx = nothing end - bb_insert_block = new_bb_insert_block + insert_bb = new_insert_bb nothing end - - # Collect all reachable blocks between the finalizer registration and the - # insertion point - blocks = reachable_blocks(ir.cfg, finalizer_bb, alloc_bb) - - # Check #2 - function check_defuse(x::Union{Int,SSAUse}) - duidx = x isa SSAUse ? x.idx : x - duidx == finalizer_idx && return true - bb = block_for_inst(ir, duidx) - # Not reachable from finalizer registration - we're ok - bb ∉ blocks && return true - note_block_use!(bb, duidx) - if dominates(domtree, finalizer_bb, bb) - return true - else - return false - end - 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 + foreach(note_defuse!, defuse.uses) + foreach(note_defuse!, defuse.defs) + insert_bb != 0 || return nothing # verify post-dominator of all uses exists if !OptimizationParams(inlining.interp).assume_fatal_throw # Collect all reachable blocks between the finalizer registration and the # insertion point - blocks = finalizer_bb == bb_insert_block ? Int[finalizer_bb] : - reachable_blocks(ir.cfg, finalizer_bb, bb_insert_block) + blocks = reachable_blocks(ir.cfg, finalizer_bb, insert_bb) - # Check #4 - function check_range_nothrow(ir::IRCode, s::Int, e::Int) + # Check #3 + function check_range_nothrow(s::Int, e::Int) return all(s:e) do sidx::Int sidx == finalizer_idx && return true - sidx == idx && return true + sidx == alloc_idx && return true return is_nothrow(ir, SSAValue(sidx)) end end for bb in blocks range = ir.cfg.blocks[bb].stmts s, e = first(range), last(range) - if bb == bb_insert_block - bb_insert_idx === nothing && continue - e = bb_insert_idx + if bb == insert_bb + insert_idx === nothing && continue + e = insert_idx end if bb == finalizer_bb s = finalizer_idx end - check_range_nothrow(ir, s, e) || return nothing + check_range_nothrow(s, e) || return nothing end end # Ok, legality check complete. Figure out the exact statement where we're # going to inline the finalizer. - loc = bb_insert_idx === nothing ? first(ir.cfg.blocks[bb_insert_block].stmts) : bb_insert_idx::Int - attach_after = bb_insert_idx !== nothing + loc = insert_idx === nothing ? first(ir.cfg.blocks[insert_bb].stmts) : insert_idx::Int + attach_after = insert_idx !== nothing finalizer_stmt = ir[SSAValue(finalizer_idx)][:stmt] argexprs = Any[finalizer_stmt.args[2], finalizer_stmt.args[3]] @@ -1702,11 +1682,10 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse return nothing end -function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse}}, used_ssas::Vector{Int}, lazydomtree::LazyDomtree, inlining::Union{Nothing, InliningState}) +function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}}, used_ssas::Vector{Int}, lazydomtree::LazyDomtree, inlining::Union{Nothing,InliningState}) 𝕃ₒ = inlining === nothing ? SimpleInferenceLattice.instance : optimizer_lattice(inlining.interp) lazypostdomtree = LazyPostDomtree(ir) for (defidx, (intermediaries, defuse)) in defuses - intermediaries = collect(intermediaries) # Check if there are any uses we did not account for. If so, the variable # escapes and we cannot eliminate the allocation. This works, because we're guaranteed # not to include any intermediaries that have dead uses. As a result, missing uses will only ever @@ -1906,7 +1885,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse end end -function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preserves::Vector{Any}) +function form_new_preserves(origex::Expr, intermediaries::Union{Vector{Int},SPCSet}, new_preserves::Vector{Any}) newex = Expr(:foreigncall) nccallargs = length(origex.args[3]::SimpleVector) for i in 1:(6+nccallargs-1) @@ -1915,7 +1894,7 @@ function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preser for i in (6+nccallargs):length(origex.args) x = origex.args[i] # don't need to preserve intermediaries - if isa(x, SSAValue) && x.id in intermediates + if isa(x, SSAValue) && x.id in intermediaries continue end push!(newex.args, x) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 80c8ddbb08c69..fceb920352482 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1570,7 +1570,6 @@ let @test get_finalization_count() == 1000 end - function cfg_finalization7(io) for i = -999:1000 o = DoAllocWithField(0) @@ -1597,7 +1596,6 @@ let @test get_finalization_count() == 1000 end - # optimize `[push!|pushfirst!](::Vector{Any}, x...)` @testset "optimize `$f(::Vector{Any}, x...)`" for f = Any[push!, pushfirst!] @eval begin From 06e7b9d292ed4ced5b523fe94daef30332eabbd3 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 1 Oct 2024 10:13:29 +0530 Subject: [PATCH 284/548] Limit `@inbounds` to indexing in the dual-iterator branch in `copyto_unaliased!` (#55919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies the `copyto_unalised!` implementation where the source and destination have different `IndexStyle`s, and limits the `@inbounds` to only the indexing operation. In particular, the iteration over `eachindex(dest)` is not marked as `@inbounds` anymore. This seems to help with performance when the destination uses Cartesian indexing. Reduced implementation of the branch: ```julia function copyto_proposed!(dest, src) axes(dest) == axes(src) || throw(ArgumentError("incompatible sizes")) iterdest, itersrc = eachindex(dest), eachindex(src) for (destind, srcind) in zip(iterdest, itersrc) @inbounds dest[destind] = src[srcind] end dest end function copyto_current!(dest, src) axes(dest) == axes(src) || throw(ArgumentError("incompatible sizes")) iterdest, itersrc = eachindex(dest), eachindex(src) ret = iterate(iterdest) @inbounds for a in src idx, state = ret::NTuple{2,Any} dest[idx] = a ret = iterate(iterdest, state) end dest end function copyto_current_limitinbounds!(dest, src) axes(dest) == axes(src) || throw(ArgumentError("incompatible sizes")) iterdest, itersrc = eachindex(dest), eachindex(src) ret = iterate(iterdest) for isrc in itersrc idx, state = ret::NTuple{2,Any} @inbounds dest[idx] = src[isrc] ret = iterate(iterdest, state) end dest end ``` ```julia julia> a = zeros(40000,4000); b = rand(size(a)...); julia> av = view(a, UnitRange.(axes(a))...); julia> @btime copyto_current!($av, $b); 617.704 ms (0 allocations: 0 bytes) julia> @btime copyto_current_limitinbounds!($av, $b); 304.146 ms (0 allocations: 0 bytes) julia> @btime copyto_proposed!($av, $b); 240.217 ms (0 allocations: 0 bytes) julia> versioninfo() Julia Version 1.12.0-DEV.1260 Commit 4a4ca9c8152 (2024-09-28 01:49 UTC) Build Info: Official https://julialang.org release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 8 × Intel(R) Core(TM) i5-10310U CPU @ 1.70GHz WORD_SIZE: 64 LLVM: libLLVM-18.1.7 (ORCJIT, skylake) Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores) Environment: JULIA_EDITOR = subl ``` I'm not quite certain why the proposed implementation here (`copyto_proposed!`) is even faster than `copyto_current_limitinbounds!`. In any case, `copyto_proposed!` is easier to read, so I'm not complaining. This fixes https://github.com/JuliaLang/julia/issues/53158 --- base/abstractarray.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 754ab20660ab8..e877a87c2cdd1 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1101,11 +1101,8 @@ function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle: end else # Dual-iterator implementation - ret = iterate(iterdest) - @inbounds for a in src - idx, state = ret::NTuple{2,Any} - dest[idx] = a - ret = iterate(iterdest, state) + for (Idest, Isrc) in zip(iterdest, itersrc) + @inbounds dest[Idest] = src[Isrc] end end end From 1cfda3f9b1a88c8f6069b2cec03fbc957f3ccd3f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 1 Oct 2024 18:10:42 +0530 Subject: [PATCH 285/548] Strong zero in Diagonal triple multiplication (#55927) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, triple multiplication with a `LinearAlgebra.BandedMatrix` sandwiched between two `Diagonal`s isn't associative, as this is implemented using broadcasting, which doesn't assume a strong zero, whereas the two-term matrix multiplication does. ```julia julia> D = Diagonal(StepRangeLen(NaN, 0, 3)); julia> B = Bidiagonal(1:3, 1:2, :U); julia> D * B * D 3×3 Matrix{Float64}: NaN NaN NaN NaN NaN NaN NaN NaN NaN julia> (D * B) * D 3×3 Bidiagonal{Float64, Vector{Float64}}: NaN NaN ⋅ ⋅ NaN NaN ⋅ ⋅ NaN julia> D * (B * D) 3×3 Bidiagonal{Float64, Vector{Float64}}: NaN NaN ⋅ ⋅ NaN NaN ⋅ ⋅ NaN ``` This PR ensures that the 3-term multiplication is evaluated as a sequence of two-term multiplications, which fixes this issue. This also improves performance, as only the bands need to be evaluated now. ```julia julia> D = Diagonal(1:1000); B = Bidiagonal(1:1000, 1:999, :U); julia> @btime $D * $B * $D; 656.364 μs (11 allocations: 7.63 MiB) # v"1.12.0-DEV.1262" 2.483 μs (12 allocations: 31.50 KiB) # This PR ``` --- stdlib/LinearAlgebra/src/special.jl | 2 ++ stdlib/LinearAlgebra/test/diagonal.jl | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 5a7c98cfdf32c..32a5476842933 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -112,6 +112,8 @@ for op in (:+, :-) end end +(*)(Da::Diagonal, A::BandedMatrix, Db::Diagonal) = _tri_matmul(Da, A, Db) + # disambiguation between triangular and banded matrices, banded ones "dominate" _mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix, alpha::Number, beta::Number) = @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index dfb901908ba69..98f5498c71033 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1265,6 +1265,17 @@ end @test *(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n)), Diagonal(1:n)) isa Diagonal end +@testset "triple multiplication with a sandwiched BandedMatrix" begin + D = Diagonal(StepRangeLen(NaN, 0, 4)); + B = Bidiagonal(1:4, 1:3, :U) + C = D * B * D + @test iszero(diag(C, 2)) + # test associativity + C1 = (D * B) * D + C2 = D * (B * D) + @test diag(C,2) == diag(C1,2) == diag(C2,2) +end + @testset "diagind" begin D = Diagonal(1:4) M = Matrix(D) From c3b7573c756ee1e6752f34fa8f1dce77bff4d6b7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 1 Oct 2024 18:38:34 +0530 Subject: [PATCH 286/548] Fix dispatch on `alg` in Float16 Hermitian eigen (#55928) Currently, ```julia julia> using LinearAlgebra julia> A = Hermitian(reshape(Float16[1:16;], 4, 4)); julia> eigen(A).values |> typeof Vector{Float16} (alias for Array{Float16, 1}) julia> eigen(A, LinearAlgebra.QRIteration()).values |> typeof Vector{Float32} (alias for Array{Float32, 1}) ``` This PR moves the specialization on the `eltype` to an internal method, so that firstly all `alg`s dispatch to that method, and secondly, there are no ambiguities introduce by specializing the top-level `eigen`. The latter currently causes test failures in `StaticArrays` (https://github.com/JuliaArrays/StaticArrays.jl/actions/runs/11092206012/job/30816955210?pr=1279), and should be fixed by this PR. --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 19 ++++++++++++------- stdlib/LinearAlgebra/test/symmetriceigen.jl | 5 +++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index fee524a702187..68a1b29f5dbc7 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -20,13 +20,6 @@ function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, alg::Algo throw(ArgumentError("Unsupported value for `alg` keyword.")) end end -function eigen(A::RealHermSymComplexHerm{Float16}; sortby::Union{Function,Nothing}=nothing) - S = eigtype(eltype(A)) - E = eigen!(eigencopy_oftype(A, S), sortby=sortby) - values = convert(AbstractVector{Float16}, E.values) - vectors = convert(AbstractMatrix{isreal(E.vectors) ? Float16 : Complex{Float16}}, E.vectors) - return Eigen(values, vectors) -end """ eigen(A::Union{Hermitian, Symmetric}, alg::Algorithm = default_eigen_alg(A)) -> Eigen @@ -53,10 +46,22 @@ The default `alg` used may change in the future. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """ function eigen(A::RealHermSymComplexHerm, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) + _eigen(A, alg; sortby) +end + +# we dispatch on the eltype in an internal method to avoid ambiguities +function _eigen(A::RealHermSymComplexHerm, alg::Algorithm; sortby) S = eigtype(eltype(A)) eigen!(eigencopy_oftype(A, S), alg; sortby) end +function _eigen(A::RealHermSymComplexHerm{Float16}, alg::Algorithm; sortby::Union{Function,Nothing}=nothing) + S = eigtype(eltype(A)) + E = eigen!(eigencopy_oftype(A, S), alg, sortby=sortby) + values = convert(AbstractVector{Float16}, E.values) + vectors = convert(AbstractMatrix{isreal(E.vectors) ? Float16 : Complex{Float16}}, E.vectors) + return Eigen(values, vectors) +end eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = Eigen(LAPACK.syevr!('V', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)...) diff --git a/stdlib/LinearAlgebra/test/symmetriceigen.jl b/stdlib/LinearAlgebra/test/symmetriceigen.jl index d55d1deb6bf33..71087ae4d8d24 100644 --- a/stdlib/LinearAlgebra/test/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/test/symmetriceigen.jl @@ -171,6 +171,11 @@ end @test D isa Eigen{ComplexF16, Float16, Matrix{ComplexF16}, Vector{Float16}} @test D.values ≈ D32.values @test D.vectors ≈ D32.vectors + + # ensure that different algorithms dispatch correctly + λ, V = eigen(C, LinearAlgebra.QRIteration()) + @test λ isa Vector{Float16} + @test C * V ≈ V * Diagonal(λ) end @testset "complex Symmetric" begin From 4eb2e4787f67437d18738cff491f5aa4de6a6c03 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 1 Oct 2024 19:11:28 +0530 Subject: [PATCH 287/548] Remove specialized `ishermitian` method for `Diagonal{<:Real}` (#55948) The fallback method for `Diagonal{<:Number}` handles this already by checking that the `diag` is real, so we don't need this additional specialization. --- stdlib/LinearAlgebra/src/diagonal.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index d762549a2b228..0a95bac5ffb93 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -227,7 +227,6 @@ Base._reverse(A::Diagonal, dims) = reverse!(Matrix(A); dims) Base._reverse(A::Diagonal, ::Colon) = Diagonal(reverse(A.diag)) Base._reverse!(A::Diagonal, ::Colon) = (reverse!(A.diag); A) -ishermitian(D::Diagonal{<:Real}) = true ishermitian(D::Diagonal{<:Number}) = isreal(D.diag) ishermitian(D::Diagonal) = all(ishermitian, D.diag) issymmetric(D::Diagonal{<:Number}) = true From 81ce6a41d737f15d8bbc2788190dcb5565e20b8b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 1 Oct 2024 10:32:57 -0400 Subject: [PATCH 288/548] Fix logic in `?` docstring example (#55945) --- base/docs/basedocs.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index e28b3a21659a8..a142ecffdb732 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -937,11 +937,14 @@ expression, rather than the side effects that evaluating `b` or `c` may have. See the manual section on [control flow](@ref man-conditional-evaluation) for more details. # Examples -``` +```jldoctest julia> x = 1; y = 2; -julia> x > y ? println("x is larger") : println("y is larger") -y is larger +julia> x > y ? println("x is larger") : println("x is not larger") +x is not larger + +julia> x > y ? "x is larger" : x == y ? "x and y are equal" : "y is larger" +"y is larger" ``` """ kw"?", kw"?:" From cf8df9a7a056d02d1953f1bb8d07946cc1ec6876 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:41:09 +0900 Subject: [PATCH 289/548] fix `unwrap_macrocalls` (#55950) The implementation of `unwrap_macrocalls` has assumed that what `:macrocall` wraps is always an `Expr` object, but that is not necessarily correct: ```julia julia> Base.@assume_effects :nothrow @show 42 ERROR: LoadError: TypeError: in typeassert, expected Expr, got a value of type Int64 Stacktrace: [1] unwrap_macrocalls(ex::Expr) @ Base ./expr.jl:906 [2] var"@assume_effects"(__source__::LineNumberNode, __module__::Module, args::Vararg{Any}) @ Base ./expr.jl:756 in expression starting at REPL[1]:1 ``` This commit addresses this issue. --- base/expr.jl | 4 ++-- test/core.jl | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index c4f64b89de8b6..478ccd7d7cc20 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -902,8 +902,8 @@ end unwrap_macrocalls(@nospecialize(x)) = x function unwrap_macrocalls(ex::Expr) inner = ex - while inner.head === :macrocall - inner = inner.args[end]::Expr + while isexpr(inner, :macrocall) + inner = inner.args[end] end return inner end diff --git a/test/core.jl b/test/core.jl index 1395817d8615e..62fde5261bfd3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8293,3 +8293,14 @@ end # to properly give error messages for basic kwargs... Core.eval(Core.Compiler, quote issue50174(;a=1) = a end) @test_throws MethodError Core.Compiler.issue50174(;b=2) + +let s = mktemp() do path, io + xxx = 42 + redirect_stdout(io) do + Base.@assume_effects :nothrow @show xxx + end + flush(io) + read(path, String) + end + @test strip(s) == "xxx = 42" +end From 75393f6618782c87d4b321bb587b375c0d52326a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 1 Oct 2024 10:42:30 -0400 Subject: [PATCH 290/548] make faster BigFloats (#55906) We can coalesce the two required allocations for the MFPR BigFloat API design into one allocation, hopefully giving a easy performance boost. It would have been slightly easier and more efficient if MPFR BigFloat was already a VLA instead of containing a pointer here, but that does not prevent the optimization. --- base/Base.jl | 1 - base/mpfr.jl | 161 ++++++++++++++++-------- base/{rawbigints.jl => rawbigfloats.jl} | 68 ++++------ stdlib/Random/src/generation.jl | 2 +- test/dict.jl | 2 +- test/mpfr.jl | 6 +- 6 files changed, 138 insertions(+), 102 deletions(-) rename base/{rawbigints.jl => rawbigfloats.jl} (58%) diff --git a/base/Base.jl b/base/Base.jl index 10a8dd1532f92..23633f0b5138b 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -306,7 +306,6 @@ end include("hashing.jl") include("rounding.jl") include("div.jl") -include("rawbigints.jl") include("float.jl") include("twiceprecision.jl") include("complex.jl") diff --git a/base/mpfr.jl b/base/mpfr.jl index d393469aa26a1..9d1a0843ebe06 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -18,12 +18,10 @@ import setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, isone, big, _string_n, decompose, minmax, _precision_with_base_2, sinpi, cospi, sincospi, tanpi, sind, cosd, tand, asind, acosd, atand, - uinttype, exponent_max, exponent_min, ieee754_representation, significand_mask, - RawBigIntRoundingIncrementHelper, truncated, RawBigInt - + uinttype, exponent_max, exponent_min, ieee754_representation, significand_mask using .Base.Libc -import ..Rounding: +import ..Rounding: Rounding, rounding_raw, setrounding_raw, rounds_to_nearest, rounds_away_from_zero, tie_breaker_is_to_even, correct_rounding_requires_increment @@ -39,7 +37,6 @@ else const libmpfr = "libmpfr.so.6" end - version() = VersionNumber(unsafe_string(ccall((:mpfr_get_version,libmpfr), Ptr{Cchar}, ()))) patches() = split(unsafe_string(ccall((:mpfr_get_patches,libmpfr), Ptr{Cchar}, ())),' ') @@ -120,44 +117,116 @@ const mpfr_special_exponent_zero = typemin(Clong) + true const mpfr_special_exponent_nan = mpfr_special_exponent_zero + true const mpfr_special_exponent_inf = mpfr_special_exponent_nan + true +struct BigFloatLayout + prec::Clong + sign::Cint + exp::Clong + d::Ptr{Limb} + # possible padding + p::Limb # Tuple{Vararg{Limb}} +end +const offset_prec = fieldoffset(BigFloatLayout, 1) % Int +const offset_sign = fieldoffset(BigFloatLayout, 2) % Int +const offset_exp = fieldoffset(BigFloatLayout, 3) % Int +const offset_d = fieldoffset(BigFloatLayout, 4) % Int +const offset_p_limbs = ((fieldoffset(BigFloatLayout, 5) % Int + sizeof(Limb) - 1) ÷ sizeof(Limb)) +const offset_p = offset_p_limbs * sizeof(Limb) + """ BigFloat <: AbstractFloat Arbitrary precision floating point number type. """ -mutable struct BigFloat <: AbstractFloat - prec::Clong - sign::Cint - exp::Clong - d::Ptr{Limb} - # _d::Buffer{Limb} # Julia gc handle for memory @ d - _d::String # Julia gc handle for memory @ d (optimized) +struct BigFloat <: AbstractFloat + d::Memory{Limb} # Not recommended for general use: # used internally by, e.g. deepcopy - global function _BigFloat(prec::Clong, sign::Cint, exp::Clong, d::String) - # ccall-based version, inlined below - #z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL, d) - #ccall((:mpfr_custom_init,libmpfr), Cvoid, (Ptr{Limb}, Clong), d, prec) # currently seems to be a no-op in mpfr - #NAN_KIND = Cint(0) - #ccall((:mpfr_custom_init_set,libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, d) - #return z - return new(prec, sign, exp, pointer(d), d) - end + global _BigFloat(d::Memory{Limb}) = new(d) function BigFloat(; precision::Integer=_precision_with_base_2(BigFloat)) precision < 1 && throw(DomainError(precision, "`precision` cannot be less than 1.")) nb = ccall((:mpfr_custom_get_size,libmpfr), Csize_t, (Clong,), precision) - nb = (nb + Core.sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this - #d = Vector{Limb}(undef, nb) - d = _string_n(nb * Core.sizeof(Limb)) - EXP_NAN = mpfr_special_exponent_nan - return _BigFloat(Clong(precision), one(Cint), EXP_NAN, d) # +NAN + nl = (nb + offset_p + sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this + d = Memory{Limb}(undef, nl % Int) + # ccall-based version, inlined below + z = _BigFloat(d) # initialize to +NAN + #ccall((:mpfr_custom_init,libmpfr), Cvoid, (Ptr{Limb}, Clong), BigFloatData(d), prec) # currently seems to be a no-op in mpfr + #NAN_KIND = Cint(0) + #ccall((:mpfr_custom_init_set,libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, BigFloatData(d)) + z.prec = Clong(precision) + z.sign = one(Cint) + z.exp = mpfr_special_exponent_nan + return z end end -# The rounding mode here shouldn't matter. -significand_limb_count(x::BigFloat) = div(sizeof(x._d), sizeof(Limb), RoundToZero) +""" +Segment of raw words of bits interpreted as a big integer. Less +significant words come first. Each word is in machine-native bit-order. +""" +struct BigFloatData{Limb} + d::Memory{Limb} +end + +# BigFloat interface +@inline function Base.getproperty(x::BigFloat, s::Symbol) + d = getfield(x, :d) + p = Base.unsafe_convert(Ptr{Limb}, d) + if s === :prec + return GC.@preserve d unsafe_load(Ptr{Clong}(p) + offset_prec) + elseif s === :sign + return GC.@preserve d unsafe_load(Ptr{Cint}(p) + offset_sign) + elseif s === :exp + return GC.@preserve d unsafe_load(Ptr{Clong}(p) + offset_exp) + elseif s === :d + return BigFloatData(d) + else + return throw(FieldError(typeof(x), s)) + end +end + +@inline function Base.setproperty!(x::BigFloat, s::Symbol, v) + d = getfield(x, :d) + p = Base.unsafe_convert(Ptr{Limb}, d) + if s === :prec + return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_prec, v) + elseif s === :sign + return GC.@preserve d unsafe_store!(Ptr{Cint}(p) + offset_sign, v) + elseif s === :exp + return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_exp, v) + #elseif s === :d # not mutable + else + return throw(FieldError(x, s)) + end +end + +# Ref interface: make sure the conversion to C is done properly +Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = error("not compatible with mpfr") +Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat}) = error("not compatible with mpfr") +Base.cconvert(::Type{Ref{BigFloat}}, x::BigFloat) = x.d # BigFloatData is the Ref type for BigFloat +function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::BigFloatData) + d = getfield(x, :d) + p = Base.unsafe_convert(Ptr{Limb}, d) + GC.@preserve d unsafe_store!(Ptr{Ptr{Limb}}(p) + offset_d, p + offset_p, :monotonic) # :monotonic ensure that TSAN knows that this isn't a data race + return Ptr{BigFloat}(p) +end +Base.unsafe_convert(::Type{Ptr{Limb}}, fd::BigFloatData) = Base.unsafe_convert(Ptr{Limb}, getfield(fd, :d)) + offset_p +function Base.setindex!(fd::BigFloatData, v, i) + d = getfield(fd, :d) + @boundscheck 1 <= i <= length(d) - offset_p_limbs || throw(BoundsError(fd, i)) + @inbounds d[i + offset_p_limbs] = v + return fd +end +function Base.getindex(fd::BigFloatData, i) + d = getfield(fd, :d) + @boundscheck 1 <= i <= length(d) - offset_p_limbs || throw(BoundsError(fd, i)) + @inbounds d[i + offset_p_limbs] +end +Base.length(fd::BigFloatData) = length(getfield(fd, :d)) - offset_p_limbs +Base.copyto!(fd::BigFloatData, limbs) = copyto!(getfield(fd, :d), offset_p_limbs + 1, limbs) # for Random + +include("rawbigfloats.jl") rounding_raw(::Type{BigFloat}) = something(Base.ScopedValues.get(CURRENT_ROUNDING_MODE), ROUNDING_MODE[]) setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r @@ -165,24 +234,12 @@ function setrounding_raw(f::Function, ::Type{BigFloat}, r::MPFRRoundingMode) Base.ScopedValues.@with(CURRENT_ROUNDING_MODE => r, f()) end - rounding(::Type{BigFloat}) = convert(RoundingMode, rounding_raw(BigFloat)) setrounding(::Type{BigFloat}, r::RoundingMode) = setrounding_raw(BigFloat, convert(MPFRRoundingMode, r)) setrounding(f::Function, ::Type{BigFloat}, r::RoundingMode) = setrounding_raw(f, BigFloat, convert(MPFRRoundingMode, r)) -# overload the definition of unsafe_convert to ensure that `x.d` is assigned -# it may have been dropped in the event that the BigFloat was serialized -Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = x -@inline function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat}) - x = x[] - if x.d == C_NULL - x.d = pointer(x._d) - end - return convert(Ptr{BigFloat}, Base.pointer_from_objref(x)) -end - """ BigFloat(x::Union{Real, AbstractString} [, rounding::RoundingMode=rounding(BigFloat)]; [precision::Integer=precision(BigFloat)]) @@ -283,17 +340,18 @@ function BigFloat(x::Float64, r::MPFRRoundingMode=rounding_raw(BigFloat); precis nlimbs = (precision + 8*Core.sizeof(Limb) - 1) ÷ (8*Core.sizeof(Limb)) # Limb is a CLong which is a UInt32 on windows (thank M$) which makes this more complicated and slower. + zd = z.d if Limb === UInt64 for i in 1:nlimbs-1 - unsafe_store!(z.d, 0x0, i) + @inbounds setindex!(zd, 0x0, i) end - unsafe_store!(z.d, val, nlimbs) + @inbounds setindex!(zd, val, nlimbs) else for i in 1:nlimbs-2 - unsafe_store!(z.d, 0x0, i) + @inbounds setindex!(zd, 0x0, i) end - unsafe_store!(z.d, val % UInt32, nlimbs-1) - unsafe_store!(z.d, (val >> 32) % UInt32, nlimbs) + @inbounds setindex!(zd, val % UInt32, nlimbs-1) + @inbounds setindex!(zd, (val >> 32) % UInt32, nlimbs) end z end @@ -440,12 +498,12 @@ function to_ieee754(::Type{T}, x::BigFloat, rm) where {T<:AbstractFloat} ret_u = if is_regular & !rounds_to_inf & !rounds_to_zero if !exp_is_huge_p # significand - v = RawBigInt{Limb}(x._d, significand_limb_count(x)) + v = x.d::BigFloatData len = max(ieee_precision + min(exp_diff, 0), 0)::Int signif = truncated(U, v, len) & significand_mask(T) # round up if necessary - rh = RawBigIntRoundingIncrementHelper(v, len) + rh = BigFloatDataRoundingIncrementHelper(v, len) incr = correct_rounding_requires_increment(rh, rm, sb) # exponent @@ -1193,10 +1251,8 @@ set_emin!(x) = check_exponent_err(ccall((:mpfr_set_emin, libmpfr), Cint, (Clong, function Base.deepcopy_internal(x::BigFloat, stackdict::IdDict) get!(stackdict, x) do - # d = copy(x._d) - d = x._d - d′ = GC.@preserve d unsafe_string(pointer(d), sizeof(d)) # creates a definitely-new String - y = _BigFloat(x.prec, x.sign, x.exp, d′) + d′ = copy(getfield(x, :d)) + y = _BigFloat(d′) #ccall((:mpfr_custom_move,libmpfr), Cvoid, (Ref{BigFloat}, Ptr{Limb}), y, d) # unnecessary return y end::BigFloat @@ -1210,7 +1266,8 @@ function decompose(x::BigFloat)::Tuple{BigInt, Int, Int} s.size = cld(x.prec, 8*sizeof(Limb)) # limbs b = s.size * sizeof(Limb) # bytes ccall((:__gmpz_realloc2, libgmp), Cvoid, (Ref{BigInt}, Culong), s, 8b) # bits - memcpy(s.d, x.d, b) + xd = x.d + GC.@preserve xd memcpy(s.d, Base.unsafe_convert(Ptr{Limb}, xd), b) s, x.exp - 8b, x.sign end diff --git a/base/rawbigints.jl b/base/rawbigfloats.jl similarity index 58% rename from base/rawbigints.jl rename to base/rawbigfloats.jl index a9bb18e163e2d..4377edfc463d8 100644 --- a/base/rawbigints.jl +++ b/base/rawbigfloats.jl @@ -1,41 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -""" -Segment of raw words of bits interpreted as a big integer. Less -significant words come first. Each word is in machine-native bit-order. -""" -struct RawBigInt{T<:Unsigned} - d::String - word_count::Int - - function RawBigInt{T}(d::String, word_count::Int) where {T<:Unsigned} - new{T}(d, word_count) - end -end +# Some operations on BigFloat can be done more directly by treating the data portion ("BigFloatData") as a BigInt -elem_count(x::RawBigInt, ::Val{:words}) = x.word_count +elem_count(x::BigFloatData, ::Val{:words}) = length(x) elem_count(x::Unsigned, ::Val{:bits}) = sizeof(x) * 8 -word_length(::RawBigInt{T}) where {T} = elem_count(zero(T), Val(:bits)) -elem_count(x::RawBigInt{T}, ::Val{:bits}) where {T} = word_length(x) * elem_count(x, Val(:words)) +word_length(::BigFloatData{T}) where {T} = elem_count(zero(T), Val(:bits)) +elem_count(x::BigFloatData{T}, ::Val{:bits}) where {T} = word_length(x) * elem_count(x, Val(:words)) reversed_index(n::Int, i::Int) = n - i - 1 reversed_index(x, i::Int, v::Val) = reversed_index(elem_count(x, v), i)::Int -split_bit_index(x::RawBigInt, i::Int) = divrem(i, word_length(x), RoundToZero) - -function get_elem_words_raw(x::RawBigInt{T}, i::Int) where {T} - @boundscheck if (i < 0) || (elem_count(x, Val(:words)) ≤ i) - throw(BoundsError(x, i)) - end - d = x.d - j = i + 1 - (GC.@preserve d unsafe_load(Ptr{T}(pointer(d)), j))::T -end +split_bit_index(x::BigFloatData, i::Int) = divrem(i, word_length(x), RoundToZero) """ `i` is the zero-based index of the wanted word in `x`, starting from the less significant words. """ -function get_elem(x::RawBigInt, i::Int, ::Val{:words}, ::Val{:ascending}) - @inbounds @inline get_elem_words_raw(x, i) +function get_elem(x::BigFloatData{T}, i::Int, ::Val{:words}, ::Val{:ascending}) where {T} + @inbounds return x[i + 1]::T end function get_elem(x, i::Int, v::Val, ::Val{:descending}) @@ -43,9 +23,9 @@ function get_elem(x, i::Int, v::Val, ::Val{:descending}) get_elem(x, j, v, Val(:ascending)) end -word_is_nonzero(x::RawBigInt, i::Int, v::Val) = !iszero(get_elem(x, i, Val(:words), v)) +word_is_nonzero(x::BigFloatData, i::Int, v::Val) = !iszero(get_elem(x, i, Val(:words), v)) -word_is_nonzero(x::RawBigInt, v::Val) = let x = x +word_is_nonzero(x::BigFloatData, v::Val) = let x = x i -> word_is_nonzero(x, i, v) end @@ -53,7 +33,7 @@ end Returns a `Bool` indicating whether the `len` least significant words of `x` are nonzero. """ -function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:words}) +function tail_is_nonzero(x::BigFloatData, len::Int, ::Val{:words}) any(word_is_nonzero(x, Val(:ascending)), 0:(len - 1)) end @@ -61,7 +41,7 @@ end Returns a `Bool` indicating whether the `len` least significant bits of the `i`-th (zero-based index) word of `x` are nonzero. """ -function tail_is_nonzero(x::RawBigInt, len::Int, i::Int, ::Val{:word}) +function tail_is_nonzero(x::BigFloatData, len::Int, i::Int, ::Val{:word}) !iszero(len) && !iszero(get_elem(x, i, Val(:words), Val(:ascending)) << (word_length(x) - len)) end @@ -70,7 +50,7 @@ end Returns a `Bool` indicating whether the `len` least significant bits of `x` are nonzero. """ -function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:bits}) +function tail_is_nonzero(x::BigFloatData, len::Int, ::Val{:bits}) if 0 < len word_count, bit_count_in_word = split_bit_index(x, len) tail_is_nonzero(x, bit_count_in_word, word_count, Val(:word)) || @@ -90,7 +70,7 @@ end """ Returns a `Bool` that is the `i`-th (zero-based index) bit of `x`. """ -function get_elem(x::RawBigInt, i::Int, ::Val{:bits}, v::Val{:ascending}) +function get_elem(x::BigFloatData, i::Int, ::Val{:bits}, v::Val{:ascending}) vb = Val(:bits) if 0 ≤ i < elem_count(x, vb) word_index, bit_index_in_word = split_bit_index(x, i) @@ -106,7 +86,7 @@ Returns an integer of type `R`, consisting of the `len` most significant bits of `x`. If there are less than `len` bits in `x`, the least significant bits are zeroed. """ -function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} +function truncated(::Type{R}, x::BigFloatData, len::Int) where {R<:Integer} ret = zero(R) if 0 < len word_count, bit_count_in_word = split_bit_index(x, len) @@ -116,7 +96,7 @@ function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} for w ∈ 0:(word_count - 1) ret <<= k - if w < lenx + if w < lenx # if the output type is larger, truncate turns into zero-extend word = get_elem(x, w, vals...) ret |= R(word) end @@ -124,7 +104,7 @@ function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} if !iszero(bit_count_in_word) ret <<= bit_count_in_word - if word_count < lenx + if word_count < lenx # if the output type is larger, truncate turns into zero-extend wrd = get_elem(x, word_count, vals...) ret |= R(wrd >>> (k - bit_count_in_word)) end @@ -133,14 +113,14 @@ function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} ret::R end -struct RawBigIntRoundingIncrementHelper{T<:Unsigned} - n::RawBigInt{T} +struct BigFloatDataRoundingIncrementHelper{T<:Unsigned} + n::BigFloatData{T} trunc_len::Int final_bit::Bool round_bit::Bool - function RawBigIntRoundingIncrementHelper{T}(n::RawBigInt{T}, len::Int) where {T<:Unsigned} + function BigFloatDataRoundingIncrementHelper{T}(n::BigFloatData{T}, len::Int) where {T<:Unsigned} vals = (Val(:bits), Val(:descending)) f = get_elem(n, len - 1, vals...) r = get_elem(n, len , vals...) @@ -148,15 +128,15 @@ struct RawBigIntRoundingIncrementHelper{T<:Unsigned} end end -function RawBigIntRoundingIncrementHelper(n::RawBigInt{T}, len::Int) where {T<:Unsigned} - RawBigIntRoundingIncrementHelper{T}(n, len) +function BigFloatDataRoundingIncrementHelper(n::BigFloatData{T}, len::Int) where {T<:Unsigned} + BigFloatDataRoundingIncrementHelper{T}(n, len) end -(h::RawBigIntRoundingIncrementHelper)(::Rounding.FinalBit) = h.final_bit +(h::BigFloatDataRoundingIncrementHelper)(::Rounding.FinalBit) = h.final_bit -(h::RawBigIntRoundingIncrementHelper)(::Rounding.RoundBit) = h.round_bit +(h::BigFloatDataRoundingIncrementHelper)(::Rounding.RoundBit) = h.round_bit -function (h::RawBigIntRoundingIncrementHelper)(::Rounding.StickyBit) +function (h::BigFloatDataRoundingIncrementHelper)(::Rounding.StickyBit) v = Val(:bits) n = h.n tail_is_nonzero(n, elem_count(n, v) - h.trunc_len - 1, v) diff --git a/stdlib/Random/src/generation.jl b/stdlib/Random/src/generation.jl index d8bb48d2764d2..b605dff9e5d80 100644 --- a/stdlib/Random/src/generation.jl +++ b/stdlib/Random/src/generation.jl @@ -66,7 +66,7 @@ function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat) limbs[end] |= Limb_high_bit end z.sign = 1 - GC.@preserve limbs unsafe_copyto!(z.d, pointer(limbs), sp.nlimbs) + copyto!(z.d, limbs) randbool end diff --git a/test/dict.jl b/test/dict.jl index 13c60d5a6a053..909afb3607907 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1049,7 +1049,7 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep # issue #26939 d26939 = WeakKeyDict() - (@noinline d -> d[big"1.0" + 1.1] = 1)(d26939) + (@noinline d -> d[big"1" + 1] = 1)(d26939) GC.gc() # primarily to make sure this doesn't segfault @test count(d26939) == 0 @test length(d26939.ht) == 1 diff --git a/test/mpfr.jl b/test/mpfr.jl index 63da732df1c09..c212bdfc92821 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -1089,11 +1089,11 @@ end end end -@testset "RawBigInt truncation OOB read" begin +@testset "BigFloatData truncation OOB read" begin @testset "T: $T" for T ∈ (UInt8, UInt16, UInt32, UInt64, UInt128) - v = Base.RawBigInt{T}("a"^sizeof(T), 1) + v = Base.MPFR.BigFloatData{T}(fill(typemax(T), 1 + Base.MPFR.offset_p_limbs)) @testset "bit_count: $bit_count" for bit_count ∈ (0:10:80) - @test Base.truncated(UInt128, v, bit_count) isa Any + @test Base.MPFR.truncated(UInt128, v, bit_count) isa Any end end end From 03f8523f27b55f75e16ff1ef592c2bbb1eafd46c Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Tue, 1 Oct 2024 16:53:59 +0200 Subject: [PATCH 291/548] Add propagate_inbounds_meta to atomic genericmemory ops (#55902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `memoryref(mem, i)` will otherwise emit a boundscheck. ``` ; │ @ /home/vchuravy/WorkstealingQueues/src/CLL.jl:53 within `setindex_atomic!` @ genericmemory.jl:329 ; │┌ @ boot.jl:545 within `memoryref` %ptls_field = getelementptr inbounds i8, ptr %tls_pgcstack, i64 16 %ptls_load = load ptr, ptr %ptls_field, align 8 %"box::GenericMemoryRef" = call noalias nonnull align 8 dereferenceable(32) ptr @ijl_gc_small_alloc(ptr %ptls_load, i32 552, i32 32, i64 23456076646928) #9 %"box::GenericMemoryRef.tag_addr" = getelementptr inbounds i64, ptr %"box::GenericMemoryRef", i64 -1 store atomic i64 23456076646928, ptr %"box::GenericMemoryRef.tag_addr" unordered, align 8 store ptr %memoryref_data, ptr %"box::GenericMemoryRef", align 8 %.repack8 = getelementptr inbounds { ptr, ptr }, ptr %"box::GenericMemoryRef", i64 0, i32 1 store ptr %memoryref_mem, ptr %.repack8, align 8 call void @ijl_bounds_error_int(ptr nonnull %"box::GenericMemoryRef", i64 %7) unreachable ``` For the Julia code: ```julia function Base.setindex_atomic!(buf::WSBuffer{T}, order::Symbol, val::T, idx::Int64) where T @inbounds Base.setindex_atomic!(buf.buffer, order, val,((idx - 1) & buf.mask) + 1) end ``` from https://github.com/gbaraldi/WorkstealingQueues.jl/blob/0ebc57237cf0c90feedf99e4338577d04b67805b/src/CLL.jl#L41 --- base/genericmemory.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 6537839320206..c4ebbc6ca14e1 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -320,11 +320,13 @@ end # get, set(once), modify, swap and replace at index, atomically function getindex_atomic(mem::GenericMemory, order::Symbol, i::Int) + @_propagate_inbounds_meta memref = memoryref(mem, i) return memoryrefget(memref, order, @_boundscheck) end function setindex_atomic!(mem::GenericMemory, order::Symbol, val, i::Int) + @_propagate_inbounds_meta T = eltype(mem) memref = memoryref(mem, i) return memoryrefset!( @@ -342,6 +344,7 @@ function setindexonce_atomic!( val, i::Int, ) + @_propagate_inbounds_meta T = eltype(mem) memref = memoryref(mem, i) return Core.memoryrefsetonce!( @@ -354,11 +357,13 @@ function setindexonce_atomic!( end function modifyindex_atomic!(mem::GenericMemory, order::Symbol, op, val, i::Int) + @_propagate_inbounds_meta memref = memoryref(mem, i) return Core.memoryrefmodify!(memref, op, val, order, @_boundscheck) end function swapindex_atomic!(mem::GenericMemory, order::Symbol, val, i::Int) + @_propagate_inbounds_meta T = eltype(mem) memref = memoryref(mem, i) return Core.memoryrefswap!( @@ -377,6 +382,7 @@ function replaceindex_atomic!( desired, i::Int, ) + @_propagate_inbounds_meta T = eltype(mem) memref = memoryref(mem, i) return Core.memoryrefreplace!( From dd310849adbf9f089d7e21c142b513deb8ff7b01 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Tue, 1 Oct 2024 20:56:25 +0200 Subject: [PATCH 292/548] fix rounding mode in construction of `BigFloat` from pi (#55911) The default argument of the method was outdated, reading the global default rounding directly, bypassing the `ScopedValue` stuff. --- base/irrationals.jl | 2 +- test/rounding.jl | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index eafe388162353..b3073c503238a 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -216,7 +216,7 @@ function irrational(sym, val, def) esym = esc(sym) qsym = esc(Expr(:quote, sym)) bigconvert = isa(def,Symbol) ? quote - function Base.BigFloat(::Irrational{$qsym}, r::MPFR.MPFRRoundingMode=MPFR.ROUNDING_MODE[]; precision=precision(BigFloat)) + function Base.BigFloat(::Irrational{$qsym}, r::MPFR.MPFRRoundingMode=Rounding.rounding_raw(BigFloat); precision=precision(BigFloat)) c = BigFloat(;precision=precision) ccall(($(string("mpfr_const_", def)), :libmpfr), Cint, (Ref{BigFloat}, MPFR.MPFRRoundingMode), c, r) diff --git a/test/rounding.jl b/test/rounding.jl index 76b15ec1d9118..6fad6f62e8dfe 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -470,3 +470,28 @@ end @test prevfloat(f) < i end end + +@testset "π to `BigFloat` with `setrounding`" begin + function irrational_to_big_float(c::AbstractIrrational) + BigFloat(c) + end + + function irrational_to_big_float_with_rounding_mode(c::AbstractIrrational, rm::RoundingMode) + f = () -> irrational_to_big_float(c) + setrounding(f, BigFloat, rm) + end + + function irrational_to_big_float_with_rounding_mode_and_precision(c::AbstractIrrational, rm::RoundingMode, prec::Int) + f = () -> irrational_to_big_float_with_rounding_mode(c, rm) + setprecision(f, BigFloat, prec) + end + + for c ∈ (π, MathConstants.γ, MathConstants.catalan) + for p ∈ 1:40 + @test ( + irrational_to_big_float_with_rounding_mode_and_precision(c, RoundDown, p) < c < + irrational_to_big_float_with_rounding_mode_and_precision(c, RoundUp, p) + ) + end + end +end From a45d701e216139a9ef6d5e1f674e943d18677c8d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:59:29 +0900 Subject: [PATCH 293/548] fix `nonsetable_type_hint_handler` (#55962) The current implementation is wrong, causing it to display inappropriate hints like the following: ```julia julia> s = Some("foo"); julia> s[] = "bar" ERROR: MethodError: no method matching setindex!(::Some{String}, ::String) The function `setindex!` exists, but no method is defined for this combination of argument types. You attempted to index the type String, rather than an instance of the type. Make sure you create the type using its constructor: d = String([...]) rather than d = String Stacktrace: [1] top-level scope @ REPL[2]:1 ``` --- base/errorshow.jl | 2 +- test/errorshow.jl | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 9c8aad8b6ee2c..20bdee1de6ec0 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -1052,7 +1052,7 @@ function nonsetable_type_hint_handler(io, ex, arg_types, kwargs) print(io, "\nAre you trying to index into an array? For multi-dimensional arrays, separate the indices with commas: ") printstyled(io, "a[1, 2]", color=:cyan) print(io, " rather than a[1][2]") - else isType(T) + elseif isType(T) Tx = T.parameters[1] print(io, "\nYou attempted to index the type $Tx, rather than an instance of the type. Make sure you create the type using its constructor: ") printstyled(io, "d = $Tx([...])", color=:cyan) diff --git a/test/errorshow.jl b/test/errorshow.jl index 3ede370553212..db22fea1131d1 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -739,8 +739,7 @@ end pop!(Base.Experimental._hint_handlers[DomainError]) # order is undefined, don't copy this struct ANumber <: Number end -let err_str - err_str = @except_str ANumber()(3 + 4) MethodError +let err_str = @except_str ANumber()(3 + 4) MethodError @test occursin("objects of type $(curmod_prefix)ANumber are not callable", err_str) @test count(==("Maybe you forgot to use an operator such as *, ^, %, / etc. ?"), split(err_str, '\n')) == 1 # issue 40478 @@ -748,22 +747,25 @@ let err_str @test count(==("Maybe you forgot to use an operator such as *, ^, %, / etc. ?"), split(err_str, '\n')) == 1 end -let err_str - a = [1 2; 3 4]; +let a = [1 2; 3 4]; err_str = @except_str (a[1][2] = 5) MethodError @test occursin("\nAre you trying to index into an array? For multi-dimensional arrays, separate the indices with commas: ", err_str) @test occursin("a[1, 2]", err_str) @test occursin("rather than a[1][2]", err_str) end -let err_str - d = Dict +let d = Dict err_str = @except_str (d[1] = 5) MethodError @test occursin("\nYou attempted to index the type Dict, rather than an instance of the type. Make sure you create the type using its constructor: ", err_str) @test occursin("d = Dict([...])", err_str) @test occursin(" rather than d = Dict", err_str) end +let s = Some("foo") + err_str = @except_str (s[] = "bar") MethodError + @test !occursin("You attempted to index the type String", err_str) +end + # Execute backtrace once before checking formatting, see #38858 backtrace() From fbb3e1175d52abec0ff4ca83d8c9e126d9f8a06b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 2 Oct 2024 07:27:35 -0400 Subject: [PATCH 294/548] REPL: make UndefVarError aware of imported modules (#55932) --- base/experimental.jl | 4 ++-- stdlib/REPL/src/REPL.jl | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/base/experimental.jl b/base/experimental.jl index 6e757e9fa0e5f..648b5da0ed9a1 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -319,9 +319,9 @@ function show_error_hints(io, ex, args...) for handler in hinters try @invokelatest handler(io, ex, args...) - catch err + catch tn = typeof(handler).name - @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" + @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" exception=current_exceptions() end end end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 44fe0446240c6..272b907165341 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -74,7 +74,17 @@ end function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol) Base.isbindingresolved(m, var) || return false (Base.isexported(m, var) || Base.ispublic(m, var)) || return false - print(io, "\nHint: a global variable of this name also exists in $m.") + active_mod = Base.active_module() + print(io, "\nHint: ") + if isdefined(active_mod, Symbol(m)) + print(io, "a global variable of this name also exists in $m.") + else + if Symbol(m) == var + print(io, "$m is loaded but not imported in the active module $active_mod.") + else + print(io, "a global variable of this name may be made accessible by importing $m in the current active module $active_mod") + end + end return true end From 5fc582b3fcc8adbd5e4b9a8df790a63fcb7f7a9c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 3 Oct 2024 07:17:00 +0900 Subject: [PATCH 295/548] fix test/staged.jl (#55967) In particular, the implementation of `overdub_generator54341` was dangerous. This fixes it up. --- test/staged.jl | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/staged.jl b/test/staged.jl index aec4a3bf135d3..0112dd73b45f7 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -381,11 +381,18 @@ let @test length(ir.cfg.blocks) == 1 end +function generate_lambda_ex(world::UInt, source::LineNumberNode, + argnames::Core.SimpleVector, spnames::Core.SimpleVector, + body::Expr) + stub = Core.GeneratedFunctionStub(identity, argnames, spnames) + return stub(world, source, body) +end + # Test that `Core.CachedGenerator` works as expected struct Generator54916 <: Core.CachedGenerator end function (::Generator54916)(world::UInt, source::LineNumberNode, args...) - stub = Core.GeneratedFunctionStub(identity, Core.svec(:doit54916, :func, :arg), Core.svec()) - return stub(world, source, :(func(arg))) + return generate_lambda_ex(world, source, + Core.svec(:doit54916, :func, :arg), Core.svec(), :(func(arg))) end @eval function doit54916(func, arg) $(Expr(:meta, :generated, Generator54916())) @@ -412,8 +419,8 @@ function generator49715(world, source, self, f, tt) sig = Tuple{f, tt.parameters...} mi = Base._which(sig; world) error("oh no") - stub = Core.GeneratedFunctionStub(identity, Core.svec(:methodinstance, :ctx, :x, :f), Core.svec()) - stub(world, source, :(nothing)) + return generate_lambda_ex(world, source, + Core.svec(:doit49715, :f, :tt), Core.svec(), :(nothing)) end @eval function doit49715(f, tt) $(Expr(:meta, :generated, generator49715)) @@ -426,9 +433,10 @@ function overdubbee54341(a, b) a + b end const overdubee_codeinfo54341 = code_lowered(overdubbee54341, Tuple{Any, Any})[1] -function overdub_generator54341(world::UInt, source::LineNumberNode, args...) - if length(args) != 2 - :(error("Wrong number of arguments")) +function overdub_generator54341(world::UInt, source::LineNumberNode, selftype, fargtypes) + if length(fargtypes) != 2 + return generate_lambda_ex(world, source, + Core.svec(:overdub54341, :args), Core.svec(), :(error("Wrong number of arguments"))) else return copy(overdubee_codeinfo54341) end @@ -438,3 +446,4 @@ end $(Expr(:meta, :generated_only)) end @test overdub54341(1, 2) == 3 +@test_throws "Wrong number of arguments" overdub54341(1, 2, 3) From d19bb472fbc92c1d93645426025f77e449cee763 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 2 Oct 2024 19:46:24 -0400 Subject: [PATCH 296/548] Explicitly store a module's location (#55963) Revise wants to know what file a module's `module` definition is in. Currently it does this by looking at the source location for the implicitly generated `eval` method. This is terrible for two reasons: 1. The method may not exist if the module is a baremodule (which is not particularly common, which is probably why we haven't seen it). 2. The fact that the implicitly generated `eval` method has this location information is an implementation detail that I'd like to get rid of (#55949). This PR adds explicit file/line info to `Module`, so that Revise doesn't have to use the hack anymore. --- base/reflection.jl | 11 +++++++++++ src/jl_exported_funcs.inc | 1 + src/julia.h | 2 ++ src/module.c | 10 ++++++++++ src/staticdata.c | 3 +++ src/toplevel.c | 4 ++++ 6 files changed, 31 insertions(+) diff --git a/base/reflection.jl b/base/reflection.jl index f738ca1a618ae..80eeb4c4efb12 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -76,6 +76,17 @@ function fullname(m::Module) return (fullname(mp)..., mn) end +""" + moduleloc(m::Module) -> LineNumberNode + +Get the location of the `module` definition. +""" +function moduleloc(m::Module) + line = Ref{Int32}(0) + file = ccall(:jl_module_getloc, Ref{Symbol}, (Any, Ref{Int32}), m, line) + return LineNumberNode(Int(line[]), file) +end + """ names(x::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) -> Vector{Symbol} diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 0c712ef37cb5b..a00a0171d23b7 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -316,6 +316,7 @@ XX(jl_module_name) \ XX(jl_module_names) \ XX(jl_module_parent) \ + XX(jl_module_getloc) \ XX(jl_module_public) \ XX(jl_module_public_p) \ XX(jl_module_use) \ diff --git a/src/julia.h b/src/julia.h index c6ff729a308eb..ed3d9bf825658 100644 --- a/src/julia.h +++ b/src/julia.h @@ -710,6 +710,8 @@ typedef struct _jl_module_t { struct _jl_module_t *parent; _Atomic(jl_svec_t*) bindings; _Atomic(jl_genericmemory_t*) bindingkeyset; // index lookup by name into bindings + jl_sym_t *file; + int32_t line; // hidden fields: arraylist_t usings; // modules with all bindings potentially imported jl_uuid_t build_id; diff --git a/src/module.c b/src/module.c index a6c05d279f5b0..f4da7e1e994de 100644 --- a/src/module.c +++ b/src/module.c @@ -52,6 +52,8 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->compile = -1; m->infer = -1; m->max_methods = -1; + m->file = name; // Using the name as a placeholder is better than nothing + m->line = 0; m->hash = parent == NULL ? bitmix(name->hash, jl_module_type->hash) : bitmix(name->hash, parent->hash); JL_MUTEX_INIT(&m->lock, "module->lock"); @@ -1179,6 +1181,14 @@ jl_module_t *jl_module_root(jl_module_t *m) } } +JL_DLLEXPORT jl_sym_t *jl_module_getloc(jl_module_t *m, int32_t *line) +{ + if (line) { + *line = m->line; + } + return m->file; +} + JL_DLLEXPORT jl_uuid_t jl_module_build_id(jl_module_t *m) { return m->build_id; } JL_DLLEXPORT jl_uuid_t jl_module_uuid(jl_module_t* m) { return m->uuid; } diff --git a/src/staticdata.c b/src/staticdata.c index aa9a16daab7a5..0a8cbe6db7c67 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1259,6 +1259,9 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t jl_atomic_store_relaxed(&newm->bindingkeyset, NULL); arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, bindingkeyset))); arraylist_push(&s->relocs_list, (void*)backref_id(s, jl_atomic_load_relaxed(&m->bindingkeyset), s->link_ids_relocs)); + newm->file = NULL; + arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, file))); + arraylist_push(&s->relocs_list, (void*)backref_id(s, m->file, s->link_ids_relocs)); // write out the usings list memset(&newm->usings._space, 0, sizeof(newm->usings._space)); diff --git a/src/toplevel.c b/src/toplevel.c index 5d17a3fcf89a7..8caa8b086ec00 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -213,6 +213,10 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex form = NULL; } + newm->file = jl_symbol(filename); + jl_gc_wb_knownold(newm, newm->file); + newm->line = lineno; + for (int i = 0; i < jl_array_nrows(exprs); i++) { // process toplevel form ct->world_age = jl_atomic_load_acquire(&jl_world_counter); From 3034fc5e3f76d46e57409b4b098577dfa60cc3fa Mon Sep 17 00:00:00 2001 From: Dennis Hoelgaard Bal <61620837+KronosTheLate@users.noreply.github.com> Date: Thu, 3 Oct 2024 01:56:06 +0200 Subject: [PATCH 297/548] mergewith: add single argument example to docstring (#55964) I ran into this edge case. I though it should be documented. --------- Co-authored-by: Lilith Orion Hafner --- base/abstractdict.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 62a5b3ee9e1b0..85a726b4cdbf4 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -392,6 +392,10 @@ Dict{String, Float64} with 3 entries: julia> ans == mergewith(+)(a, b) true + +julia> mergewith(-, Dict(), Dict(:a=>1)) # Combining function only used if key is present in both +Dict{Any, Any} with 1 entry: + :a => 1 ``` """ mergewith(combine, d::AbstractDict, others::AbstractDict...) = From 77c5875b3cbe85e7fb0bb5a7e796809c901ede95 Mon Sep 17 00:00:00 2001 From: Michael Cho Date: Wed, 2 Oct 2024 20:46:36 -0400 Subject: [PATCH 298/548] [build] avoid libedit linkage and align libccalllazy* SONAMEs (#55968) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While building the 1.11.0-rc4 in Homebrew[^1] in preparation for 1.11.0 release (and to confirm Sequoia successfully builds) I noticed some odd linkage for our Linux builds, which included of: 1. LLVM libraries were linking to `libedit.so`, e.g. ``` Dynamic Section: NEEDED libedit.so.0 NEEDED libz.so.1 NEEDED libzstd.so.1 NEEDED libstdc++.so.6 NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6 NEEDED ld-linux-x86-64.so.2 SONAME libLLVM-16jl.so ``` CMakeCache.txt showed ``` //Use libedit if available. LLVM_ENABLE_LIBEDIT:BOOL=ON ``` Which might be overriding `HAVE_LIBEDIT` at https://github.com/JuliaLang/llvm-project/blob/julia-release/16.x/llvm/cmake/config-ix.cmake#L222-L225. So just added `LLVM_ENABLE_LIBEDIT` 2. Wasn't sure if there was a reason for this but `libccalllazy*` had mismatched SONAME: ```console ❯ objdump -p lib/julia/libccalllazy* | rg '\.so' lib/julia/libccalllazybar.so: file format elf64-x86-64 NEEDED ccalllazyfoo.so SONAME ccalllazybar.so lib/julia/libccalllazyfoo.so: file format elf64-x86-64 SONAME ccalllazyfoo.so ``` Modifying this, but can drop if intentional. --- [^1]: https://github.com/Homebrew/homebrew-core/pull/192116 --- deps/llvm.mk | 2 +- src/Makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps/llvm.mk b/deps/llvm.mk index 73697069a4fac..3f4bc3e6746f0 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -102,7 +102,7 @@ endif LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir)) LLVM_CMAKE += -DLLVM_INCLUDE_UTILS=ON -DLLVM_INSTALL_UTILS=ON -LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_LIBEDIT=Off +LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_LIBEDIT=Off -DLLVM_ENABLE_LIBEDIT=OFF ifeq ($(LLVM_ASSERTIONS), 1) LLVM_CMAKE += -DLLVM_ENABLE_ASSERTIONS:BOOL=ON endif # LLVM_ASSERTIONS diff --git a/src/Makefile b/src/Makefile index 52e673aa6cc1a..a6b1f433b73ce 100644 --- a/src/Makefile +++ b/src/Makefile @@ -287,10 +287,10 @@ endif $(INSTALL_NAME_CMD)libccalltest.$(SHLIB_EXT) $@ $(build_shlibdir)/libccalllazyfoo.$(SHLIB_EXT): $(SRCDIR)/ccalllazyfoo.c - @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JL_CFLAGS) $(JCPPFLAGS) $(FLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(call SONAME_FLAGS,ccalllazyfoo.$(SHLIB_EXT))) + @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JL_CFLAGS) $(JCPPFLAGS) $(FLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(call SONAME_FLAGS,libccalllazyfoo.$(SHLIB_EXT))) $(build_shlibdir)/libccalllazybar.$(SHLIB_EXT): $(SRCDIR)/ccalllazybar.c $(build_shlibdir)/libccalllazyfoo.$(SHLIB_EXT) - @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JL_CFLAGS) $(JCPPFLAGS) $(FLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(call SONAME_FLAGS,ccalllazybar.$(SHLIB_EXT)) -lccalllazyfoo) + @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JL_CFLAGS) $(JCPPFLAGS) $(FLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(call SONAME_FLAGS,libccalllazybar.$(SHLIB_EXT)) -lccalllazyfoo) $(build_shlibdir)/libllvmcalltest.$(SHLIB_EXT): $(SRCDIR)/llvmcalltest.cpp $(LLVM_CONFIG_ABSOLUTE) @$(call PRINT_CC, $(CXX) $(LLVM_CXXFLAGS) $(FLAGS) $(CPPFLAGS) $(CXXFLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(NO_WHOLE_ARCHIVE) $(CG_LLVMLINK)) -lpthread From 234baad6c4406819af9778c1d4f753cd15f149a3 Mon Sep 17 00:00:00 2001 From: "David K. Zhang" Date: Thu, 3 Oct 2024 13:26:45 +0000 Subject: [PATCH 299/548] Add missing `copy!(::AbstractMatrix, ::UniformScaling)` method (#55970) Hi everyone! First PR to Julia here. It was noticed in a Slack thread yesterday that `copy!(A, I)` doesn't work, but `copyto!(A, I)` does. This PR adds the missing method for `copy!(::AbstractMatrix, ::UniformScaling)`, which simply defers to `copyto!`, and corresponding tests. I added a `compat` notice for Julia 1.12. --------- Co-authored-by: Lilith Orion Hafner --- stdlib/LinearAlgebra/src/uniformscaling.jl | 10 ++++++++++ stdlib/LinearAlgebra/test/uniformscaling.jl | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index b75886b8d99fb..472ea53078f87 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -403,6 +403,16 @@ function copyto!(A::Tridiagonal, J::UniformScaling) return A end +""" + copy!(dest::AbstractMatrix, src::UniformScaling) + +Copies a [`UniformScaling`](@ref) onto a matrix. + +!!! compat "Julia 1.12" + This method is available as of Julia 1.12. +""" +Base.copy!(A::AbstractMatrix, J::UniformScaling) = copyto!(A, J) + function cond(J::UniformScaling{T}) where T onereal = inv(one(real(J.λ))) return J.λ ≠ zero(T) ? onereal : oftype(onereal, Inf) diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl index 92547e8648d8a..d335cd6f63521 100644 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ b/stdlib/LinearAlgebra/test/uniformscaling.jl @@ -226,6 +226,13 @@ let @test copyto!(B, J) == [λ zero(λ)] end + @testset "copy!" begin + A = Matrix{Int}(undef, (3,3)) + @test copy!(A, I) == one(A) + B = Matrix{ComplexF64}(undef, (1,2)) + @test copy!(B, J) == [λ zero(λ)] + end + @testset "binary ops with vectors" begin v = complex.(randn(3), randn(3)) # As shown in #20423@GitHub, vector acts like x1 matrix when participating in linear algebra From be401635fe02b28ce994e2e3cae0733d101f8927 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 3 Oct 2024 08:28:32 -0500 Subject: [PATCH 300/548] Add forward progress update to NEWS.md (#54089) Closes #40009 which was left open because of the needs news tag. --------- Co-authored-by: Ian Butterworth --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index cc1bbc7449e5d..fb1fcf381cc7f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -37,6 +37,10 @@ Language changes expression within a given `:toplevel` expression to make use of macros defined earlier in the same `:toplevel` expression. ([#53515]) + - Trivial infinite loops (like `while true; end`) are no longer undefined + behavior. Infinite loops that actually do things (e.g. have side effects + or sleep) were never and are still not undefined behavior. ([#52999]) + Compiler/Runtime improvements ----------------------------- From 6b9719f767d98fa7d6e0d86adf0e204ed226f90e Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 3 Oct 2024 11:22:29 -0400 Subject: [PATCH 301/548] Fix an intermittent test failure in `core` test (#55973) The test wants to assert that `Module` is not resolved in `Main`, but other tests do resolve this identifier, so the test can fail depending on test order (and I've been seeing such failures on CI recently). Fix that by running the test in a fresh subprocess. --- test/core.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core.jl b/test/core.jl index 62fde5261bfd3..b27832209a835 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1183,7 +1183,7 @@ end # Make sure that `Module` is not resolved to `Core.Module` during sysimg generation # so that users can define their own binding named `Module` in Main. -@test !Base.isbindingresolved(Main, :Module) +@test success(`$(Base.julia_cmd()) -e '@assert !Base.isbindingresolved(Main, :Module)'`) # Module() constructor @test names(Module(:anonymous), all = true, imported = true) == [:anonymous] From 42737f79e02bbaf444a9d93e6668b3c55cdb8a6e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 3 Oct 2024 16:23:21 -0400 Subject: [PATCH 302/548] fix comma logic in time_print (#55977) Minor formatting fix --- base/timing.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 6d97d70d2f04c..9686c5b33bccd 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -206,7 +206,7 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl print(io, length(timestr) < 10 ? (" "^(10 - length(timestr))) : "") end print(io, timestr, " seconds") - parens = bytes != 0 || allocs != 0 || gctime > 0 || compile_time > 0 + parens = bytes != 0 || allocs != 0 || gctime > 0 || compile_time > 0 || lock_conflicts > 0 parens && print(io, " (") if bytes != 0 || allocs != 0 allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) @@ -228,7 +228,7 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl print(io, ", ", lock_conflicts, " lock conflict$plural") end if compile_time > 0 - if bytes != 0 || allocs != 0 || gctime > 0 + if bytes != 0 || allocs != 0 || gctime > 0 || lock_conflicts > 0 print(io, ", ") end print(io, Ryu.writefixed(Float64(100*compile_time/elapsedtime), 2), "% compilation time") From b6b5528da1ea8f322b80247ee4c6c3e65b5a236e Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 4 Oct 2024 06:01:42 +0900 Subject: [PATCH 303/548] optimizer: fix up the inlining algorithm to use correct `nargs`/`isva` (#55976) It appears that inlining.jl was not updated in JuliaLang/julia#54341. Specifically, using `nargs`/`isva` from `mi.def::Method` in `ir_prepare_inlining!` causes the following error to occur: ```julia function generate_lambda_ex(world::UInt, source::LineNumberNode, argnames, spnames, @nospecialize body) stub = Core.GeneratedFunctionStub(identity, Core.svec(argnames...), Core.svec(spnames...)) return stub(world, source, body) end function overdubbee54341(a, b) return a + b end const overdubee_codeinfo54341 = code_lowered(overdubbee54341, Tuple{Any, Any})[1] function overdub_generator54341(world::UInt, source::LineNumberNode, selftype, fargtypes) if length(fargtypes) != 2 return generate_lambda_ex(world, source, (:overdub54341, :args), (), :(error("Wrong number of arguments"))) else return copy(overdubee_codeinfo54341) end end @eval function overdub54341(args...) $(Expr(:meta, :generated, overdub_generator54341)) $(Expr(:meta, :generated_only)) end topfunc(x) = overdub54341(x, 2) ``` ```julia julia> topfunc(1) Internal error: during type inference of topfunc(Int64) Encountered unexpected error in runtime: BoundsError(a=Array{Any, 1}(dims=(2,), mem=Memory{Any}(8, 0x10632e780)[SSAValue(2), SSAValue(3), #, #, #, #, #, #]), i=(3,)) throw_boundserror at ./essentials.jl:14 getindex at ./essentials.jl:909 [inlined] ssa_substitute_op! at ./compiler/ssair/inlining.jl:1798 ssa_substitute_op! at ./compiler/ssair/inlining.jl:1852 ir_inline_item! at ./compiler/ssair/inlining.jl:386 ... ``` This commit updates the abstract interpretation and inlining algorithm to use the `nargs`/`isva` values held by `CodeInfo`. Similar modifications have also been made to EscapeAnalysis.jl. @nanosoldier `runbenchmarks("inference", vs=":master")` --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/inferencestate.jl | 25 +++++---- base/compiler/optimize.jl | 14 ++--- base/compiler/ssair/inlining.jl | 68 ++++++++++++++----------- base/compiler/ssair/passes.jl | 4 +- base/compiler/stmtinfo.jl | 1 + base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 7 ++- test/compiler/EscapeAnalysis/EAUtils.jl | 11 ++-- test/staged.jl | 14 ++--- 10 files changed, 81 insertions(+), 67 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 96355f2a6b5dd..c8a25be422637 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1282,7 +1282,7 @@ function semi_concrete_eval_call(interp::AbstractInterpreter, effects = Effects(effects; noub=ALWAYS_TRUE) end exct = refine_exception_type(result.exct, effects) - return ConstCallResults(rt, exct, SemiConcreteResult(mi, ir, effects), effects, mi) + return ConstCallResults(rt, exct, SemiConcreteResult(mi, ir, effects, spec_info(irsv)), effects, mi) end end end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 05d95d1d5bdc7..5f8fb82caaa34 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -236,7 +236,7 @@ mutable struct InferenceState slottypes::Vector{Any} src::CodeInfo cfg::CFG - method_info::MethodInfo + spec_info::SpecInfo #= intermediate states for local abstract interpretation =# currbb::Int @@ -294,7 +294,7 @@ mutable struct InferenceState sptypes = sptypes_from_meth_instance(mi) code = src.code::Vector{Any} cfg = compute_basic_blocks(code) - method_info = MethodInfo(src) + spec_info = SpecInfo(src) currbb = currpc = 1 ip = BitSet(1) # TODO BitSetBoundedMinPrioritySet(1) @@ -351,7 +351,7 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) this = new( - mi, world, mod, sptypes, slottypes, src, cfg, method_info, + mi, world, mod, sptypes, slottypes, src, cfg, spec_info, currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, tasks, pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, @@ -791,7 +791,7 @@ end # TODO add `result::InferenceResult` and put the irinterp result into the inference cache? mutable struct IRInterpretationState - const method_info::MethodInfo + const spec_info::SpecInfo const ir::IRCode const mi::MethodInstance const world::UInt @@ -809,7 +809,7 @@ mutable struct IRInterpretationState parentid::Int function IRInterpretationState(interp::AbstractInterpreter, - method_info::MethodInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any}, + spec_info::SpecInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any}, world::UInt, min_world::UInt, max_world::UInt) curridx = 1 given_argtypes = Vector{Any}(undef, length(argtypes)) @@ -831,7 +831,7 @@ mutable struct IRInterpretationState tasks = WorkThunk[] edges = Any[] callstack = AbsIntState[] - return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, + return new(spec_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0, 0) end end @@ -845,14 +845,13 @@ function IRInterpretationState(interp::AbstractInterpreter, else isa(src, CodeInfo) || return nothing end - method_info = MethodInfo(src) + spec_info = SpecInfo(src) ir = inflate_ir(src, mi) argtypes = va_process_argtypes(optimizer_lattice(interp), argtypes, src.nargs, src.isva) - return IRInterpretationState(interp, method_info, ir, mi, argtypes, world, + return IRInterpretationState(interp, spec_info, ir, mi, argtypes, world, codeinst.min_world, codeinst.max_world) end - # AbsIntState # =========== @@ -927,11 +926,11 @@ is_constproped(::IRInterpretationState) = true is_cached(sv::InferenceState) = !iszero(sv.cache_mode & CACHE_MODE_GLOBAL) is_cached(::IRInterpretationState) = false -method_info(sv::InferenceState) = sv.method_info -method_info(sv::IRInterpretationState) = sv.method_info +spec_info(sv::InferenceState) = sv.spec_info +spec_info(sv::IRInterpretationState) = sv.spec_info -propagate_inbounds(sv::AbsIntState) = method_info(sv).propagate_inbounds -method_for_inference_limit_heuristics(sv::AbsIntState) = method_info(sv).method_for_inference_limit_heuristics +propagate_inbounds(sv::AbsIntState) = spec_info(sv).propagate_inbounds +method_for_inference_limit_heuristics(sv::AbsIntState) = spec_info(sv).method_for_inference_limit_heuristics frame_world(sv::InferenceState) = sv.world frame_world(sv::IRInterpretationState) = sv.world diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 1971b47323f5d..02f6b46e2e73f 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -644,10 +644,10 @@ function ((; code_cache)::GetNativeEscapeCache)(mi::MethodInstance) return false end -function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState) +function refine_effects!(interp::AbstractInterpreter, opt::OptimizationState, sv::PostOptAnalysisState) if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending) ir = sv.ir - nargs = let def = sv.result.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end + nargs = Int(opt.src.nargs) estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp)) argescapes = EscapeAnalysis.ArgEscapeCache(estate) stack_analysis_result!(sv.result, argescapes) @@ -939,7 +939,8 @@ function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner) end end -function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result::InferenceResult) +function ipo_dataflow_analysis!(interp::AbstractInterpreter, opt::OptimizationState, + ir::IRCode, result::InferenceResult) if !is_ipo_dataflow_analysis_profitable(result.ipo_effects) return false end @@ -967,13 +968,13 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: end end - return refine_effects!(interp, sv) + return refine_effects!(interp, opt, sv) end # run the optimization work function optimize(interp::AbstractInterpreter, opt::OptimizationState, caller::InferenceResult) - @timeit "optimizer" ir = run_passes_ipo_safe(opt.src, opt, caller) - ipo_dataflow_analysis!(interp, ir, caller) + @timeit "optimizer" ir = run_passes_ipo_safe(opt.src, opt) + ipo_dataflow_analysis!(interp, opt, ir, caller) return finish(interp, opt, ir, caller) end @@ -995,7 +996,6 @@ matchpass(::Nothing, _, _) = false function run_passes_ipo_safe( ci::CodeInfo, sv::OptimizationState, - caller::InferenceResult, optimize_until = nothing, # run all passes by default ) __stage__ = 0 # used by @pass diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 9f250b156cd2f..5017b619469ff 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -12,6 +12,8 @@ struct InliningTodo mi::MethodInstance # The IR of the inlinee ir::IRCode + # The SpecInfo for the inlinee + spec_info::SpecInfo # The DebugInfo table for the inlinee di::DebugInfo # If the function being inlined is a single basic block we can use a @@ -20,8 +22,8 @@ struct InliningTodo # Effects of the call statement effects::Effects end -function InliningTodo(mi::MethodInstance, (ir, di)::Tuple{IRCode, DebugInfo}, effects::Effects) - return InliningTodo(mi, ir, di, linear_inline_eligible(ir), effects) +function InliningTodo(mi::MethodInstance, ir::IRCode, spec_info::SpecInfo, di::DebugInfo, effects::Effects) + return InliningTodo(mi, ir, spec_info, di, linear_inline_eligible(ir), effects) end struct ConstantCase @@ -321,7 +323,8 @@ function ir_inline_linetable!(debuginfo::DebugInfoStream, inlinee_debuginfo::Deb end function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCode, IncrementalCompact}, - ir::IRCode, di::DebugInfo, mi::MethodInstance, inlined_at::NTuple{3,Int32}, argexprs::Vector{Any}) + ir::IRCode, spec_info::SpecInfo, di::DebugInfo, mi::MethodInstance, + inlined_at::NTuple{3,Int32}, argexprs::Vector{Any}) def = mi.def::Method debuginfo = inline_target isa IRCode ? inline_target.debuginfo : inline_target.ir.debuginfo topline = new_inlined_at = ir_inline_linetable!(debuginfo, di, inlined_at) @@ -334,8 +337,8 @@ function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCod spvals_ssa = insert_node!( removable_if_unused(NewInstruction(Expr(:call, Core._compute_sparams, def, argexprs...), SimpleVector, topline))) end - if def.isva - nargs_def = Int(def.nargs::Int32) + if spec_info.isva + nargs_def = spec_info.nargs if nargs_def > 0 argexprs = fix_va_argexprs!(insert_node!, inline_target, argexprs, nargs_def, topline) end @@ -362,7 +365,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector item::InliningTodo, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) # Ok, do the inlining here inlined_at = compact.result[idx][:line] - ssa_substitute = ir_prepare_inlining!(InsertHere(compact), compact, item.ir, item.di, item.mi, inlined_at, argexprs) + ssa_substitute = ir_prepare_inlining!(InsertHere(compact), compact, item.ir, item.spec_info, item.di, item.mi, inlined_at, argexprs) boundscheck = has_flag(compact.result[idx], IR_FLAG_INBOUNDS) ? :off : boundscheck # If the iterator already moved on to the next basic block, @@ -860,15 +863,14 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, if inferred_result isa ConstantCase add_inlining_backedge!(et, mi) return inferred_result - end - if inferred_result isa InferredResult + elseif inferred_result isa InferredResult (; src, effects) = inferred_result elseif inferred_result isa CodeInstance src = @atomic :monotonic inferred_result.inferred effects = decode_effects(inferred_result.ipo_purity_bits) - else - src = nothing - effects = Effects() + else # there is no cached source available, bail out + return compileable_specialization(mi, Effects(), et, info; + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) end # the duplicated check might have been done already within `analyze_method!`, but still @@ -883,9 +885,12 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) add_inlining_backedge!(et, mi) - ir = inferred_result isa CodeInstance ? retrieve_ir_for_inlining(inferred_result, src) : - retrieve_ir_for_inlining(mi, src, preserve_local_sources) - return InliningTodo(mi, ir, effects) + if inferred_result isa CodeInstance + ir, spec_info, debuginfo = retrieve_ir_for_inlining(inferred_result, src) + else + ir, spec_info, debuginfo = retrieve_ir_for_inlining(mi, src, preserve_local_sources) + end + return InliningTodo(mi, ir, spec_info, debuginfo, effects) end # the special resolver for :invoke-d call @@ -901,23 +906,17 @@ function resolve_todo(mi::MethodInstance, @nospecialize(info::CallInfo), flag::U if cached_result isa ConstantCase add_inlining_backedge!(et, mi) return cached_result - end - if cached_result isa InferredResult - (; src, effects) = cached_result elseif cached_result isa CodeInstance src = @atomic :monotonic cached_result.inferred effects = decode_effects(cached_result.ipo_purity_bits) - else - src = nothing - effects = Effects() + else # there is no cached source available, bail out + return nothing end - preserve_local_sources = true src_inlining_policy(state.interp, src, info, flag) || return nothing - ir = cached_result isa CodeInstance ? retrieve_ir_for_inlining(cached_result, src) : - retrieve_ir_for_inlining(mi, src, preserve_local_sources) + ir, spec_info, debuginfo = retrieve_ir_for_inlining(cached_result, src) add_inlining_backedge!(et, mi) - return InliningTodo(mi, ir, effects) + return InliningTodo(mi, ir, spec_info, debuginfo, effects) end function validate_sparams(sparams::SimpleVector) @@ -971,22 +970,29 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, return resolve_todo(mi, volatile_inf_result, info, flag, state; invokesig) end -function retrieve_ir_for_inlining(cached_result::CodeInstance, src::MaybeCompressed) - src = _uncompressed_ir(cached_result, src)::CodeInfo - return inflate_ir!(src, cached_result.def), src.debuginfo +function retrieve_ir_for_inlining(cached_result::CodeInstance, src::String) + src = _uncompressed_ir(cached_result, src) + return inflate_ir!(src, cached_result.def), SpecInfo(src), src.debuginfo +end +function retrieve_ir_for_inlining(cached_result::CodeInstance, src::CodeInfo) + return inflate_ir!(copy(src), cached_result.def), SpecInfo(src), src.debuginfo end function retrieve_ir_for_inlining(mi::MethodInstance, src::CodeInfo, preserve_local_sources::Bool) if preserve_local_sources src = copy(src) end - return inflate_ir!(src, mi), src.debuginfo + return inflate_ir!(src, mi), SpecInfo(src), src.debuginfo end function retrieve_ir_for_inlining(mi::MethodInstance, ir::IRCode, preserve_local_sources::Bool) if preserve_local_sources ir = copy(ir) end + # COMBAK this is not correct, we should make `InferenceResult` propagate `SpecInfo` + spec_info = let m = mi.def::Method + SpecInfo(Int(m.nargs), m.isva, false, nothing) + end ir.debuginfo.def = mi - return ir, DebugInfo(ir.debuginfo, length(ir.stmts)) + return ir, spec_info, DebugInfo(ir.debuginfo, length(ir.stmts)) end function handle_single_case!(todo::Vector{Pair{Int,Any}}, @@ -1466,8 +1472,8 @@ function semiconcrete_result_item(result::SemiConcreteResult, add_inlining_backedge!(et, mi) preserve_local_sources = OptimizationParams(state.interp).preserve_local_sources - ir = retrieve_ir_for_inlining(mi, result.ir, preserve_local_sources) - return InliningTodo(mi, ir, result.effects) + ir, _, debuginfo = retrieve_ir_for_inlining(mi, result.ir, preserve_local_sources) + return InliningTodo(mi, ir, result.spec_info, debuginfo, result.effects) end function handle_semi_concrete_result!(cases::Vector{InliningCase}, result::SemiConcreteResult, diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 3981f7382d707..e227249b48598 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1532,7 +1532,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, end src_inlining_policy(inlining.interp, src, info, IR_FLAG_NULL) || return false - src, di = retrieve_ir_for_inlining(code, src) + src, spec_info, di = retrieve_ir_for_inlining(code, src) # For now: Require finalizer to only have one basic block length(src.cfg.blocks) == 1 || return false @@ -1542,7 +1542,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, # TODO: Should there be a special line number node for inlined finalizers? inline_at = ir[SSAValue(idx)][:line] - ssa_substitute = ir_prepare_inlining!(InsertBefore(ir, SSAValue(idx)), ir, src, di, mi, inline_at, argexprs) + ssa_substitute = ir_prepare_inlining!(InsertBefore(ir, SSAValue(idx)), ir, src, spec_info, di, mi, inline_at, argexprs) # TODO: Use the actual inliner here rather than open coding this special purpose inliner. ssa_rename = Vector{Any}(undef, length(src.stmts)) diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index ac5ffbdd5d76d..9dba7a4459f9e 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -94,6 +94,7 @@ struct SemiConcreteResult <: ConstResult mi::MethodInstance ir::IRCode effects::Effects + spec_info::SpecInfo end # XXX Technically this does not represent a result of constant inference, but rather that of diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 77a2e02129ce4..8b85f7c6f35f1 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -941,7 +941,7 @@ function typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance, end (; result) = frame opt = OptimizationState(frame, interp) - ir = run_passes_ipo_safe(opt.src, opt, result, optimize_until) + ir = run_passes_ipo_safe(opt.src, opt, optimize_until) rt = widenconst(ignorelimited(result.result)) return ir, rt end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index c51785f23ea29..ecf2417fd6199 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -41,11 +41,14 @@ struct StmtInfo used::Bool end -struct MethodInfo +struct SpecInfo + nargs::Int + isva::Bool propagate_inbounds::Bool method_for_inference_limit_heuristics::Union{Nothing,Method} end -MethodInfo(src::CodeInfo) = MethodInfo( +SpecInfo(src::CodeInfo) = SpecInfo( + Int(src.nargs), src.isva, src.propagate_inbounds, src.method_for_inference_limit_heuristics::Union{Nothing,Method}) diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index 188ec93ebc5be..b8ad4589db626 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -116,12 +116,14 @@ CC.get_inference_world(interp::EscapeAnalyzer) = interp.world CC.get_inference_cache(interp::EscapeAnalyzer) = interp.inf_cache CC.cache_owner(::EscapeAnalyzer) = EAToken() -function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, ir::IRCode, caller::InferenceResult) +function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::OptimizationState, + ir::IRCode, caller::InferenceResult) # run EA on all frames that have been optimized - nargs = let def = caller.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end + nargs = Int(opt.src.nargs) + 𝕃ₒ = CC.optimizer_lattice(interp) get_escape_cache = GetEscapeCache(interp) estate = try - analyze_escapes(ir, nargs, CC.optimizer_lattice(interp), get_escape_cache) + analyze_escapes(ir, nargs, 𝕃ₒ, get_escape_cache) catch err @error "error happened within EA, inspect `Main.failed_escapeanalysis`" Main.failed_escapeanalysis = FailedAnalysis(ir, nargs, get_escape_cache) @@ -133,7 +135,8 @@ function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, ir::IRCode, caller::I end record_escapes!(interp, caller, estate, ir) - @invoke CC.ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, caller::InferenceResult) + @invoke CC.ipo_dataflow_analysis!(interp::AbstractInterpreter, opt::OptimizationState, + ir::IRCode, caller::InferenceResult) end function record_escapes!(interp::EscapeAnalyzer, diff --git a/test/staged.jl b/test/staged.jl index 0112dd73b45f7..1b28144639f97 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -382,9 +382,8 @@ let end function generate_lambda_ex(world::UInt, source::LineNumberNode, - argnames::Core.SimpleVector, spnames::Core.SimpleVector, - body::Expr) - stub = Core.GeneratedFunctionStub(identity, argnames, spnames) + argnames, spnames, @nospecialize body) + stub = Core.GeneratedFunctionStub(identity, Core.svec(argnames...), Core.svec(spnames...)) return stub(world, source, body) end @@ -392,7 +391,7 @@ end struct Generator54916 <: Core.CachedGenerator end function (::Generator54916)(world::UInt, source::LineNumberNode, args...) return generate_lambda_ex(world, source, - Core.svec(:doit54916, :func, :arg), Core.svec(), :(func(arg))) + (:doit54916, :func, :arg), (), :(func(arg))) end @eval function doit54916(func, arg) $(Expr(:meta, :generated, Generator54916())) @@ -420,7 +419,7 @@ function generator49715(world, source, self, f, tt) mi = Base._which(sig; world) error("oh no") return generate_lambda_ex(world, source, - Core.svec(:doit49715, :f, :tt), Core.svec(), :(nothing)) + (:doit49715, :f, :tt), (), nothing) end @eval function doit49715(f, tt) $(Expr(:meta, :generated, generator49715)) @@ -436,7 +435,7 @@ const overdubee_codeinfo54341 = code_lowered(overdubbee54341, Tuple{Any, Any})[1 function overdub_generator54341(world::UInt, source::LineNumberNode, selftype, fargtypes) if length(fargtypes) != 2 return generate_lambda_ex(world, source, - Core.svec(:overdub54341, :args), Core.svec(), :(error("Wrong number of arguments"))) + (:overdub54341, :args), (), :(error("Wrong number of arguments"))) else return copy(overdubee_codeinfo54341) end @@ -446,4 +445,7 @@ end $(Expr(:meta, :generated_only)) end @test overdub54341(1, 2) == 3 +# check if the inlining pass handles `nargs`/`isva` correctly +@test first(only(code_typed((Int,Int)) do x, y; @inline overdub54341(x, y); end)) isa Core.CodeInfo +@test first(only(code_typed((Int,)) do x; @inline overdub54341(x, 1); end)) isa Core.CodeInfo @test_throws "Wrong number of arguments" overdub54341(1, 2, 3) From b9d9b69165493f6fc03870d975be05c67f14a30b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 4 Oct 2024 09:02:29 +0530 Subject: [PATCH 304/548] Add `.zed` directory to `.gitignore` (#55974) Similar to the `vscode` config directory, we may ignore the `zed` directory as well. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 524a12d066c4d..80bdd67619454 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ .DS_Store .idea/* .vscode/* +.zed/* *.heapsnapshot .cache # Buildkite: Ignore the entire .buildkite directory From b2cb6a3fb1071f2bd056cc78e048e58e9186e9b4 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 9 Jun 2024 23:36:26 +0800 Subject: [PATCH 305/548] typeintersect: reduce unneeded allocations from `merge_env` `merge_env` and `final_merge_env` could be skipped for emptiness test or if we know there's only 1 valid Union state. --- src/subtype.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 2011ca8b1c705..acfce3649236f 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -4138,9 +4138,13 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) save_env(e, &se, 1); int niter = 0, total_iter = 0; is[0] = intersect(x, y, e, 0); // root - if (is[0] != jl_bottom_type) + if (is[0] == jl_bottom_type) { + restore_env(e, &se, 1); + } + else if (!e->emptiness_only && has_next_union_state(e, 1)) { niter = merge_env(e, &me, &se, niter); - restore_env(e, &se, 1); + restore_env(e, &se, 1); + } while (next_union_state(e, 1)) { if (e->emptiness_only && is[0] != jl_bottom_type) break; @@ -4148,9 +4152,16 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e->Runions.more = 0; is[1] = intersect(x, y, e, 0); - if (is[1] != jl_bottom_type) + if (is[1] == jl_bottom_type) { + restore_env(e, &se, 1); + } + else if (niter > 0 || (!e->emptiness_only && has_next_union_state(e, 1))) { niter = merge_env(e, &me, &se, niter); - restore_env(e, &se, 1); + restore_env(e, &se, 1); + } + else { + assert(is[0] == jl_bottom_type); + } if (is[0] == jl_bottom_type) is[0] = is[1]; else if (is[1] != jl_bottom_type) { From 5d69bbbb1ab54b44bf970b4ca0f2156f7f14332c Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 9 Jun 2024 23:49:14 +0800 Subject: [PATCH 306/548] typeintersect: trunc env before nested `intersect_all` if valid. This only covers the simplest cases. We might want a full dependence analysis and keep env length minimum in the future. --- src/subtype.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index acfce3649236f..65ee4d5916bce 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2423,24 +2423,47 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, if (obviously_egal(x, y)) return x; + jl_varbinding_t *vars = NULL; + jl_varbinding_t *bbprev = NULL; + jl_varbinding_t *xb = jl_is_typevar(x) ? lookup(e, (jl_tvar_t *)x) : NULL; + jl_varbinding_t *yb = jl_is_typevar(y) ? lookup(e, (jl_tvar_t *)y) : NULL; + int simple_x = !jl_has_free_typevars(!jl_is_typevar(x) ? x : xb ? xb->ub : ((jl_tvar_t *)x)->ub); + int simple_y = !jl_has_free_typevars(!jl_is_typevar(y) ? y : yb ? yb->ub : ((jl_tvar_t *)y)->ub); + if (simple_x && simple_y && !(xb && yb)) { + vars = e->vars; + e->vars = xb ? xb : yb; + if (e->vars != NULL) { + bbprev = e->vars->prev; + e->vars->prev = NULL; + } + } jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); int savedepth = e->invdepth; e->invdepth = depth; jl_value_t *res = intersect_all(x, y, e); e->invdepth = savedepth; pop_unionstate(&e->Runions, &oldRunions); + if (bbprev) e->vars->prev = bbprev; + if (vars) e->vars = vars; return res; } static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { - if (param == 2 || (!jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u))) { + int no_free = !jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u); + if (param == 2 || no_free) { jl_value_t *a=NULL, *b=NULL; JL_GC_PUSH2(&a, &b); + jl_varbinding_t *vars = NULL; + if (no_free) { + vars = e->vars; + e->vars = NULL; + } jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); a = R ? intersect_all(x, u->a, e) : intersect_all(u->a, x, e); b = R ? intersect_all(x, u->b, e) : intersect_all(u->b, x, e); pop_unionstate(&e->Runions, &oldRunions); + if (vars) e->vars = vars; jl_value_t *i = simple_join(a,b); JL_GC_POP(); return i; From 636a35d83ca16d2077fc507701f41d50f409c7a5 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 4 Oct 2024 09:57:53 -0400 Subject: [PATCH 307/548] `@time` actually fix time report commas & add tests (#55982) https://github.com/JuliaLang/julia/pull/55977 looked simple but wasn't quite right because of a bad pattern in the lock conflicts report section. So fix and add tests. --- base/timing.jl | 7 +++++-- test/misc.jl | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 9686c5b33bccd..b094aa230e1c2 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -206,7 +206,7 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl print(io, length(timestr) < 10 ? (" "^(10 - length(timestr))) : "") end print(io, timestr, " seconds") - parens = bytes != 0 || allocs != 0 || gctime > 0 || compile_time > 0 || lock_conflicts > 0 + parens = bytes != 0 || allocs != 0 || gctime > 0 || lock_conflicts > 0 || compile_time > 0 parens && print(io, " (") if bytes != 0 || allocs != 0 allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) @@ -224,8 +224,11 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl print(io, Ryu.writefixed(Float64(100*gctime/elapsedtime), 2), "% gc time") end if lock_conflicts > 0 + if bytes != 0 || allocs != 0 || gctime > 0 + print(io, ", ") + end plural = lock_conflicts == 1 ? "" : "s" - print(io, ", ", lock_conflicts, " lock conflict$plural") + print(io, lock_conflicts, " lock conflict$plural") end if compile_time > 0 if bytes != 0 || allocs != 0 || gctime > 0 || lock_conflicts > 0 diff --git a/test/misc.jl b/test/misc.jl index 66b70956935cd..e089395ce4557 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -360,6 +360,15 @@ let foo() = 1 @test @timev foo() true end +# this is internal, but used for easy testing +@test sprint(Base.time_print, 1e9) == " 1.000000 seconds" +@test sprint(Base.time_print, 1e9, 111, 0, 222) == " 1.000000 seconds (222 allocations: 111 bytes)" +@test sprint(Base.time_print, 1e9, 111, 0.5e9, 222) == " 1.000000 seconds (222 allocations: 111 bytes, 50.00% gc time)" +@test sprint(Base.time_print, 1e9, 111, 0, 222, 333) == " 1.000000 seconds (222 allocations: 111 bytes, 333 lock conflicts)" +@test sprint(Base.time_print, 1e9, 0, 0, 0, 333) == " 1.000000 seconds (333 lock conflicts)" +@test sprint(Base.time_print, 1e9, 111, 0, 222, 333, 0.25e9) == " 1.000000 seconds (222 allocations: 111 bytes, 333 lock conflicts, 25.00% compilation time)" +@test sprint(Base.time_print, 1e9, 111, 0.5e9, 222, 333, 0.25e9, 0.175e9) == " 1.000000 seconds (222 allocations: 111 bytes, 50.00% gc time, 333 lock conflicts, 25.00% compilation time: 70% of which was recompilation)" + # @showtime @test @showtime true let foo() = true From 80d67d579f89d264939220d09377f55fc7bfbcb2 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 5 Oct 2024 00:59:16 +0900 Subject: [PATCH 308/548] adjust EA to JuliaLang/julia#52527 (#55986) `EnterNode.catch_dest` can now be `0` after the `try`/`catch` elision feature implemented in JuliaLang/julia#52527, and we actually need to adjust `EscapeAnalysis.compute_frameinfo` too. --- .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 10 ++++++---- test/compiler/EscapeAnalysis/EscapeAnalysis.jl | 17 +++++++++++++++++ test/compiler/irpasses.jl | 7 ++----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index 6967efe495be1..0ad55d6fbcd9e 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -732,11 +732,13 @@ function compute_frameinfo(ir::IRCode) inst = ir[SSAValue(idx)] stmt = inst[:stmt] if isa(stmt, EnterNode) - @assert idx ≤ nstmts "try/catch inside new_nodes unsupported" - tryregions === nothing && (tryregions = UnitRange{Int}[]) leave_block = stmt.catch_dest - leave_pc = first(ir.cfg.blocks[leave_block].stmts) - push!(tryregions, idx:leave_pc) + if leave_block ≠ 0 + @assert idx ≤ nstmts "try/catch inside new_nodes unsupported" + tryregions === nothing && (tryregions = UnitRange{Int}[]) + leave_pc = first(ir.cfg.blocks[leave_block].stmts) + push!(tryregions, idx:leave_pc) + end elseif arrayinfo !== nothing # TODO this super limited alias analysis is able to handle only very simple cases # this should be replaced with a proper forward dimension analysis diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index 8c3e065818208..99bd86228f50a 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -2299,4 +2299,21 @@ let result = code_escapes((SafeRef{String},Any)) do x, y @test has_all_escape(result.state[Argument(3)]) # y end +@eval function scope_folding() + $(Expr(:tryfinally, + Expr(:block, + Expr(:tryfinally, :(), :(), 2), + :(return Core.current_scope())), + :(), 1)) +end +@eval function scope_folding_opt() + $(Expr(:tryfinally, + Expr(:block, + Expr(:tryfinally, :(), :(), :(Base.inferencebarrier(2))), + :(return Core.current_scope())), + :(), :(Base.inferencebarrier(1)))) +end +@test (@code_escapes scope_folding()) isa EAUtils.EscapeResult +@test (@code_escapes scope_folding_opt()) isa EAUtils.EscapeResult + end # module test_EA diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 281317ac25bf8..740ac5f4958e4 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -576,7 +576,6 @@ let # lifting `isa` through Core.ifelse @test count(iscall((src, isa)), src.code) == 0 end - let # lifting `isdefined` through PhiNode src = code_typed1((Bool,Some{Int},)) do c, x y = c ? x : nothing @@ -1035,8 +1034,7 @@ exc39508 = ErrorException("expected") end @test test39508() === exc39508 -let - # `typeassert` elimination after SROA +let # `typeassert` elimination after SROA # NOTE we can remove this optimization once inference is able to reason about memory-effects src = @eval Module() begin mutable struct Foo; x; end @@ -1051,8 +1049,7 @@ let @test count(iscall((src, typeassert)), src.code) == 0 end -let - # Test for https://github.com/JuliaLang/julia/issues/43402 +let # Test for https://github.com/JuliaLang/julia/issues/43402 # Ensure that structs required not used outside of the ccall, # still get listed in the ccall_preserves From a8d7b68072d15d0ec1a19e27a8064253bea912c5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 19 Sep 2024 12:45:52 -0400 Subject: [PATCH 309/548] Improvements to JITLink Seeing what this will look like, since it has a number of features (delayed compilation, concurrent compilation) that are starting to become important, so it would be nice to switch to only supporting one common implementation of memory management. Refs #50248 I am expecting https://github.com/llvm/llvm-project/issues/63236 may cause some problems, since we reconfigured some CI machines to minimize that issue, but it is still likely relevant. --- src/codegen.cpp | 14 +-- src/jitlayers.cpp | 289 +++++++++++++++++++++++++--------------------- src/jitlayers.h | 26 ++--- 3 files changed, 174 insertions(+), 155 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a7a985284c87b..cee88a3d20f3d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10396,7 +10396,7 @@ static void init_jit_functions(void) } #ifdef JL_USE_INTEL_JITEVENTS -char jl_using_intel_jitevents; // Non-zero if running under Intel VTune Amplifier +char jl_using_intel_jitevents = 0; // Non-zero if running under Intel VTune Amplifier #endif #ifdef JL_USE_OPROFILE_JITEVENTS @@ -10510,9 +10510,6 @@ extern "C" void jl_init_llvm(void) #if defined(JL_USE_INTEL_JITEVENTS) || \ defined(JL_USE_OPROFILE_JITEVENTS) || \ defined(JL_USE_PERF_JITEVENTS) -#ifdef JL_USE_JITLINK -#pragma message("JIT profiling support (JL_USE_*_JITEVENTS) not yet available on platforms that use JITLink") -#else const char *jit_profiling = getenv("ENABLE_JITPROFILING"); #if defined(JL_USE_INTEL_JITEVENTS) @@ -10529,24 +10526,23 @@ extern "C" void jl_init_llvm(void) #if defined(JL_USE_PERF_JITEVENTS) if (jit_profiling && atoi(jit_profiling)) { - jl_using_perf_jitevents= 1; + jl_using_perf_jitevents = 1; } #endif #ifdef JL_USE_INTEL_JITEVENTS if (jl_using_intel_jitevents) - jl_ExecutionEngine->RegisterJITEventListener(JITEventListener::createIntelJITEventListener()); + jl_ExecutionEngine->enableIntelJITEventListener(); #endif #ifdef JL_USE_OPROFILE_JITEVENTS if (jl_using_oprofile_jitevents) - jl_ExecutionEngine->RegisterJITEventListener(JITEventListener::createOProfileJITEventListener()); + jl_ExecutionEngine->enableOProfileJITEventListener(); #endif #ifdef JL_USE_PERF_JITEVENTS if (jl_using_perf_jitevents) - jl_ExecutionEngine->RegisterJITEventListener(JITEventListener::createPerfJITEventListener()); -#endif + jl_ExecutionEngine->enablePerfJITEventListener(); #endif #endif diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 442103c91be0f..7489b086105e6 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -14,6 +14,15 @@ #include #include #include +#if JL_LLVM_VERSION >= 180000 +#include +#include +#include +#endif +#if JL_LLVM_VERSION >= 190000 +#include +#include +#endif #include #include #include @@ -142,13 +151,14 @@ void jl_dump_llvm_opt_impl(void *s) **jl_ExecutionEngine->get_dump_llvm_opt_stream() = (ios_t*)s; } +#ifndef JL_USE_JITLINK static int jl_add_to_ee( orc::ThreadSafeModule &M, const StringMap &NewExports, DenseMap &Queued, SmallVectorImpl &Stack) JL_NOTSAFEPOINT; +#endif static void jl_decorate_module(Module &M) JL_NOTSAFEPOINT; -static uint64_t getAddressForFunction(StringRef fname) JL_NOTSAFEPOINT; void jl_link_global(GlobalVariable *GV, void *addr) JL_NOTSAFEPOINT { @@ -177,23 +187,6 @@ void jl_jit_globals(std::map &globals) JL_NOTSAFEPOINT } } -// used for image_codegen, where we keep all the gvs external -// so we can't jit them directly into each module -static orc::ThreadSafeModule jl_get_globals_module(orc::ThreadSafeContext &ctx, const DataLayout &DL, const Triple &T, std::map &globals) JL_NOTSAFEPOINT -{ - auto lock = ctx.getLock(); - auto GTSM = jl_create_ts_module("globals", ctx, DL, T); - auto GM = GTSM.getModuleUnlocked(); - for (auto &global : globals) { - auto GV = global.second; - auto GV2 = new GlobalVariable(*GM, GV->getValueType(), GV->isConstant(), GlobalValue::ExternalLinkage, literal_static_pointer_val(global.first, GV->getValueType()), GV->getName(), nullptr, GV->getThreadLocalMode(), GV->getAddressSpace(), false); - GV2->copyAttributesFrom(GV); - GV2->setDSOLocal(true); - GV2->setAlignment(GV->getAlign()); - } - return GTSM; -} - // this generates llvm code for the lambda info // and adds the result to the jitlayers // (and the shadow module), @@ -238,8 +231,21 @@ static jl_callptr_t _jl_compile_codeinst( // to ensure that the globals are defined when they are compiled. if (params.imaging_mode) { // Won't contain any PLT/dlsym calls, so no need to optimize those - jl_ExecutionEngine->addModule(jl_get_globals_module(params.tsctx, params.DL, params.TargetTriple, params.global_targets)); - } else { + if (!params.global_targets.empty()) { + void **globalslots = new void*[params.global_targets.size()]; + void **slot = globalslots; + for (auto &global : params.global_targets) { + auto GV = global.second; + *slot = global.first; + jl_ExecutionEngine->addGlobalMapping(GV->getName(), (uintptr_t)slot); + slot++; + } +#ifdef __clang_analyzer__ + static void **leaker = globalslots; // for the purpose of the analyzer, we need to expressly leak this variable or it thinks we forgot to free it +#endif + } + } + else { StringMap NewGlobals; for (auto &global : params.global_targets) { NewGlobals[global.second->getName()] = global.first; @@ -255,6 +261,7 @@ static jl_callptr_t _jl_compile_codeinst( } } +#ifndef JL_USE_JITLINK // Collect the exported functions from the params.compiled_functions modules, // which form dependencies on which functions need to be // compiled first. Cycles of functions are compiled together. @@ -281,18 +288,40 @@ static jl_callptr_t _jl_compile_codeinst( jl_add_to_ee(M, NewExports, Queued, Stack); assert(Queued.empty() && Stack.empty() && !M); } +#else + for (auto &def : params.compiled_functions) { + // Add the results to the execution engine now + orc::ThreadSafeModule &M = std::get<0>(def.second); + if (M) + jl_ExecutionEngine->addModule(std::move(M)); + } +#endif ++CompiledCodeinsts; MaxWorkqueueSize.updateMax(params.compiled_functions.size()); IndirectCodeinsts += params.compiled_functions.size() - 1; } + // batch compile job for all new functions + SmallVector NewDefs; + for (auto &def : params.compiled_functions) { + jl_llvm_functions_t &decls = std::get<1>(def.second); + if (decls.functionObject != "jl_fptr_args" && + decls.functionObject != "jl_fptr_sparam" && + decls.functionObject != "jl_f_opaque_closure_call") + NewDefs.push_back(decls.functionObject); + if (!decls.specFunctionObject.empty()) + NewDefs.push_back(decls.specFunctionObject); + } + auto Addrs = jl_ExecutionEngine->findSymbols(NewDefs); + size_t i = 0; + size_t nextaddr = 0; for (auto &def : params.compiled_functions) { jl_code_instance_t *this_code = def.first; if (i < jl_timing_print_limit) jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_DEFAULT_BLOCK); - jl_llvm_functions_t decls = std::get<1>(def.second); + jl_llvm_functions_t &decls = std::get<1>(def.second); jl_callptr_t addr; bool isspecsig = false; if (decls.functionObject == "jl_fptr_args") { @@ -305,12 +334,16 @@ static jl_callptr_t _jl_compile_codeinst( addr = jl_f_opaque_closure_call_addr; } else { - addr = (jl_callptr_t)getAddressForFunction(decls.functionObject); + assert(NewDefs[nextaddr] == decls.functionObject); + addr = (jl_callptr_t)Addrs[nextaddr++]; + assert(addr); isspecsig = true; } if (!decls.specFunctionObject.empty()) { void *prev_specptr = NULL; - auto spec = (void*)getAddressForFunction(decls.specFunctionObject); + assert(NewDefs[nextaddr] == decls.specFunctionObject); + void *spec = (void*)Addrs[nextaddr++]; + assert(spec); if (jl_atomic_cmpswap_acqrel(&this_code->specptr.fptr, &prev_specptr, spec)) { // only set specsig and invoke if we were the first to set specptr jl_atomic_store_relaxed(&this_code->specsigflags, (uint8_t) isspecsig); @@ -601,48 +634,6 @@ static auto countBasicBlocks(const Function &F) JL_NOTSAFEPOINT static constexpr size_t N_optlevels = 4; -static Expected validateExternRelocations(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { -#if !defined(JL_NDEBUG) && !defined(JL_USE_JITLINK) - auto isIntrinsicFunction = [](GlobalObject &GO) JL_NOTSAFEPOINT { - auto F = dyn_cast(&GO); - if (!F) - return false; - return F->isIntrinsic() || F->getName().starts_with("julia."); - }; - // validate the relocations for M (only for RuntimeDyld, JITLink performs its own symbol validation) - auto Err = TSM.withModuleDo([isIntrinsicFunction](Module &M) JL_NOTSAFEPOINT { - Error Err = Error::success(); - for (auto &GO : make_early_inc_range(M.global_objects())) { - if (!GO.isDeclarationForLinker()) - continue; - if (GO.use_empty()) { - GO.eraseFromParent(); - continue; - } - if (isIntrinsicFunction(GO)) - continue; - auto sym = jl_ExecutionEngine->findUnmangledSymbol(GO.getName()); - if (sym) - continue; - // TODO have we ever run into this check? It's been guaranteed to not - // fire in an assert build, since previously LLVM would abort due to - // not handling the error if we didn't find the unmangled symbol - if (SectionMemoryManager::getSymbolAddressInProcess( - jl_ExecutionEngine->getMangledName(GO.getName()))) { - consumeError(sym.takeError()); - continue; - } - Err = joinErrors(std::move(Err), sym.takeError()); - } - return Err; - }); - if (Err) { - return std::move(Err); - } -#endif - return std::move(TSM); -} - static Expected selectOptLevel(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) { TSM.withModuleDo([](Module &M) { size_t opt_level = std::max(static_cast(jl_options.opt_level), 0); @@ -673,18 +664,6 @@ static Expected selectOptLevel(orc::ThreadSafeModule TSM, return std::move(TSM); } -static void recordDebugTSM(orc::MaterializationResponsibility &, orc::ThreadSafeModule TSM) JL_NOTSAFEPOINT { - auto ptr = TSM.withModuleDo([](Module &M) JL_NOTSAFEPOINT { - auto md = M.getModuleFlag("julia.__jit_debug_tsm_addr"); - if (!md) - return static_cast(nullptr); - return reinterpret_cast(cast(cast(md)->getValue())->getZExtValue()); - }); - if (ptr) { - *ptr = std::move(TSM); - } -} - void jl_register_jit_object(const object::ObjectFile &debugObj, std::function getLoadAddress, std::function lookupWriteAddress); @@ -1584,6 +1563,7 @@ JuliaOJIT::JuliaOJIT() #ifdef JL_USE_JITLINK MemMgr(createJITLinkMemoryManager()), ObjectLayer(ES, *MemMgr), + CompileLayer(ES, ObjectLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), #else MemMgr(createRTDyldMemoryManager()), ObjectLayer( @@ -1593,15 +1573,12 @@ JuliaOJIT::JuliaOJIT() return result; } ), -#endif LockLayer(ObjectLayer), CompileLayer(ES, LockLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), +#endif JITPointersLayer(ES, CompileLayer, orc::IRTransformLayer::TransformFunction(JITPointersT(SharedBytes, RLST_mutex))), OptimizeLayer(ES, JITPointersLayer, orc::IRTransformLayer::TransformFunction(OptimizerT(*TM, PrintLLVMTimers, llvm_printing_mutex))), - OptSelLayer(ES, OptimizeLayer, orc::IRTransformLayer::TransformFunction(selectOptLevel)), - DepsVerifyLayer(ES, OptSelLayer, orc::IRTransformLayer::TransformFunction(validateExternRelocations)), - ExternalCompileLayer(ES, LockLayer, - std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)) + OptSelLayer(ES, OptimizeLayer, orc::IRTransformLayer::TransformFunction(selectOptLevel)) { JL_MUTEX_INIT(&this->jitlock, "JuliaOJIT"); #ifdef JL_USE_JITLINK @@ -1625,7 +1602,6 @@ JuliaOJIT::JuliaOJIT() registerRTDyldJITObject(Object, LO, MemMgr); }); #endif - CompileLayer.setNotifyCompiled(recordDebugTSM); std::string ErrorStr; @@ -1786,8 +1762,8 @@ void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) { JL_TIMING(LLVM_JIT, JIT_Total); ++ModulesAdded; +#ifndef JL_USE_JITLINK orc::SymbolLookupSet NewExports; - orc::ThreadSafeModule CurrentlyCompiling; TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { for (auto &F : M.global_values()) { if (!F.isDeclaration() && F.getLinkage() == GlobalValue::ExternalLinkage) { @@ -1796,42 +1772,24 @@ void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) } } assert(!verifyLLVMIR(M)); - auto jit_debug_tsm_addr = ConstantInt::get(Type::getIntNTy(M.getContext(), sizeof(void*) * CHAR_BIT), (uintptr_t) &CurrentlyCompiling); - M.addModuleFlag(Module::Error, "julia.__jit_debug_tsm_addr", jit_debug_tsm_addr); }); +#endif - // TODO: what is the performance characteristics of this? - auto Err = DepsVerifyLayer.add(JD, std::move(TSM)); + auto Err = OptSelLayer.add(JD, std::move(TSM)); if (Err) { ES.reportError(std::move(Err)); errs() << "Failed to add module to JIT!\n"; - if (CurrentlyCompiling) { - CurrentlyCompiling.withModuleDo([](Module &M) JL_NOTSAFEPOINT { errs() << "Dumping failing module\n" << M << "\n"; }); - } else { - errs() << "Module unavailable to be printed\n"; - } abort(); } +#ifndef JL_USE_JITLINK // force eager compilation (for now), due to memory management specifics // (can't handle compilation recursion) auto Lookups = ES.lookup({{&JD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly}}, NewExports); if (!Lookups) { ES.reportError(Lookups.takeError()); errs() << "Failed to lookup symbols in module!\n"; - if (CurrentlyCompiling) { - CurrentlyCompiling.withModuleDo([](Module &M) JL_NOTSAFEPOINT { errs() << "Dumping failing module\n" << M << "\n"; }); - } else { - errs() << "Module unavailable to be printed\n"; - } - } - for (auto &Sym : *Lookups) { - #if JL_LLVM_VERSION >= 170000 - assert(Sym.second.getAddress()); - #else - assert(Sym.second); - #endif - (void) Sym; } +#endif } Error JuliaOJIT::addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, bool ShouldOptimize) @@ -1850,12 +1808,33 @@ Error JuliaOJIT::addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, return Error::success(); })) return Err; - return ExternalCompileLayer.add(JD.getDefaultResourceTracker(), std::move(TSM)); + return CompileLayer.add(JD.getDefaultResourceTracker(), std::move(TSM)); } Error JuliaOJIT::addObjectFile(orc::JITDylib &JD, std::unique_ptr Obj) { assert(Obj && "Can not add null object"); +#ifdef JL_USE_JITLINK + return ObjectLayer.add(JD.getDefaultResourceTracker(), std::move(Obj)); +#else return LockLayer.add(JD.getDefaultResourceTracker(), std::move(Obj)); +#endif +} + +SmallVector JuliaOJIT::findSymbols(ArrayRef Names) +{ + DenseMap Unmangled; + orc::SymbolLookupSet Exports; + for (StringRef Name : Names) { + auto Mangled = ES.intern(getMangledName(Name)); + Unmangled[NonOwningSymbolStringPtr(Mangled)] = Unmangled.size(); + Exports.add(std::move(Mangled)); + } + SymbolMap Syms = cantFail(ES.lookup(orc::makeJITDylibSearchOrder(ArrayRef(&JD)), std::move(Exports))); + SmallVector Addrs(Names.size()); + for (auto it : Syms) { + Addrs[Unmangled.at(orc::NonOwningSymbolStringPtr(it.first))] = it.second.getAddress().getValue(); + } + return Addrs; } #if JL_LLVM_VERSION >= 170000 @@ -1887,7 +1866,7 @@ uint64_t JuliaOJIT::getGlobalValueAddress(StringRef Name) consumeError(addr.takeError()); return 0; } - return cantFail(std::move(addr)).getAddress().getValue(); + return addr->getAddress().getValue(); } uint64_t JuliaOJIT::getFunctionAddress(StringRef Name) @@ -1897,7 +1876,7 @@ uint64_t JuliaOJIT::getFunctionAddress(StringRef Name) consumeError(addr.takeError()); return 0; } - return cantFail(std::move(addr)).getAddress().getValue(); + return addr->getAddress().getValue(); } #else JL_JITSymbol JuliaOJIT::findSymbol(StringRef Name, bool ExportedSymbolsOnly) @@ -1973,41 +1952,92 @@ StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_callptr_t invoke, jl return *fname; } - #ifdef JL_USE_JITLINK -extern "C" orc::shared::CWrapperFunctionResult -llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size); +#if JL_LLVM_VERSION >= 170000 +#define addAbsoluteToMap(map,name) \ + (map[mangle(#name)] = {ExecutorAddr::fromPtr(&name), JITSymbolFlags::Exported | JITSymbolFlags::Callable}, orc::ExecutorAddr::fromPtr(&name)) +#else +#define addAbsoluteToMap(map,name) \ + (map[mangle(#name)] = JITEvaluatedSymbol::fromPointer(&name, JITSymbolFlags::Exported | JITSymbolFlags::Callable), orc::ExecutorAddr::fromPtr(&name)) +#endif void JuliaOJIT::enableJITDebuggingSupport() { orc::SymbolMap GDBFunctions; - #if JL_LLVM_VERSION >= 170000 - GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBAllocAction")] = {ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBAllocAction), JITSymbolFlags::Exported | JITSymbolFlags::Callable}; - GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBWrapper")] = {ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBWrapper), JITSymbolFlags::Exported | JITSymbolFlags::Callable}; - #else - GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBAllocAction")] = JITEvaluatedSymbol::fromPointer(&llvm_orc_registerJITLoaderGDBAllocAction, JITSymbolFlags::Exported | JITSymbolFlags::Callable); - GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBWrapper")] = JITEvaluatedSymbol::fromPointer(&llvm_orc_registerJITLoaderGDBWrapper, JITSymbolFlags::Exported | JITSymbolFlags::Callable); - #endif + addAbsoluteToMap(GDBFunctions,llvm_orc_registerJITLoaderGDBAllocAction); + auto registerJITLoaderGDBWrapper = addAbsoluteToMap(GDBFunctions,llvm_orc_registerJITLoaderGDBWrapper); cantFail(JD.define(orc::absoluteSymbols(GDBFunctions))); if (TM->getTargetTriple().isOSBinFormatMachO()) ObjectLayer.addPlugin(cantFail(orc::GDBJITDebugInfoRegistrationPlugin::Create(ES, JD, TM->getTargetTriple()))); #ifndef _COMPILER_ASAN_ENABLED_ // TODO: Fix duplicated sections spam #51794 else if (TM->getTargetTriple().isOSBinFormatELF()) //EPCDebugObjectRegistrar doesn't take a JITDylib, so we have to directly provide the call address - ObjectLayer.addPlugin(std::make_unique(ES, std::make_unique(ES, orc::ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBWrapper)))); + ObjectLayer.addPlugin(std::make_unique(ES, std::make_unique(ES, registerJITLoaderGDBWrapper))); +#endif +} + +void JuliaOJIT::enableIntelJITEventListener() +{ +#if JL_LLVM_VERSION >= 190000 + if (TT.isOSBinFormatELF()) { + orc::SymbolMap VTuneFunctions; + auto RegisterImplAddr = addAbsoluteToMap(VTuneFunctions,llvm_orc_registerVTuneImpl); + auto UnregisterImplAddr = addAbsoluteToMap(VTuneFunctions,llvm_orc_unregisterVTuneImpl); + ObjectLayer.addPlugin(cantFail(DebugInfoPreservationPlugin::Create())); + //ObjectLayer.addPlugin(cantFail(VTuneSupportPlugin::Create(ES.getExecutorProcessControl(), + // JD, /*EmitDebugInfo=*/true, + // /*TestMode=*/false))); + bool EmitDebugInfo = true; + ObjectLayer.addPlugin(std::make_unique( + ES.getExecutorProcessControl(), RegisterImplAddr, UnregisterImplAddr, EmitDebugInfo)); + } +#endif +} + +void JuliaOJIT::enableOProfileJITEventListener() +{ + // implement when available in LLVM +} + +void JuliaOJIT::enablePerfJITEventListener() +{ +#if JL_LLVM_VERSION >= 180000 + orc::SymbolMap PerfFunctions; + auto StartAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfStart); + auto EndAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfEnd); + auto ImplAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfImpl); + cantFail(JD.define(orc::absoluteSymbols(PerfFunctions))); + if (TM->getTargetTriple().isOSBinFormatELF()) { + ObjectLayer.addPlugin(cantFail(DebugInfoPreservationPlugin::Create())); + //ObjectLayer.addPlugin(cantFail(PerfSupportPlugin::Create( + // ES.getExecutorProcessControl(), *JD, true, true))); + bool EmitDebugInfo = true, EmitUnwindInfo = true; + ObjectLayer.addPlugin(std::make_unique( + ES.getExecutorProcessControl(), StartAddr, EndAddr, ImplAddr, EmitDebugInfo, EmitUnwindInfo)); + } #endif } #else +void JuliaOJIT::RegisterJITEventListener(JITEventListener *L) +{ + if (L) + ObjectLayer.registerJITEventListener(*L); +} void JuliaOJIT::enableJITDebuggingSupport() { RegisterJITEventListener(JITEventListener::createGDBRegistrationListener()); } - -void JuliaOJIT::RegisterJITEventListener(JITEventListener *L) +void JuliaOJIT::enableIntelJITEventListener() { - if (!L) - return; - this->ObjectLayer.registerJITEventListener(*L); + RegisterJITEventListener(JITEventListener::createIntelJITEventListener()); +} +void JuliaOJIT::enableOProfileJITEventListener() +{ + RegisterJITEventListener(JITEventListener::createOProfileJITEventListener()); +} +void JuliaOJIT::enablePerfJITEventListener() +{ + RegisterJITEventListener(JITEventListener::createPerfJITEventListener()); } #endif @@ -2251,6 +2281,7 @@ static void jl_decorate_module(Module &M) { } } +#ifndef JL_USE_JITLINK // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable static int jl_add_to_ee( orc::ThreadSafeModule &M, @@ -2316,13 +2347,7 @@ static int jl_add_to_ee( jl_ExecutionEngine->addModule(std::move(M)); return 0; } - -static uint64_t getAddressForFunction(StringRef fname) -{ - auto addr = jl_ExecutionEngine->getFunctionAddress(fname); - assert(addr); - return addr; -} +#endif // helper function for adding a DLLImport (dlsym) address to the execution engine void add_named_global(StringRef name, void *addr) diff --git a/src/jitlayers.h b/src/jitlayers.h index 93669c2351d88..3353a4093bd27 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -47,7 +47,7 @@ // and feature support (e.g. Windows, JITEventListeners for various profilers, // etc.). Thus, we currently only use JITLink where absolutely required, that is, // for Mac/aarch64 and Linux/aarch64. -// #define JL_FORCE_JITLINK +//#define JL_FORCE_JITLINK #if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_) # define HAS_SANITIZER @@ -363,7 +363,6 @@ class JuliaOJIT { typedef orc::ObjectLinkingLayer ObjLayerT; #else typedef orc::RTDyldObjectLinkingLayer ObjLayerT; -#endif struct LockLayerT : public orc::ObjectLayer { LockLayerT(orc::ObjectLayer &BaseLayer) JL_NOTSAFEPOINT : orc::ObjectLayer(BaseLayer.getExecutionSession()), BaseLayer(BaseLayer) {} @@ -381,11 +380,11 @@ class JuliaOJIT { orc::ObjectLayer &BaseLayer; std::mutex EmissionMutex; }; +#endif typedef orc::IRCompileLayer CompileLayerT; typedef orc::IRTransformLayer JITPointersLayerT; typedef orc::IRTransformLayer OptimizeLayerT; typedef orc::IRTransformLayer OptSelLayerT; - typedef orc::IRTransformLayer DepsVerifyLayerT; typedef object::OwningBinary OwningObj; template - void registerObject(const ObjT &Obj, const LoadResult &LO); +#ifndef JL_USE_JITLINK + void RegisterJITEventListener(JITEventListener *L) JL_NOTSAFEPOINT; +#endif public: @@ -511,10 +509,9 @@ class JuliaOJIT { ~JuliaOJIT() JL_NOTSAFEPOINT; void enableJITDebuggingSupport() JL_NOTSAFEPOINT; -#ifndef JL_USE_JITLINK - // JITLink doesn't support old JITEventListeners (yet). - void RegisterJITEventListener(JITEventListener *L) JL_NOTSAFEPOINT; -#endif + void enableIntelJITEventListener() JL_NOTSAFEPOINT; + void enableOProfileJITEventListener() JL_NOTSAFEPOINT; + void enablePerfJITEventListener() JL_NOTSAFEPOINT; orc::SymbolStringPtr mangle(StringRef Name) JL_NOTSAFEPOINT; void addGlobalMapping(StringRef Name, uint64_t Addr) JL_NOTSAFEPOINT; @@ -525,7 +522,7 @@ class JuliaOJIT { bool ShouldOptimize = false) JL_NOTSAFEPOINT; Error addObjectFile(orc::JITDylib &JD, std::unique_ptr Obj) JL_NOTSAFEPOINT; - orc::IRCompileLayer &getIRCompileLayer() JL_NOTSAFEPOINT { return ExternalCompileLayer; }; + orc::IRCompileLayer &getIRCompileLayer() JL_NOTSAFEPOINT { return CompileLayer; }; orc::ExecutionSession &getExecutionSession() JL_NOTSAFEPOINT { return ES; } orc::JITDylib &getExternalJITDylib() JL_NOTSAFEPOINT { return ExternalJD; } @@ -533,6 +530,7 @@ class JuliaOJIT { Expected findSymbol(StringRef Name, bool ExportedSymbolsOnly) JL_NOTSAFEPOINT; Expected findUnmangledSymbol(StringRef Name) JL_NOTSAFEPOINT; Expected findExternalJDSymbol(StringRef Name, bool ExternalJDOnly) JL_NOTSAFEPOINT; + SmallVector findSymbols(ArrayRef Names) JL_NOTSAFEPOINT; #else JITEvaluatedSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) JL_NOTSAFEPOINT; JITEvaluatedSymbol findUnmangledSymbol(StringRef Name) JL_NOTSAFEPOINT; @@ -616,13 +614,13 @@ class JuliaOJIT { const std::unique_ptr MemMgr; #endif ObjLayerT ObjectLayer; +#ifndef JL_USE_JITLINK LockLayerT LockLayer; +#endif CompileLayerT CompileLayer; JITPointersLayerT JITPointersLayer; OptimizeLayerT OptimizeLayer; OptSelLayerT OptSelLayer; - DepsVerifyLayerT DepsVerifyLayer; - CompileLayerT ExternalCompileLayer; }; extern JuliaOJIT *jl_ExecutionEngine; std::unique_ptr jl_create_llvm_module(StringRef name, LLVMContext &ctx, const DataLayout &DL = jl_ExecutionEngine->getDataLayout(), const Triple &triple = jl_ExecutionEngine->getTargetTriple()) JL_NOTSAFEPOINT; From 218edeaa80c7d70609e2029f81287137d7b201dd Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 4 Oct 2024 14:38:47 +0000 Subject: [PATCH 310/548] rewrite catchjmp asm to use normal relocations instead of manual editing --- src/cgmemmgr.cpp | 29 -------------- src/codegen.cpp | 1 + src/debug-registry.h | 3 +- src/debuginfo.cpp | 47 ++++++++-------------- src/jitlayers.cpp | 95 +++++++++++++++++++++++++++----------------- 5 files changed, 78 insertions(+), 97 deletions(-) diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index c78e6092ca5db..8557698a4e513 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -833,28 +833,6 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { mapAddresses(Dyld, ro_alloc); mapAddresses(Dyld, exe_alloc); } -#ifdef _OS_WINDOWS_ - template - void *lookupWriteAddressFor(void *rt_addr, Alloc &&allocator) - { - for (auto &alloc: allocator->allocations) { - if (alloc.rt_addr == rt_addr) { - return alloc.wr_addr; - } - } - return nullptr; - } - void *lookupWriteAddressFor(void *rt_addr) - { - if (!ro_alloc) - return rt_addr; - if (void *ptr = lookupWriteAddressFor(rt_addr, ro_alloc)) - return ptr; - if (void *ptr = lookupWriteAddressFor(rt_addr, exe_alloc)) - return ptr; - return rt_addr; - } -#endif // _OS_WINDOWS_ }; uint8_t *RTDyldMemoryManagerJL::allocateCodeSection(uintptr_t Size, @@ -947,13 +925,6 @@ void RTDyldMemoryManagerJL::deregisterEHFrames(uint8_t *Addr, } -#ifdef _OS_WINDOWS_ -void *lookupWriteAddressFor(RTDyldMemoryManager *memmgr, void *rt_addr) -{ - return ((RTDyldMemoryManagerJL*)memmgr)->lookupWriteAddressFor(rt_addr); -} -#endif - RTDyldMemoryManager* createRTDyldMemoryManager() { return new RTDyldMemoryManagerJL(); diff --git a/src/codegen.cpp b/src/codegen.cpp index cee88a3d20f3d..bcda527416676 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10371,6 +10371,7 @@ static void init_jit_functions(void) #ifdef _OS_WINDOWS_ #if defined(_CPU_X86_64_) + add_named_global("__julia_personality", &__julia_personality); #if defined(_COMPILER_GCC_) add_named_global("___chkstk_ms", &___chkstk_ms); #else diff --git a/src/debug-registry.h b/src/debug-registry.h index 85a94245ce6aa..4c9e13d8cd72d 100644 --- a/src/debug-registry.h +++ b/src/debug-registry.h @@ -145,8 +145,7 @@ class JITDebugInfoRegistry void add_code_in_flight(llvm::StringRef name, jl_code_instance_t *codeinst, const llvm::DataLayout &DL) JL_NOTSAFEPOINT; jl_method_instance_t *lookupLinfo(size_t pointer) JL_NOTSAFEPOINT; void registerJITObject(const llvm::object::ObjectFile &Object, - std::function getLoadAddress, - std::function lookupWriteAddress); + std::function getLoadAddress); objectmap_t& getObjectMap() JL_NOTSAFEPOINT; void add_image_info(image_info_t info) JL_NOTSAFEPOINT; bool get_image_info(uint64_t base, image_info_t *info) const JL_NOTSAFEPOINT; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 84550811072fe..cfaf8d4c70ee9 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -223,11 +223,21 @@ static void create_PRUNTIME_FUNCTION(uint8_t *Code, size_t Size, StringRef fnnam #endif void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, - std::function getLoadAddress, - std::function lookupWriteAddress) + std::function getLoadAddress) { object::section_iterator EndSection = Object.section_end(); + bool anyfunctions = false; + for (const object::SymbolRef &sym_iter : Object.symbols()) { + object::SymbolRef::Type SymbolType = cantFail(sym_iter.getType()); + if (SymbolType != object::SymbolRef::ST_Function) + continue; + anyfunctions = true; + break; + } + if (!anyfunctions) + return; + #ifdef _CPU_ARM_ // ARM does not have/use .eh_frame uint64_t arm_exidx_addr = 0; @@ -281,14 +291,13 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, #if defined(_OS_WINDOWS_) uint64_t SectionAddrCheck = 0; uint64_t SectionLoadCheck = 0; (void)SectionLoadCheck; - uint64_t SectionWriteCheck = 0; (void)SectionWriteCheck; uint8_t *UnwindData = NULL; #if defined(_CPU_X86_64_) uint8_t *catchjmp = NULL; for (const object::SymbolRef &sym_iter : Object.symbols()) { StringRef sName = cantFail(sym_iter.getName()); if (sName.equals("__UnwindData") || sName.equals("__catchjmp")) { - uint64_t Addr = cantFail(sym_iter.getAddress()); + uint64_t Addr = cantFail(sym_iter.getAddress()); // offset into object (including section offset) auto Section = cantFail(sym_iter.getSection()); assert(Section != EndSection && Section->isText()); uint64_t SectionAddr = Section->getAddress(); @@ -300,10 +309,7 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, SectionLoadCheck == SectionLoadAddr); SectionAddrCheck = SectionAddr; SectionLoadCheck = SectionLoadAddr; - SectionWriteCheck = SectionLoadAddr; - if (lookupWriteAddress) - SectionWriteCheck = (uintptr_t)lookupWriteAddress((void*)SectionLoadAddr); - Addr += SectionWriteCheck - SectionLoadCheck; + Addr += SectionLoadAddr - SectionAddr; if (sName.equals("__UnwindData")) { UnwindData = (uint8_t*)Addr; } @@ -314,25 +320,7 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, } assert(catchjmp); assert(UnwindData); - assert(SectionAddrCheck); assert(SectionLoadCheck); - assert(!memcmp(catchjmp, "\0\0\0\0\0\0\0\0\0\0\0\0", 12) && - !memcmp(UnwindData, "\0\0\0\0\0\0\0\0\0\0\0\0", 12)); - catchjmp[0] = 0x48; - catchjmp[1] = 0xb8; // mov RAX, QWORD PTR [&__julia_personality] - *(uint64_t*)(&catchjmp[2]) = (uint64_t)&__julia_personality; - catchjmp[10] = 0xff; - catchjmp[11] = 0xe0; // jmp RAX - UnwindData[0] = 0x09; // version info, UNW_FLAG_EHANDLER - UnwindData[1] = 4; // size of prolog (bytes) - UnwindData[2] = 2; // count of unwind codes (slots) - UnwindData[3] = 0x05; // frame register (rbp) = rsp - UnwindData[4] = 4; // second instruction - UnwindData[5] = 0x03; // mov RBP, RSP - UnwindData[6] = 1; // first instruction - UnwindData[7] = 0x50; // push RBP - *(DWORD*)&UnwindData[8] = (DWORD)(catchjmp - (uint8_t*)SectionWriteCheck); // relative location of catchjmp - UnwindData -= SectionWriteCheck - SectionLoadCheck; #endif // defined(_OS_X86_64_) #endif // defined(_OS_WINDOWS_) @@ -353,7 +341,7 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, uint64_t SectionAddr = Section->getAddress(); StringRef secName = cantFail(Section->getName()); uint64_t SectionLoadAddr = getLoadAddress(secName); - Addr -= SectionAddr - SectionLoadAddr; + Addr += SectionLoadAddr - SectionAddr; StringRef sName = cantFail(sym_iter.getName()); uint64_t SectionSize = Section->getSize(); size_t Size = sym_size.second; @@ -404,10 +392,9 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, } void jl_register_jit_object(const object::ObjectFile &Object, - std::function getLoadAddress, - std::function lookupWriteAddress) + std::function getLoadAddress) { - getJITDebugRegistry().registerJITObject(Object, getLoadAddress, lookupWriteAddress); + getJITDebugRegistry().registerJITObject(Object, getLoadAddress); } // TODO: convert the safe names from aotcomile.cpp:makeSafeName back into symbols diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 7489b086105e6..d1757cadee05c 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -665,8 +665,7 @@ static Expected selectOptLevel(orc::ThreadSafeModule TSM, } void jl_register_jit_object(const object::ObjectFile &debugObj, - std::function getLoadAddress, - std::function lookupWriteAddress); + std::function getLoadAddress); namespace { @@ -726,7 +725,7 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { return result->second; }; - jl_register_jit_object(*NewInfo->Object, getLoadAddress, nullptr); + jl_register_jit_object(*NewInfo->Object, getLoadAddress); PendingObjs.erase(&MR); } @@ -957,10 +956,6 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { }; -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) -void *lookupWriteAddressFor(RTDyldMemoryManager *MemMgr, void *rt_addr); -#endif - void registerRTDyldJITObject(const object::ObjectFile &Object, const RuntimeDyld::LoadedObjectInfo &L, const std::shared_ptr &MemMgr) @@ -983,14 +978,7 @@ void registerRTDyldJITObject(const object::ObjectFile &Object, }; auto DebugObject = L.getObjectForDebug(Object); // ELF requires us to make a copy to mutate the header with the section load addresses. On other platforms this is a no-op. - jl_register_jit_object(DebugObject.getBinary() ? *DebugObject.getBinary() : Object, - getLoadAddress, -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - [MemMgr](void *p) { return lookupWriteAddressFor(MemMgr.get(), p); } -#else - nullptr -#endif - ); + jl_register_jit_object(DebugObject.getBinary() ? *DebugObject.getBinary() : Object, getLoadAddress); } namespace { static std::unique_ptr createTargetMachine() JL_NOTSAFEPOINT { @@ -1281,8 +1269,9 @@ namespace { } // Windows needs some inline asm to help - // build unwind tables - jl_decorate_module(M); + // build unwind tables, if they have any functions to decorate + if (!M.functions().empty()) + jl_decorate_module(M); }); return std::move(TSM); } @@ -2255,30 +2244,64 @@ static void jl_decorate_module(Module &M) { if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { // Add special values used by debuginfo to build the UnwindData table registration for Win64 // This used to be GV, but with https://reviews.llvm.org/D100944 we no longer can emit GV into `.text` - // TODO: The data is set in debuginfo.cpp but it should be okay to actually emit it here. - std::string inline_asm = "\ - .section "; - inline_asm += + // and with JITLink it became difficult to change the content afterwards, but we + // would prefer that this simple content wasn't recompiled in every single module, + // so we emit the necessary PLT trampoline as inline assembly. + // This is somewhat duplicated with the .pdata section, but we haven't been able to + // use that yet due to relocation issues. +#define ASM_USES_ELF // use ELF or COFF syntax based on FORCE_ELF + StringRef inline_asm( + ".section" #if JL_LLVM_VERSION >= 180000 - ".ltext,\"ax\",@progbits"; + " .ltext,\"ax\",@progbits\n" #else - ".text"; + " .text\n" #endif - inline_asm += "\n\ - .type __UnwindData,@object \n\ - .p2align 2, 0x90 \n\ - __UnwindData: \n\ - .zero 12 \n\ - .size __UnwindData, 12 \n\ - \n\ - .type __catchjmp,@object \n\ - .p2align 2, 0x90 \n\ - __catchjmp: \n\ - .zero 12 \n\ - .size __catchjmp, 12"; - + ".globl __julia_personality\n" + "\n" +#ifdef ASM_USES_ELF + ".type __UnwindData,@object\n" +#else + ".def __UnwindData\n" + ".scl 2\n" + ".type 0\n" + ".endef\n" +#endif + ".p2align 2, 0x90\n" + "__UnwindData:\n" + " .byte 0x09;\n" // version info, UNW_FLAG_EHANDLER + " .byte 4;\n" // size of prolog (bytes) + " .byte 2;\n" // count of unwind codes (slots) + " .byte 0x05;\n" // frame register (rbp) = rsp + " .byte 4;\n" // second instruction + " .byte 0x03;\n" // mov RBP, RSP + " .byte 1;\n" // first instruction + " .byte 0x50;\n" // push RBP + " .int __catchjmp - " +#if JL_LLVM_VERSION >= 180000 + ".ltext;\n" // Section-relative offset (if using COFF and JITLink, this can be relative to __ImageBase instead, though then we could possibly use pdata/xdata directly then) +#else + ".text;\n" +#endif + ".size __UnwindData, 12\n" + "\n" +#ifdef ASM_USES_ELF + ".type __catchjmp,@function\n" +#else + ".def __catchjmp\n" + ".scl 2\n" + ".type 32\n" + ".endef\n" +#endif + ".p2align 2, 0x90\n" + "__catchjmp:\n" + " movabsq $__julia_personality, %rax\n" + " jmpq *%rax\n" + ".size __catchjmp, . - __catchjmp\n" + "\n"); M.appendModuleInlineAsm(inline_asm); } +#undef ASM_USES_ELF } #ifndef JL_USE_JITLINK From 7e2d8035990e67810ccbbc9fb5da810b1b83b774 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 4 Oct 2024 12:03:53 -0400 Subject: [PATCH 311/548] add logic to prefer loading modules that are already loaded (#55908) Iterate over the list of existing loaded modules for PkgId whenever loading a new module for PkgId, so that we will use that existing build_id content if it otherwise passes the other stale_checks. --- base/Base.jl | 2 +- base/loading.jl | 184 +++++++++++++++++++++++++++--------------------- test/loading.jl | 36 +++++++++- 3 files changed, 138 insertions(+), 84 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 23633f0b5138b..84e10ca788ba2 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -648,7 +648,7 @@ function __init__() empty!(explicit_loaded_modules) empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty for (mod, key) in module_keys - loaded_precompiles[key => module_build_id(mod)] = mod + push!(get!(Vector{Module}, loaded_precompiles, key), mod) end if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES") MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"]) diff --git a/base/loading.jl b/base/loading.jl index 9080a2271fb27..475ce7f50eae7 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1231,7 +1231,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No dep = depmods[i] dep isa Module && continue _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} - dep = loaded_precompiles[depkey => depbuild_id] + dep = something(maybe_loaded_precompile(depkey, depbuild_id)) @assert PkgId(dep) == depkey && module_build_id(dep) === depbuild_id depmods[i] = dep end @@ -1337,6 +1337,7 @@ end function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) # This function is also used by PkgCacheInspector.jl + assert_havelock(require_lock) restored = sv[1]::Vector{Any} for M in restored M = M::Module @@ -1345,7 +1346,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) end if parentmodule(M) === M push!(loaded_modules_order, M) - loaded_precompiles[pkg => module_build_id(M)] = M + push!(get!(Vector{Module}, loaded_precompiles, pkg), M) end end @@ -1945,90 +1946,102 @@ end assert_havelock(require_lock) paths = find_all_in_cache_path(pkg, DEPOT_PATH) newdeps = PkgId[] - for path_to_try in paths::Vector{String} - staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try; reasons, stalecheck) - if staledeps === true - continue - end - try - staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} - # finish checking staledeps module graph - for i in eachindex(staledeps) - dep = staledeps[i] - dep isa Module && continue - modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} - modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) - for modpath_to_try in modpaths - modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try; stalecheck) - if modstaledeps === true - continue - end - modstaledeps, modocachepath, _ = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} - staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath) - @goto check_next_dep + try_build_ids = UInt128[build_id] + if build_id == UInt128(0) + let loaded = get(loaded_precompiles, pkg, nothing) + if loaded !== nothing + for mod in loaded # try these in reverse original load order to see if one is already valid + pushfirst!(try_build_ids, module_build_id(mod)) end - @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." - @goto check_next_path - @label check_next_dep - end - M = get(loaded_precompiles, pkg => newbuild_id, nothing) - if isa(M, Module) - stalecheck && register_root_module(M) - return M end - if stalecheck - try - touch(path_to_try) # update timestamp of precompilation file - catch ex # file might be read-only and then we fail to update timestamp, which is fine - ex isa IOError || rethrow() - end + end + end + for build_id in try_build_ids + for path_to_try in paths::Vector{String} + staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try; reasons, stalecheck) + if staledeps === true + continue end - # finish loading module graph into staledeps - # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock - for i in eachindex(staledeps) - dep = staledeps[i] - dep isa Module && continue - modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} - dep = start_loading(modkey, modbuild_id, stalecheck) - while true - if dep isa Module - if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id - break - else - @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." - @goto check_next_path + try + staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} + # finish checking staledeps module graph + for i in eachindex(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} + modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) + for modpath_to_try in modpaths + modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try; stalecheck) + if modstaledeps === true + continue end + modstaledeps, modocachepath, _ = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} + staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath) + @goto check_next_dep + end + @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." + @goto check_next_path + @label check_next_dep + end + M = maybe_loaded_precompile(pkg, newbuild_id) + if isa(M, Module) + stalecheck && register_root_module(M) + return M + end + if stalecheck + try + touch(path_to_try) # update timestamp of precompilation file + catch ex # file might be read-only and then we fail to update timestamp, which is fine + ex isa IOError || rethrow() end - if dep === nothing - try - set_pkgorigin_version_path(modkey, modpath) - dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) - finally - end_loading(modkey, dep) + end + # finish loading module graph into staledeps + # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock + for i in eachindex(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} + dep = start_loading(modkey, modbuild_id, stalecheck) + while true + if dep isa Module + if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id + break + else + @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." + @goto check_next_path + end end - if !isa(dep, Module) - @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep - @goto check_next_path - else - push!(newdeps, modkey) + if dep === nothing + try + set_pkgorigin_version_path(modkey, modpath) + dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) + finally + end_loading(modkey, dep) + end + if !isa(dep, Module) + @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep + @goto check_next_path + else + push!(newdeps, modkey) + end end end + staledeps[i] = dep end - staledeps[i] = dep - end - restored = get(loaded_precompiles, pkg => newbuild_id, nothing) - if !isa(restored, Module) - restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps; register = stalecheck) - end - isa(restored, Module) && return restored - @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored - @label check_next_path - finally - for modkey in newdeps - insert_extension_triggers(modkey) - stalecheck && run_package_callbacks(modkey) + restored = maybe_loaded_precompile(pkg, newbuild_id) + if !isa(restored, Module) + restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps; register = stalecheck) + end + isa(restored, Module) && return restored + @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored + @label check_next_path + finally + for modkey in newdeps + insert_extension_triggers(modkey) + stalecheck && run_package_callbacks(modkey) + end + empty!(newdeps) end - empty!(newdeps) end end return nothing @@ -2047,7 +2060,7 @@ function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) loaded = stalecheck ? maybe_root_module(modkey) : nothing loaded isa Module && return loaded if build_id != UInt128(0) - loaded = get(loaded_precompiles, modkey => build_id, nothing) + loaded = maybe_loaded_precompile(modkey, build_id) loaded isa Module && return loaded end loading = get(package_locks, modkey, nothing) @@ -2377,12 +2390,21 @@ const pkgorigins = Dict{PkgId,PkgOrigin}() const explicit_loaded_modules = Dict{PkgId,Module}() # Emptied on Julia start const loaded_modules = Dict{PkgId,Module}() # available to be explicitly loaded -const loaded_precompiles = Dict{Pair{PkgId,UInt128},Module}() # extended (complete) list of modules, available to be loaded +const loaded_precompiles = Dict{PkgId,Vector{Module}}() # extended (complete) list of modules, available to be loaded const loaded_modules_order = Vector{Module}() const module_keys = IdDict{Module,PkgId}() # the reverse of loaded_modules root_module_key(m::Module) = @lock require_lock module_keys[m] +function maybe_loaded_precompile(key::PkgId, buildid::UInt128) + assert_havelock(require_lock) + mods = get(loaded_precompiles, key, nothing) + mods === nothing && return + for mod in mods + module_build_id(mod) == buildid && return mod + end +end + function module_build_id(m::Module) hi, lo = ccall(:jl_module_build_id, NTuple{2,UInt64}, (Any,), m) return (UInt128(hi) << 64) | lo @@ -2403,7 +2425,7 @@ end end end end - haskey(loaded_precompiles, key => module_build_id(m)) || push!(loaded_modules_order, m) + maybe_loaded_precompile(key, module_build_id(m)) === nothing && push!(loaded_modules_order, m) loaded_modules[key] = m explicit_loaded_modules[key] = m module_keys[m] = key @@ -3789,8 +3811,8 @@ end for i in 1:ndeps req_key, req_build_id = required_modules[i] # Check if module is already loaded - if !stalecheck && haskey(loaded_precompiles, req_key => req_build_id) - M = loaded_precompiles[req_key => req_build_id] + M = stalecheck ? nothing : maybe_loaded_precompile(req_key, req_build_id) + if M !== nothing @assert PkgId(M) == req_key && module_build_id(M) === req_build_id depmods[i] = M elseif root_module_exists(req_key) diff --git a/test/loading.jl b/test/loading.jl index b66fd632f23fa..1674a9f59a0c3 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1,10 +1,10 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -original_depot_path = copy(Base.DEPOT_PATH) - using Test # Tests for @__LINE__ inside and outside of macros +# NOTE: the __LINE__ numbers for these first couple tests are significant, so +# adding any lines here will make those tests fail @test (@__LINE__) == 8 macro macro_caller_lineno() @@ -33,6 +33,9 @@ end @test @nested_LINE_expansion() == ((@__LINE__() - 4, @__LINE__() - 12), @__LINE__()) @test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__()) +original_depot_path = copy(Base.DEPOT_PATH) +include("precompile_utils.jl") + loaded_files = String[] push!(Base.include_callbacks, (mod::Module, fn::String) -> push!(loaded_files, fn)) include("test_sourcepath.jl") @@ -1603,3 +1606,32 @@ end copy!(LOAD_PATH, old_load_path) end end + +@testset "require_stdlib loading duplication" begin + depot_path = mktempdir() + oldBase64 = nothing + try + push!(empty!(DEPOT_PATH), depot_path) + Base64_key = Base.PkgId(Base.UUID("2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"), "Base64") + oldBase64 = Base.unreference_module(Base64_key) + cc = Base.compilecache(Base64_key) + @test Base.isprecompiled(Base64_key, cachepaths=String[cc[1]]) + empty!(DEPOT_PATH) + Base.require_stdlib(Base64_key) + push!(DEPOT_PATH, depot_path) + append!(DEPOT_PATH, original_depot_path) + oldloaded = @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) + Base.require(Base64_key) + @test @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) == oldloaded + Base.unreference_module(Base64_key) + empty!(DEPOT_PATH) + push!(DEPOT_PATH, depot_path) + Base.require(Base64_key) + @test @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) == oldloaded + 1 + Base.unreference_module(Base64_key) + finally + oldBase64 === nothing || Base.register_root_module(oldBase64) + copy!(DEPOT_PATH, original_depot_path) + rm(depot_path, force=true, recursive=true) + end +end From 7986e17dd215367bfcdc5700eacf2a2d89f61d50 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 4 Oct 2024 12:05:15 -0400 Subject: [PATCH 312/548] Apple: fix bus error on smaller readonly file in unix (#55859) Enables the fix for #28245 in #44354 for Apple now that the Julia bugs are fixed by #55641 and #55877. Closes #28245 --- stdlib/Mmap/src/Mmap.jl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stdlib/Mmap/src/Mmap.jl b/stdlib/Mmap/src/Mmap.jl index df2f4f1a19991..7d57bf053940d 100644 --- a/stdlib/Mmap/src/Mmap.jl +++ b/stdlib/Mmap/src/Mmap.jl @@ -213,9 +213,7 @@ function mmap(io::IO, szfile = convert(Csize_t, len + offset) requestedSizeLarger = false if !(io isa Mmap.Anonymous) - @static if !Sys.isapple() - requestedSizeLarger = szfile > filesize(io) - end + requestedSizeLarger = szfile > filesize(io) end # platform-specific mmapping @static if Sys.isunix() @@ -231,9 +229,6 @@ function mmap(io::IO, throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) end end - @static if Sys.isapple() - iswrite && grow && grow!(io, offset, len) - end # mmap the file ptr = ccall(:jl_mmap, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t, Cint, Cint, RawFD, Int64), C_NULL, mmaplen, prot, flags, file_desc, offset_page) From 1c5cd96f57bddeeccc1e99d12f6efae26ed6e333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 4 Oct 2024 21:02:29 +0100 Subject: [PATCH 313/548] Add `Float16` to `Base.HWReal` (#55929) --- base/intfuncs.jl | 2 +- test/intfuncs.jl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 8d46fcffa3ad5..06a0213e7141c 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -362,7 +362,7 @@ end # Restrict inlining to hardware-supported arithmetic types, which # are fast enough to benefit from inlining. -const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64} +const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float16,Float32,Float64} const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}} # Inline x^2 and x^3 for Val diff --git a/test/intfuncs.jl b/test/intfuncs.jl index deb1dd10681e8..6f1bde69dddfe 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -616,3 +616,20 @@ end @test Base.infer_effects(gcdx, (Int,Int)) |> Core.Compiler.is_foldable @test Base.infer_effects(invmod, (Int,Int)) |> Core.Compiler.is_foldable @test Base.infer_effects(binomial, (Int,Int)) |> Core.Compiler.is_foldable + +@testset "literal power" begin + @testset for T in Base.uniontypes(Base.HWReal) + ns = (T(0), T(1), T(5)) + if T <: AbstractFloat + ns = (ns..., T(3.14), T(-2.71)) + end + for n in ns + @test n ^ 0 === T(1) + @test n ^ 1 === n + @test n ^ 2 === n * n + @test n ^ 3 === n * n * n + @test n ^ -1 ≈ inv(n) + @test n ^ -2 ≈ inv(n) * inv(n) + end + end +end From 675da9df8710444a868d10b9206a683daff3fde3 Mon Sep 17 00:00:00 2001 From: Mo-Gul Date: Fri, 4 Oct 2024 23:29:41 +0200 Subject: [PATCH 314/548] docs: make mod an operator (#55988) --- base/range.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index 8b30222382c9a..ce2d2e2821c24 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1485,7 +1485,7 @@ end """ mod(x::Integer, r::AbstractUnitRange) -Find `y` in the range `r` such that ``x ≡ y (mod n)``, where `n = length(r)`, +Find `y` in the range `r` such that ``x ≡ y (\\mod n)``, where `n = length(r)`, i.e. `y = mod(x - first(r), n) + first(r)`. See also [`mod1`](@ref). From fe864374d50b121aeb3ce5473c206338b21df5e5 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 4 Oct 2024 20:28:31 -0400 Subject: [PATCH 315/548] InteractiveUtils: add `@trace_compile` and `@trace_dispatch` (#55915) --- NEWS.md | 4 ++ doc/src/manual/performance-tips.md | 6 +- src/gf.c | 54 ++++++++++++++++-- src/julia_internal.h | 6 ++ stdlib/InteractiveUtils/docs/src/index.md | 2 + .../InteractiveUtils/src/InteractiveUtils.jl | 2 +- stdlib/InteractiveUtils/src/macros.jl | 55 +++++++++++++++++++ stdlib/InteractiveUtils/test/runtests.jl | 34 +++++++++++- 8 files changed, 151 insertions(+), 12 deletions(-) diff --git a/NEWS.md b/NEWS.md index fb1fcf381cc7f..bb22c9f940a78 100644 --- a/NEWS.md +++ b/NEWS.md @@ -182,6 +182,10 @@ Standard library changes #### InteractiveUtils +* New macros `@trace_compile` and `@trace_dispatch` for running an expression with + `--trace-compile=stderr --trace-compile-timing` and `--trace-dispatch=stderr` respectively enabled. + ([#55915]) + Deprecated or removed --------------------- diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 417d5ac7a4ca1..3033720b5df8c 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1486,11 +1486,11 @@ from the manifest, then revert the change with `pkg> undo`. If loading time is dominated by slow `__init__()` methods having compilation, one verbose way to identify what is being compiled is to use the julia args `--trace-compile=stderr --trace-compile-timing` which will report a [`precompile`](@ref) -statement each time a method is compiled, along with how long compilation took. For instance, the full setup would be: +statement each time a method is compiled, along with how long compilation took. The InteractiveUtils macro +[`@trace_compile`](@ref) provides a way to enable those args for a specific call. So a call for a complete report report would look like: ``` -$ julia --startup-file=no --trace-compile=stderr --trace-compile-timing -julia> @time @time_imports using CustomPackage +julia> @time @time_imports @trace_compile using CustomPackage ... ``` diff --git a/src/gf.c b/src/gf.c index 56ebe6fe2fa84..fc2e62ebff96b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2513,12 +2513,32 @@ jl_code_instance_t *jl_method_inferred_with_abi(jl_method_instance_t *mi JL_PROP jl_mutex_t precomp_statement_out_lock; +_Atomic(uint8_t) jl_force_trace_compile_timing_enabled = 0; + +/** + * @brief Enable force trace compile to stderr with timing. + */ +JL_DLLEXPORT void jl_force_trace_compile_timing_enable(void) +{ + // Increment the flag to allow reentrant callers to `@trace_compile`. + jl_atomic_fetch_add(&jl_force_trace_compile_timing_enabled, 1); +} +/** + * @brief Disable force trace compile to stderr with timing. + */ +JL_DLLEXPORT void jl_force_trace_compile_timing_disable(void) +{ + // Increment the flag to allow reentrant callers to `@trace_compile`. + jl_atomic_fetch_add(&jl_force_trace_compile_timing_enabled, -1); +} + static void record_precompile_statement(jl_method_instance_t *mi, double compilation_time, int is_recompile) { static ios_t f_precompile; static JL_STREAM* s_precompile = NULL; jl_method_t *def = mi->def.method; - if (jl_options.trace_compile == NULL) + uint8_t force_trace_compile = jl_atomic_load_relaxed(&jl_force_trace_compile_timing_enabled); + if (force_trace_compile == 0 && jl_options.trace_compile == NULL) return; if (!jl_is_method(def)) return; @@ -2528,7 +2548,7 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila JL_LOCK(&precomp_statement_out_lock); if (s_precompile == NULL) { const char *t = jl_options.trace_compile; - if (!strncmp(t, "stderr", 6)) { + if (force_trace_compile || !strncmp(t, "stderr", 6)) { s_precompile = JL_STDERR; } else { @@ -2540,7 +2560,7 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila if (!jl_has_free_typevars(mi->specTypes)) { if (is_recompile && s_precompile == JL_STDERR && jl_options.color != JL_OPTIONS_COLOR_OFF) jl_printf(s_precompile, "\e[33m"); - if (jl_options.trace_compile_timing) + if (force_trace_compile || jl_options.trace_compile_timing) jl_printf(s_precompile, "#= %6.1f ms =# ", compilation_time / 1e6); jl_printf(s_precompile, "precompile("); jl_static_show(s_precompile, mi->specTypes); @@ -2562,6 +2582,25 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila jl_mutex_t dispatch_statement_out_lock; +_Atomic(uint8_t) jl_force_trace_dispatch_enabled = 0; + +/** + * @brief Enable force trace dispatch to stderr. + */ +JL_DLLEXPORT void jl_force_trace_dispatch_enable(void) +{ + // Increment the flag to allow reentrant callers to `@trace_dispatch`. + jl_atomic_fetch_add(&jl_force_trace_dispatch_enabled, 1); +} +/** + * @brief Disable force trace dispatch to stderr. + */ +JL_DLLEXPORT void jl_force_trace_dispatch_disable(void) +{ + // Increment the flag to allow reentrant callers to `@trace_dispatch`. + jl_atomic_fetch_add(&jl_force_trace_dispatch_enabled, -1); +} + static void record_dispatch_statement(jl_method_instance_t *mi) { static ios_t f_dispatch; @@ -2570,10 +2609,11 @@ static void record_dispatch_statement(jl_method_instance_t *mi) if (!jl_is_method(def)) return; + uint8_t force_trace_dispatch = jl_atomic_load_relaxed(&jl_force_trace_dispatch_enabled); JL_LOCK(&dispatch_statement_out_lock); if (s_dispatch == NULL) { const char *t = jl_options.trace_dispatch; - if (!strncmp(t, "stderr", 6)) { + if (force_trace_dispatch || !strncmp(t, "stderr", 6)) { s_dispatch = JL_STDERR; } else { @@ -3393,7 +3433,8 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t // unreachable } // mfunc is about to be dispatched - if (jl_options.trace_dispatch != NULL) { + uint8_t force_trace_dispatch = jl_atomic_load_relaxed(&jl_force_trace_dispatch_enabled); + if (force_trace_dispatch || jl_options.trace_dispatch != NULL) { uint8_t miflags = jl_atomic_load_relaxed(&mfunc->flags); uint8_t was_dispatched = miflags & JL_MI_FLAGS_MASK_DISPATCHED; if (!was_dispatched) { @@ -3524,7 +3565,8 @@ jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, jl_value jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation } JL_GC_PROMISE_ROOTED(mfunc); - if (jl_options.trace_dispatch != NULL) { + uint8_t force_trace_dispatch = jl_atomic_load_relaxed(&jl_force_trace_dispatch_enabled); + if (force_trace_dispatch || jl_options.trace_dispatch != NULL) { uint8_t miflags = jl_atomic_load_relaxed(&mfunc->flags); uint8_t was_dispatched = miflags & JL_MI_FLAGS_MASK_DISPATCHED; if (!was_dispatched) { diff --git a/src/julia_internal.h b/src/julia_internal.h index 9a61c3d18356f..20d90fede3d5e 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1171,6 +1171,12 @@ JL_DLLEXPORT jl_code_instance_t *jl_cache_uninferred(jl_method_instance_t *mi, j JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst_for_uninferred(jl_method_instance_t *mi, jl_code_info_t *src); JL_DLLEXPORT extern jl_value_t *(*const jl_rettype_inferred_addr)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_force_trace_compile_timing_enable(void); +JL_DLLEXPORT void jl_force_trace_compile_timing_disable(void); + +JL_DLLEXPORT void jl_force_trace_dispatch_enable(void); +JL_DLLEXPORT void jl_force_trace_dispatch_disable(void); + uint32_t jl_module_next_counter(jl_module_t *m) JL_NOTSAFEPOINT; jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs); diff --git a/stdlib/InteractiveUtils/docs/src/index.md b/stdlib/InteractiveUtils/docs/src/index.md index dbfb42b9a931d..69b68a27e4e81 100644 --- a/stdlib/InteractiveUtils/docs/src/index.md +++ b/stdlib/InteractiveUtils/docs/src/index.md @@ -33,5 +33,7 @@ InteractiveUtils.@code_llvm InteractiveUtils.code_native InteractiveUtils.@code_native InteractiveUtils.@time_imports +InteractiveUtils.@trace_compile +InteractiveUtils.@trace_dispatch InteractiveUtils.clipboard ``` diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 835988ddf149f..f3c1ff7fba59f 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -11,7 +11,7 @@ Base.Experimental.@optlevel 1 export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo, versioninfo, subtypes, supertypes, @which, @edit, @less, @functionloc, @code_warntype, - @code_typed, @code_lowered, @code_llvm, @code_native, @time_imports, clipboard + @code_typed, @code_lowered, @code_llvm, @code_native, @time_imports, clipboard, @trace_compile, @trace_dispatch import Base.Docs.apropos diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index bb56c47b4f9ca..211687df47954 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -256,6 +256,28 @@ macro time_imports(ex) end end +macro trace_compile(ex) + quote + try + ccall(:jl_force_trace_compile_timing_enable, Cvoid, ()) + $(esc(ex)) + finally + ccall(:jl_force_trace_compile_timing_disable, Cvoid, ()) + end + end +end + +macro trace_dispatch(ex) + quote + try + ccall(:jl_force_trace_dispatch_enable, Cvoid, ()) + $(esc(ex)) + finally + ccall(:jl_force_trace_dispatch_disable, Cvoid, ()) + end + end +end + """ @functionloc @@ -409,3 +431,36 @@ julia> @time_imports using CSV """ :@time_imports + +""" + @trace_compile + +A macro to execute an expression and show any methods that were compiled (or recompiled in yellow), +like the julia args `--trace-compile=stderr --trace-compile-timing` but specifically for a call. + +```julia-repl +julia> @trace_compile rand(2,2) * rand(2,2) +#= 39.1 ms =# precompile(Tuple{typeof(Base.rand), Int64, Int64}) +#= 102.0 ms =# precompile(Tuple{typeof(Base.:(*)), Array{Float64, 2}, Array{Float64, 2}}) +2×2 Matrix{Float64}: + 0.421704 0.864841 + 0.211262 0.444366 +``` + +!!! compat "Julia 1.12" + This macro requires at least Julia 1.12 + +""" +:@trace_compile + +""" + @trace_dispatch + +A macro to execute an expression and report methods that were compiled via dynamic dispatch, +like the julia arg `--trace-dispatch=stderr` but specifically for a call. + +!!! compat "Julia 1.12" + This macro requires at least Julia 1.12 + +""" +:@trace_dispatch diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 424564b70384c..8e7090cb53020 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -708,7 +708,7 @@ let length((@code_lowered sum(1:10)).code) end -@testset "@time_imports" begin +@testset "@time_imports, @trace_compile, @trace_dispatch" begin mktempdir() do dir cd(dir) do try @@ -717,7 +717,16 @@ end write(foo_file, """ module Foo3242 - foo() = 1 + function foo() + Base.Experimental.@force_compile + foo(1) + end + foo(x) = x + function bar() + Base.Experimental.@force_compile + bar(1) + end + bar(x) = x end """) @@ -734,6 +743,27 @@ end @test occursin("ms Foo3242", String(buf)) + fname = tempname() + f = open(fname, "w") + redirect_stderr(f) do + @trace_compile @eval Foo3242.foo() + end + close(f) + buf = read(fname) + rm(fname) + + @test occursin("ms =# precompile(", String(buf)) + + fname = tempname() + f = open(fname, "w") + redirect_stderr(f) do + @trace_dispatch @eval Foo3242.bar() + end + close(f) + buf = read(fname) + rm(fname) + + @test occursin("precompile(", String(buf)) finally filter!((≠)(dir), LOAD_PATH) end From 3a132cfc7661f850340d0360df6d3279d1aa7e16 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 5 Oct 2024 05:29:19 +0200 Subject: [PATCH 316/548] Profile: document heap snapshot viewing tools (#55743) --- stdlib/Profile/docs/src/index.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/Profile/docs/src/index.md b/stdlib/Profile/docs/src/index.md index 5b4db77b9cb16..0b358e5decfa9 100644 --- a/stdlib/Profile/docs/src/index.md +++ b/stdlib/Profile/docs/src/index.md @@ -155,3 +155,8 @@ julia> Profile.HeapSnapshot.assemble_snapshot("snapshot", "snapshot.heapsnapshot The resulting heap snapshot file can be uploaded to chrome devtools to be viewed. For more information, see the [chrome devtools docs](https://developer.chrome.com/docs/devtools/memory-problems/heap-snapshots/#view_snapshots). +An alternative for analyzing Chromium heap snapshots is with the VS Code extension +`ms-vscode.vscode-js-profile-flame`. + +The Firefox heap snapshots are of a different format, and Firefox currently may +*not* be used for viewing the heap snapshots generated by Julia. From fb77d60fb7088a3b8ccac73ea3476b9a8c88d455 Mon Sep 17 00:00:00 2001 From: Christian Guinard <28689358+christiangnrd@users.noreply.github.com> Date: Sat, 5 Oct 2024 01:24:42 -0300 Subject: [PATCH 317/548] [REPL] Fix #55850 by using `safe_realpath` instead of `abspath` in `projname` (#55851) --- stdlib/REPL/src/Pkg_beforeload.jl | 2 +- stdlib/REPL/test/repl.jl | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index ebd0cd255ce19..472fbc924668d 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -88,7 +88,7 @@ function projname(project_file::String) end for depot in Base.DEPOT_PATH envdir = joinpath(depot, "environments") - if startswith(abspath(project_file), abspath(envdir)) + if startswith(safe_realpath(project_file), safe_realpath(envdir)) return "@" * name end end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index f4d594b2a02e1..85a8137fa003e 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1966,11 +1966,20 @@ 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 + withenv("JULIA_DEPOT_PATH" => mktempdir() * (Sys.iswindows() ? ";" : ":"), "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 + # Issue 55850 + tmp_55850 = mktempdir() + tmp_sym_link = joinpath(tmp_55850, "sym") + symlink(tmp_55850, tmp_sym_link; dir_target=true) + withenv("JULIA_DEPOT_PATH" => tmp_sym_link * (Sys.iswindows() ? ";" : ":"), "JULIA_LOAD_PATH" => nothing) do + prompt = readchomp(`$(Base.julia_cmd()[1]) --startup-file=no -e "using REPL; print(REPL.projname(REPL.find_project_file()))"`) + @test prompt == "@v$(VERSION.major).$(VERSION.minor)" + 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> " From 096c1d2bcbae1b7388ee4285b89da50dbb9f7094 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:38:53 +0900 Subject: [PATCH 318/548] optimizer: enable load forwarding with the `finalizer` elision (#55991) When the finalizer elision pass is used, load forwarding is not performed currently, regardless of whether the pass succeeds or not. But this is not necessary, and by keeping the `setfield!` call, we can safely forward `getfield` even if finalizer elision is tried. --- base/compiler/ssair/passes.jl | 16 +++++++++------- test/compiler/inline.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index e227249b48598..0e2272524a0ed 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1707,22 +1707,24 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}} ismutabletype(typ) || continue typ = typ::DataType # First check for any finalizer calls - finalizer_idx = nothing - for use in defuse.uses + finalizer_useidx = nothing + for (useidx, use) in enumerate(defuse.uses) if use.kind === :finalizer # For now: Only allow one finalizer per allocation - finalizer_idx !== nothing && @goto skip - finalizer_idx = use.idx + finalizer_useidx !== nothing && @goto skip + finalizer_useidx = useidx end end - if finalizer_idx !== nothing && inlining !== nothing + all_eliminated = all_forwarded = true + if finalizer_useidx !== nothing && inlining !== nothing + finalizer_idx = defuse.uses[finalizer_useidx].idx try_resolve_finalizer!(ir, defidx, finalizer_idx, defuse, inlining, lazydomtree, lazypostdomtree, ir[SSAValue(finalizer_idx)][:info]) - continue + deleteat!(defuse.uses, finalizer_useidx) + all_eliminated = all_forwarded = false # can't eliminate `setfield!` calls safely end # Partition defuses by field fielddefuse = SSADefUse[SSADefUse() for _ = 1:fieldcount(typ)] - all_eliminated = all_forwarded = true for use in defuse.uses if use.kind === :preserve for du in fielddefuse diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index fceb920352482..2de6d9950d4e4 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1596,6 +1596,32 @@ let @test get_finalization_count() == 1000 end +# Load forwarding with `finalizer` elision +let src = code_typed1((Int,)) do x + xs = finalizer(Ref(x)) do obj + @noinline + Base.@assume_effects :nothrow :notaskstate + Core.println("finalizing: ", obj[]) + end + Base.@assume_effects :nothrow @noinline println("xs[] = ", @inline xs[]) + return xs[] + end + @test count(iscall((src, getfield)), src.code) == 0 +end +let src = code_typed1((Int,)) do x + xs = finalizer(Ref(x)) do obj + @noinline + Base.@assume_effects :nothrow :notaskstate + Core.println("finalizing: ", obj[]) + end + Base.@assume_effects :nothrow @noinline println("xs[] = ", @inline xs[]) + xs[] += 1 + return xs[] + end + @test count(iscall((src, getfield)), src.code) == 0 + @test count(iscall((src, setfield!)), src.code) == 1 +end + # optimize `[push!|pushfirst!](::Vector{Any}, x...)` @testset "optimize `$f(::Vector{Any}, x...)`" for f = Any[push!, pushfirst!] @eval begin From 5d12c6de4399e62e5e863a9b25b676ce4e26ce97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 5 Oct 2024 08:34:48 +0100 Subject: [PATCH 319/548] Avoid `stat`-ing stdlib path if it's unreadable (#55992) --- base/loading.jl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 475ce7f50eae7..c69e37e4d56ea 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3864,10 +3864,17 @@ end # now check if this file's content hash has changed relative to its source files if stalecheck - if !samefile(includes[1].filename, modpath) && !samefile(fixup_stdlib_path(includes[1].filename), modpath) - @debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath" - record_reason(reasons, "wrong source") - return true # cache file was compiled from a different path + if !samefile(includes[1].filename, modpath) + # In certain cases the path rewritten by `fixup_stdlib_path` may + # point to an unreadable directory, make sure we can `stat` the + # file before comparing it with `modpath`. + stdlib_path = fixup_stdlib_path(includes[1].filename) + if !(isreadable(stdlib_path) && samefile(stdlib_path, modpath)) + !samefile(fixup_stdlib_path(includes[1].filename), modpath) + @debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath" + record_reason(reasons, "wrong source") + return true # cache file was compiled from a different path + end end for (modkey, req_modkey) in requires # verify that `require(modkey, name(req_modkey))` ==> `req_modkey` From ae9472993ba299a7596b2025adb4d9abf7e02e9b Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 5 Oct 2024 11:46:02 +0200 Subject: [PATCH 320/548] doc: manual: cmd: fix Markdown in table entry for `--trim` (#55979) --- doc/src/manual/command-line-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 5255720e55cd7..734d7031db5e8 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -219,7 +219,7 @@ The following is a complete list of command-line switches available when launchi |`--trace-dispatch={stderr\|name}` |Print precompile statements for methods dispatched during execution or save to stderr or a path.| |`--image-codegen` |Force generate code in imaging mode| |`--permalloc-pkgimg={yes\|no*}` |Copy the data section of package images into memory| -|`--trim={no*|safe|unsafe|unsafe-warn}` |Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. The three non-default options differ in how they handle dynamic call sites. In safe mode, such sites result in compile-time errors. In unsafe mode, such sites are allowed but the resulting binary might be missing needed code and can throw runtime errors. With unsafe-warn, such sites will trigger warnings at compile-time and might error at runtime.| +|`--trim={no*\|safe\|unsafe\|unsafe-warn}` |Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. The three non-default options differ in how they handle dynamic call sites. In safe mode, such sites result in compile-time errors. In unsafe mode, such sites are allowed but the resulting binary might be missing needed code and can throw runtime errors. With unsafe-warn, such sites will trigger warnings at compile-time and might error at runtime.| !!! compat "Julia 1.1" In Julia 1.0, the default `--project=@.` option did not search up from the root From 03c0b899f0457cf818268093b72af1b1b5d9868c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 5 Oct 2024 19:32:40 +0100 Subject: [PATCH 321/548] Avoid conversions to `Float64` in non-literal powers of `Float16` (#55994) Co-authored-by: Alex Arslan --- base/math.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/base/math.jl b/base/math.jl index da51ab3a17bd0..16a8a547e8de1 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1276,14 +1276,12 @@ end return ifelse(isfinite(x) & isfinite(err), muladd(x, y, err), x*y) end -function ^(x::Float32, n::Integer) +function ^(x::Union{Float16,Float32}, n::Integer) n == -2 && return (i=inv(x); i*i) n == 3 && return x*x*x #keep compatibility with literal_pow - n < 0 && return Float32(Base.power_by_squaring(inv(Float64(x)),-n)) - Float32(Base.power_by_squaring(Float64(x),n)) + n < 0 && return oftype(x, Base.power_by_squaring(inv(widen(x)),-n)) + oftype(x, Base.power_by_squaring(widen(x),n)) end -@inline ^(x::Float16, y::Integer) = Float16(Float32(x) ^ y) -@inline literal_pow(::typeof(^), x::Float16, ::Val{p}) where {p} = Float16(literal_pow(^,Float32(x),Val(p))) ## rem2pi-related calculations ## From ab6df86f77b526f93d7c1a315924cf42dc8e1feb Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Sun, 6 Oct 2024 02:13:40 +0200 Subject: [PATCH 322/548] Remove unreachable error branch in memset calls (and in repeat) (#55985) Some places use the pattern memset(A, v, length(A)), which requires a conversion UInt(length(A)). This is technically fallible, but can't actually fail when A is a Memory or Array. Remove the dead error branch by casting to UInt instead. Similarly, in repeat(x, r), r is first checked to be nonnegative, then converted to UInt, then used in multiple calls where it is converted to UInt each time. Here, only do it once. --- base/array.jl | 2 +- base/genericmemory.jl | 2 +- base/iddict.jl | 2 +- base/strings/string.jl | 3 ++- base/strings/substring.jl | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/base/array.jl b/base/array.jl index 648fedd5036e1..5b3e6cc398479 100644 --- a/base/array.jl +++ b/base/array.jl @@ -415,7 +415,7 @@ function fill!(a::Union{Array{UInt8}, Array{Int8}}, x::Integer) ref = a.ref t = @_gc_preserve_begin ref p = unsafe_convert(Ptr{Cvoid}, ref) - memset(p, x isa eltype(a) ? x : convert(eltype(a), x), length(a)) + memset(p, x isa eltype(a) ? x : convert(eltype(a), x), length(a) % UInt) @_gc_preserve_end t return a end diff --git a/base/genericmemory.jl b/base/genericmemory.jl index c4ebbc6ca14e1..91b87ab14c6b1 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -190,7 +190,7 @@ function fill!(a::Union{Memory{UInt8}, Memory{Int8}}, x::Integer) t = @_gc_preserve_begin a p = unsafe_convert(Ptr{Cvoid}, a) T = eltype(a) - memset(p, x isa T ? x : convert(T, x), length(a)) + memset(p, x isa T ? x : convert(T, x), length(a) % UInt) @_gc_preserve_end t return a end diff --git a/base/iddict.jl b/base/iddict.jl index 9c133d5ba23c6..f1632e93427a8 100644 --- a/base/iddict.jl +++ b/base/iddict.jl @@ -126,7 +126,7 @@ function empty!(d::IdDict) d.ht = Memory{Any}(undef, 32) ht = d.ht t = @_gc_preserve_begin ht - memset(unsafe_convert(Ptr{Cvoid}, ht), 0, sizeof(ht)) + memset(unsafe_convert(Ptr{Cvoid}, ht), 0, sizeof(ht) % UInt) @_gc_preserve_end t d.ndel = 0 d.count = 0 diff --git a/base/strings/string.jl b/base/strings/string.jl index 90d6e5b26ccd3..a46ee60e4f023 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -570,9 +570,10 @@ julia> repeat('A', 3) ``` """ function repeat(c::AbstractChar, r::Integer) + r < 0 && throw(ArgumentError("can't repeat a character $r times")) + r = UInt(r)::UInt c = Char(c)::Char r == 0 && return "" - r < 0 && throw(ArgumentError("can't repeat a character $r times")) u = bswap(reinterpret(UInt32, c)) n = 4 - (leading_zeros(u | 0xff) >> 3) s = _string_n(n*r) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 2a6b4ae7b9a22..50717d3c27e23 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -272,6 +272,7 @@ end function repeat(s::Union{String, SubString{String}}, r::Integer) r < 0 && throw(ArgumentError("can't repeat a string $r times")) + r = UInt(r)::UInt r == 0 && return "" r == 1 && return String(s) n = sizeof(s) From 9bceed846e9c50ad64046b8d2ed033681f250993 Mon Sep 17 00:00:00 2001 From: Mo-Gul Date: Sun, 6 Oct 2024 04:32:13 +0200 Subject: [PATCH 323/548] fix up docstring of `mod` (#56000) --- base/range.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index ce2d2e2821c24..4b5d076dcf436 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1485,7 +1485,7 @@ end """ mod(x::Integer, r::AbstractUnitRange) -Find `y` in the range `r` such that ``x ≡ y (\\mod n)``, where `n = length(r)`, +Find `y` in the range `r` such that `x` ≡ `y` (mod `n`), where `n = length(r)`, i.e. `y = mod(x - first(r), n) + first(r)`. See also [`mod1`](@ref). From 8248bf420d951c159374b8e77b705e52f14baaaa Mon Sep 17 00:00:00 2001 From: spaette <111918424+spaette@users.noreply.github.com> Date: Sun, 6 Oct 2024 07:42:27 +0200 Subject: [PATCH 324/548] fix typos (#56008) these are all in markdown files Co-authored-by: spaette --- doc/src/devdocs/build/distributing.md | 2 +- doc/src/devdocs/gc.md | 2 +- doc/src/devdocs/llvm.md | 2 +- doc/src/manual/environment-variables.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/src/devdocs/build/distributing.md b/doc/src/devdocs/build/distributing.md index 99c08923b415b..ed06c20fa0df3 100644 --- a/doc/src/devdocs/build/distributing.md +++ b/doc/src/devdocs/build/distributing.md @@ -108,7 +108,7 @@ Alternatively, Julia may be built as a framework by invoking `make` with the Windows ------- -Instructions for reating a Julia distribution on Windows are described in the +Instructions for creating a Julia distribution on Windows are described in the [build devdocs for Windows](https://github.com/JuliaLang/julia/blob/master/doc/src/devdocs/build/windows.md). Notes on BLAS and LAPACK diff --git a/doc/src/devdocs/gc.md b/doc/src/devdocs/gc.md index 9b9038c9445f3..a45e8afb271ce 100644 --- a/doc/src/devdocs/gc.md +++ b/doc/src/devdocs/gc.md @@ -21,7 +21,7 @@ lists. Metadata for free pages, however, may be stored into three separate lock- Julia's pool allocator follows a "tiered" allocation discipline. When requesting a memory page for the pool allocator, Julia will: -- Try to claim a page from `page_pool_lazily_freed`, which contains pages which were empty on the last stop-the-world phase, but not yet madivsed by a concurrent sweeper GC thread. +- Try to claim a page from `page_pool_lazily_freed`, which contains pages which were empty on the last stop-the-world phase, but not yet madvised by a concurrent sweeper GC thread. - If it failed claiming a page from `page_pool_lazily_freed`, it will try to claim a page from `the page_pool_clean`, which contains pages which were mmaped on a previous page allocation request but never accessed. diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index ab8f7dde50022..c4b80f632cd4e 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -17,7 +17,7 @@ The code for lowering Julia AST to LLVM IR or interpreting it directly is in dir | `cgutils.cpp` | Lowering utilities, notably for array and tuple accesses | | `codegen.cpp` | Top-level of code generation, pass list, lowering builtins | | `debuginfo.cpp` | Tracks debug information for JIT code | -| `disasm.cpp` | Handles native object file and JIT code diassembly | +| `disasm.cpp` | Handles native object file and JIT code disassembly | | `gf.c` | Generic functions | | `intrinsics.cpp` | Lowering intrinsics | | `jitlayers.cpp` | JIT-specific code, ORC compilation layers/utilities | diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index 1fb11018a22e7..b86822e0be4b7 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -144,7 +144,7 @@ files, artifacts, etc. For example, to switch the user depot to `/foo/bar` just ```sh export JULIA_DEPOT_PATH="/foo/bar:" ``` -All package operations, like cloning registrise or installing packages, will now write to +All package operations, like cloning registries or installing packages, will now write to `/foo/bar`, but since the empty entry is expanded to the default system depot, any bundled resources will still be available. If you really only want to use the depot at `/foo/bar`, and not load any bundled resources, simply set the environment variable to `/foo/bar` From 2ae0b7e071b4c128b99485e04a33aaad2c4cd645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sun, 6 Oct 2024 09:34:42 +0100 Subject: [PATCH 325/548] Vectorise random vectors of `Float16` (#55997) --- stdlib/Random/src/XoshiroSimd.jl | 34 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/stdlib/Random/src/XoshiroSimd.jl b/stdlib/Random/src/XoshiroSimd.jl index 6d4886f31d22b..1c5f8306cc302 100644 --- a/stdlib/Random/src/XoshiroSimd.jl +++ b/stdlib/Random/src/XoshiroSimd.jl @@ -44,6 +44,17 @@ simdThreshold(::Type{Bool}) = 640 l = Float32(li >>> 8) * Float32(0x1.0p-24) (UInt64(reinterpret(UInt32, u)) << 32) | UInt64(reinterpret(UInt32, l)) end +@inline function _bits2float(x::UInt64, ::Type{Float16}) + i1 = (x>>>48) % UInt16 + i2 = (x>>>32) % UInt16 + i3 = (x>>>16) % UInt16 + i4 = x % UInt16 + f1 = Float16(i1 >>> 5) * Float16(0x1.0p-11) + f2 = Float16(i2 >>> 5) * Float16(0x1.0p-11) + f3 = Float16(i3 >>> 5) * Float16(0x1.0p-11) + f4 = Float16(i4 >>> 5) * Float16(0x1.0p-11) + return (UInt64(reinterpret(UInt16, f1)) << 48) | (UInt64(reinterpret(UInt16, f2)) << 32) | (UInt64(reinterpret(UInt16, f3)) << 16) | UInt64(reinterpret(UInt16, f4)) +end # required operations. These could be written more concisely with `ntuple`, but the compiler # sometimes refuses to properly vectorize. @@ -118,6 +129,18 @@ for N in [4,8,16] ret <$N x i64> %i """ @eval @inline _bits2float(x::$VT, ::Type{Float32}) = llvmcall($code, $VT, Tuple{$VT}, x) + + code = """ + %as16 = bitcast <$N x i64> %0 to <$(4N) x i16> + %shiftamt = shufflevector <1 x i16> , <1 x i16> undef, <$(4N) x i32> zeroinitializer + %sh = lshr <$(4N) x i16> %as16, %shiftamt + %f = uitofp <$(4N) x i16> %sh to <$(4N) x half> + %scale = shufflevector <1 x half> , <1 x half> undef, <$(4N) x i32> zeroinitializer + %m = fmul <$(4N) x half> %f, %scale + %i = bitcast <$(4N) x half> %m to <$N x i64> + ret <$N x i64> %i + """ + @eval @inline _bits2float(x::$VT, ::Type{Float16}) = llvmcall($code, $VT, Tuple{$VT}, x) end end @@ -137,7 +160,7 @@ end _id(x, T) = x -@inline function xoshiro_bulk(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, T::Union{Type{UInt8}, Type{Bool}, Type{Float32}, Type{Float64}}, ::Val{N}, f::F = _id) where {N, F} +@inline function xoshiro_bulk(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, T::Union{Type{UInt8}, Type{Bool}, Type{Float16}, Type{Float32}, Type{Float64}}, ::Val{N}, f::F = _id) where {N, F} if len >= simdThreshold(T) written = xoshiro_bulk_simd(rng, dst, len, T, Val(N), f) len -= written @@ -265,13 +288,8 @@ end end -function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Float32}, ::SamplerTrivial{CloseOpen01{Float32}}) - GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*4, Float32, xoshiroWidth(), _bits2float) - dst -end - -function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Float64}, ::SamplerTrivial{CloseOpen01{Float64}}) - GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*8, Float64, xoshiroWidth(), _bits2float) +function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{T}, ::SamplerTrivial{CloseOpen01{T}}) where {T<:Union{Float16,Float32,Float64}} + GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*sizeof(T), T, xoshiroWidth(), _bits2float) dst end From 7cc195cf19a317e633a9ea575c842396a2fa70a8 Mon Sep 17 00:00:00 2001 From: Sagnac <83491030+Sagnac@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:58:30 +0000 Subject: [PATCH 326/548] Clarify `div` docstring for floating-point input (#55918) Closes #55837 This is a variant of the warning found in the `fld` docstring clarifying floating-point behaviour. --- base/div.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/base/div.jl b/base/div.jl index 8988f2b70f27b..3fec8d2f5cdf3 100644 --- a/base/div.jl +++ b/base/div.jl @@ -43,6 +43,21 @@ julia> div(4, 3, RoundFromZero) julia> div(-4, 3, RoundFromZero) -2 ``` +Because `div(x, y)` implements strictly correct truncated rounding based on the true +value of floating-point numbers, unintuitive situations can arise. For example: +```jldoctest +julia> div(6.0, 0.1) +59.0 +julia> 6.0 / 0.1 +60.0 +julia> 6.0 / big(0.1) +59.99999999999999666933092612453056361837965690217069245739573412231113406246995 +``` +What is happening here is that the true value of the floating-point number written +as `0.1` is slightly larger than the numerical value 1/10 while `6.0` represents +the number 6 precisely. Therefore the true value of `6.0 / 0.1` is slightly less +than 60. When doing division, this is rounded to precisely `60.0`, but +`div(6.0, 0.1, RoundToZero)` always truncates the true value, so the result is `59.0`. """ div(x, y, r::RoundingMode) From 43f4afec44eb8119121e2eaee68dac12885397ef Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Sun, 6 Oct 2024 14:44:10 -0400 Subject: [PATCH 327/548] improve getproperty(Pairs) warnings (#55989) - Only call `depwarn` if the field is `itr` or `data`; otherwise let the field error happen as normal - Give a more specific deprecation warning. --- base/deprecated.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index f88a53526aa37..b43a4227d42c4 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -432,7 +432,8 @@ const All16{T,N} = Tuple{T,T,T,T,T,T,T,T, # the plan is to eventually overload getproperty to access entries of the dict @noinline function getproperty(x::Pairs, s::Symbol) - depwarn("use values(kwargs) and keys(kwargs) instead of kwargs.data and kwargs.itr", :getproperty, force=true) + s == :data && depwarn("use values(kwargs) instead of kwargs.data", :getproperty, force=true) + s == :itr && depwarn("use keys(kwargs) instead of kwargs.itr", :getproperty, force=true) return getfield(x, s) end From 2ca88addb25035d523f06ec14e6fafd6f5047d38 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Sun, 6 Oct 2024 15:16:59 -0400 Subject: [PATCH 328/548] Document type-piracy / type-leakage restrictions for `require_stdlib` (#56005) I was a recent offender in https://github.com/JuliaLang/Pkg.jl/issues/4017#issuecomment-2377589989 This PR tries to lay down some guidelines for the behavior that stdlibs and the callers of `require_stdlib` must adhere to to avoid "duplicate stdlib" bugs These bugs are particularly nasty because they are experienced semi-rarely and under pretty specific circumstances (they only occur when `require_stdlib` loads another copy of a stdlib, often in a particular order and/or with a particular state of your pre-compile / loading cache) so they may make it a long way through a pre-release cycle without an actionable bug report. --- base/loading.jl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/base/loading.jl b/base/loading.jl index c69e37e4d56ea..4eac272848baf 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2610,6 +2610,46 @@ 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 +""" + require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) + +!!! warning "May load duplicate copies of stdlib packages." + + This requires that all stdlib packages loaded are compatible with having concurrent + copies of themselves loaded into memory. It also places additional restrictions on + the kinds of type-piracy that are allowed in stdlibs, since type-piracy can cause the + dispatch table to become visibly "torn" across multiple different packages. + + The specific requirements are: + + The import side (caller of `require_stdlib`) must not leak any stdlib types, esp. + to any context that may have a conflicting copy of the stdlib(s) (or vice-versa). + - e.g., if an output is forwarded to user code, it must contain only Base types. + - e.g., if an output contains types from the stdlib, it must be consumed "internally" + before reaching user code. + + The imported code (loaded stdlibs) must be very careful about type piracy: + - It must not access any global state that may differ between stdlib copies in + type-pirated methods. + - It must not return any stdlib types from any type-pirated public methods (since + a loaded duplicate would overwrite the Base method again, returning different + types that don't correspond to the user-accessible copy of the stdlib). + - It must not pass / discriminate stdlib types in type-pirated methods, except + indirectly via methods defined in Base and implemented (w/o type-piracy) in + all copies of the stdlib over their respective types. + + The idea behind the above restrictions is that any type-pirated methods in the stdlib + must return a result that is simultaneously correct for all of the stdlib's loaded + copies, including accounting for global state differences and split type identities. + + Furthermore, any imported code must not leak any stdlib types to globals and containers + (e.g. Vectors and mutable structs) in upstream Modules, since this will also lead to + type-confusion when the type is later pulled out in user / stdlib code. + + For examples of issues like the above, see: + [1] https://github.com/JuliaLang/Pkg.jl/issues/4017#issuecomment-2377589989 + [2] https://github.com/JuliaLang/StyledStrings.jl/issues/91#issuecomment-2379602914 +""" function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) @lock require_lock begin # the PkgId of the ext, or package if not an ext From c2a2e38079c1b540e93e8524cf53da4153358481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:10:51 +0100 Subject: [PATCH 329/548] [LinearAlgebra] Remove unreliable doctests (#56011) The exact textual representation of the output of these doctests depend on the specific kernel used by the BLAS backend, and can vary between versions of OpenBLAS (as it did in #41973), or between different CPUs, which makes these doctests unreliable. Fix #55998. --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 26 ++++------------------- stdlib/LinearAlgebra/src/hessenberg.jl | 2 +- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 17216845b350c..3ecb714a6cfe1 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -394,17 +394,8 @@ julia> Y = zero(X); julia> ldiv!(Y, qr(A), X); -julia> Y -3-element Vector{Float64}: - 0.7128099173553719 - -0.051652892561983674 - 0.10020661157024757 - -julia> A\\X -3-element Vector{Float64}: - 0.7128099173553719 - -0.05165289256198333 - 0.10020661157024785 +julia> Y ≈ A\\X +true ``` """ ldiv!(Y, A, B) @@ -435,17 +426,8 @@ julia> Y = copy(X); julia> ldiv!(qr(A), X); -julia> X -3-element Vector{Float64}: - 0.7128099173553719 - -0.051652892561983674 - 0.10020661157024757 - -julia> A\\Y -3-element Vector{Float64}: - 0.7128099173553719 - -0.05165289256198333 - 0.10020661157024785 +julia> X ≈ A\\Y +true ``` """ ldiv!(A, B) diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index bbaca3c878293..524e57711ce3a 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -446,7 +446,7 @@ This is useful because multiple shifted solves `(F + μ*I) \\ b` Iterating the decomposition produces the factors `F.Q, F.H, F.μ`. # Examples -```jldoctest +```julia-repl julia> A = [4. 9. 7.; 4. 4. 1.; 4. 3. 2.] 3×3 Matrix{Float64}: 4.0 9.0 7.0 From 4d2184159b15137b587b2f765c508621dce3a4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateus=20Ara=C3=BAjo?= Date: Mon, 7 Oct 2024 04:31:49 +0200 Subject: [PATCH 330/548] cleanup functions of Hermitian matrices (#55951) The functions of Hermitian matrices are a bit of a mess. For example, if we have a Hermitian matrix `a` with negative eigenvalues, `a^0.5` doesn't produce the `Symmetric` wrapper, but `sqrt(a)` does. On the other hand, if we have a positive definite `b`, `b^0.5` will be `Hermitian`, but `sqrt(b)` will be `Symmetric`: ```julia using LinearAlgebra a = Hermitian([1.0 2.0;2.0 1.0]) a^0.5 sqrt(a) b = Hermitian([2.0 1.0; 1.0 2.0]) b^0.5 sqrt(b) ``` This sort of arbitrary assignment of wrappers happens with pretty much all functions defined there. There's also some oddities, such as `cis` being the only function defined for `SymTridiagonal`, even though all `eigen`-based functions work, and `cbrt` being the only function not defined for complex Hermitian matrices. I did a cleanup: I defined all functions for `SymTridiagonal` and `Hermitian{<:Complex}`, and always assigned the appropriate wrapper, preserving the input one when possible. There's an inconsistency remaining that I didn't fix, that only `sqrt` and `log` accept a tolerance argument, as changing that is probably breaking. There were also hardly any tests that I could find (only `exp`, `log`, `cis`, and `sqrt`). I'm happy to add them if it's desired. --- stdlib/LinearAlgebra/src/symmetric.jl | 158 +++++++++++++++----------- 1 file changed, 93 insertions(+), 65 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index a7739596a73bb..e17eb80d25453 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -810,26 +810,32 @@ end # Matrix functions ^(A::Symmetric{<:Real}, p::Integer) = sympow(A, p) ^(A::Symmetric{<:Complex}, p::Integer) = sympow(A, p) -function sympow(A::Symmetric, p::Integer) - if p < 0 - return Symmetric(Base.power_by_squaring(inv(A), -p)) - else - return Symmetric(Base.power_by_squaring(A, p)) - end -end -function ^(A::Symmetric{<:Real}, p::Real) - isinteger(p) && return integerpow(A, p) - F = eigen(A) - if all(λ -> λ ≥ 0, F.values) - return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors') - else - return Symmetric((F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors') +^(A::SymTridiagonal{<:Real}, p::Integer) = sympow(A, p) +^(A::SymTridiagonal{<:Complex}, p::Integer) = sympow(A, p) +for hermtype in (:Symmetric, :SymTridiagonal) + @eval begin + function sympow(A::$hermtype, p::Integer) + if p < 0 + return Symmetric(Base.power_by_squaring(inv(A), -p)) + else + return Symmetric(Base.power_by_squaring(A, p)) + end + end + function ^(A::$hermtype{<:Real}, p::Real) + isinteger(p) && return integerpow(A, p) + F = eigen(A) + if all(λ -> λ ≥ 0, F.values) + return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors') + else + return Symmetric((F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors') + end + end + function ^(A::$hermtype{<:Complex}, p::Real) + isinteger(p) && return integerpow(A, p) + return Symmetric(schurpow(A, p)) + end end end -function ^(A::Symmetric{<:Complex}, p::Real) - isinteger(p) && return integerpow(A, p) - return Symmetric(schurpow(A, p)) -end function ^(A::Hermitian, p::Integer) if p < 0 retmat = Base.power_by_squaring(inv(A), -p) @@ -855,16 +861,25 @@ function ^(A::Hermitian{T}, p::Real) where T return Hermitian(retmat) end else - return (F.vectors * Diagonal((complex.(F.values).^p))) * F.vectors' + retmat = (F.vectors * Diagonal((complex.(F.values).^p))) * F.vectors' + if T <: Real + return Symmetric(retmat) + else + return retmat + end end end -for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh) - @eval begin - function ($func)(A::HermOrSym{<:Real}) - F = eigen(A) - return Symmetric((F.vectors * Diagonal(($func).(F.values))) * F.vectors') +for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh, :cbrt) + for (hermtype, wrapper) in [(:Symmetric, :Symmetric), (:SymTridiagonal, :Symmetric), (:Hermitian, :Hermitian)] + @eval begin + function ($func)(A::$hermtype{<:Real}) + F = eigen(A) + return $wrapper((F.vectors * Diagonal(($func).(F.values))) * F.vectors') + end end + end + @eval begin function ($func)(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) @@ -877,23 +892,34 @@ for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh) end end -function cis(A::Union{RealHermSymComplexHerm,SymTridiagonal{<:Real}}) +for wrapper in (:Symmetric, :Hermitian, :SymTridiagonal) + @eval begin + function cis(A::$wrapper{<:Real}) + F = eigen(A) + return Symmetric(F.vectors .* cis.(F.values') * F.vectors') + end + end +end +function cis(A::Hermitian{<:Complex}) F = eigen(A) - # The returned matrix is unitary, and is complex-symmetric for real A return F.vectors .* cis.(F.values') * F.vectors' end + for func in (:acos, :asin) - @eval begin - function ($func)(A::HermOrSym{<:Real}) - F = eigen(A) - if all(λ -> -1 ≤ λ ≤ 1, F.values) - retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' - else - retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' + for (hermtype, wrapper) in [(:Symmetric, :Symmetric), (:SymTridiagonal, :Symmetric), (:Hermitian, :Hermitian)] + @eval begin + function ($func)(A::$hermtype{<:Real}) + F = eigen(A) + if all(λ -> -1 ≤ λ ≤ 1, F.values) + return $wrapper((F.vectors * Diagonal(($func).(F.values))) * F.vectors') + else + return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors') + end end - return Symmetric(retmat) end + end + @eval begin function ($func)(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) @@ -910,14 +936,17 @@ for func in (:acos, :asin) end end -function acosh(A::HermOrSym{<:Real}) - F = eigen(A) - if all(λ -> λ ≥ 1, F.values) - retmat = (F.vectors * Diagonal(acosh.(F.values))) * F.vectors' - else - retmat = (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors' +for (hermtype, wrapper) in [(:Symmetric, :Symmetric), (:SymTridiagonal, :Symmetric), (:Hermitian, :Hermitian)] + @eval begin + function acosh(A::$hermtype{<:Real}) + F = eigen(A) + if all(λ -> λ ≥ 1, F.values) + return $wrapper((F.vectors * Diagonal(acosh.(F.values))) * F.vectors') + else + return Symmetric((F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors') + end + end end - return Symmetric(retmat) end function acosh(A::Hermitian{<:Complex}) n = checksquare(A) @@ -933,14 +962,18 @@ function acosh(A::Hermitian{<:Complex}) end end -function sincos(A::HermOrSym{<:Real}) - n = checksquare(A) - F = eigen(A) - S, C = Diagonal(similar(A, (n,))), Diagonal(similar(A, (n,))) - for i in 1:n - S.diag[i], C.diag[i] = sincos(F.values[i]) +for (hermtype, wrapper) in [(:Symmetric, :Symmetric), (:SymTridiagonal, :Symmetric), (:Hermitian, :Hermitian)] + @eval begin + function sincos(A::$hermtype{<:Real}) + n = checksquare(A) + F = eigen(A) + S, C = Diagonal(similar(A, (n,))), Diagonal(similar(A, (n,))) + for i in 1:n + S.diag[i], C.diag[i] = sincos(F.values[i]) + end + return $wrapper((F.vectors * S) * F.vectors'), $wrapper((F.vectors * C) * F.vectors') + end end - return Symmetric((F.vectors * S) * F.vectors'), Symmetric((F.vectors * C) * F.vectors') end function sincos(A::Hermitian{<:Complex}) n = checksquare(A) @@ -962,18 +995,20 @@ for func in (:log, :sqrt) # sqrt has rtol arg to handle matrices that are semidefinite up to roundoff errors rtolarg = func === :sqrt ? Any[Expr(:kw, :(rtol::Real), :(eps(real(float(one(T))))*size(A,1)))] : Any[] rtolval = func === :sqrt ? :(-maximum(abs, F.values) * rtol) : 0 - @eval begin - function ($func)(A::HermOrSym{T}; $(rtolarg...)) where {T<:Real} - F = eigen(A) - λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff - if all(λ -> λ ≥ λ₀, F.values) - retmat = (F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors' - else - retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' + for (hermtype, wrapper) in [(:Symmetric, :Symmetric), (:SymTridiagonal, :Symmetric), (:Hermitian, :Hermitian)] + @eval begin + function ($func)(A::$hermtype{T}; $(rtolarg...)) where {T<:Real} + F = eigen(A) + λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff + if all(λ -> λ ≥ λ₀, F.values) + return $wrapper((F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors') + else + return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors') + end end - return Symmetric(retmat) end - + end + @eval begin function ($func)(A::Hermitian{T}; $(rtolarg...)) where {T<:Complex} n = checksquare(A) F = eigen(A) @@ -992,13 +1027,6 @@ for func in (:log, :sqrt) end end -# Cube root of a real-valued symmetric matrix -function cbrt(A::HermOrSym{<:Real}) - F = eigen(A) - A = F.vectors * Diagonal(cbrt.(F.values)) * F.vectors' - return A -end - """ hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian From 57e3c9e4bfd2bc3d54a4923066ed2c3f087b1311 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 7 Oct 2024 02:31:59 -0400 Subject: [PATCH 331/548] Fix no-arg `ScopedValues.@with` within a scope (#56019) Fixes https://github.com/JuliaLang/julia/issues/56017 --- base/scopedvalues.jl | 2 ++ test/scopedvalues.jl | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/base/scopedvalues.jl b/base/scopedvalues.jl index 6ccd4687c5c65..39e3c2c076718 100644 --- a/base/scopedvalues.jl +++ b/base/scopedvalues.jl @@ -85,6 +85,8 @@ struct Scope values::ScopeStorage end +Scope(scope::Scope) = scope + function Scope(parent::Union{Nothing, Scope}, key::ScopedValue{T}, value) where T val = convert(T, value) if parent === nothing diff --git a/test/scopedvalues.jl b/test/scopedvalues.jl index 61b10c557c455..2c2f4a510c1c9 100644 --- a/test/scopedvalues.jl +++ b/test/scopedvalues.jl @@ -138,6 +138,12 @@ end @test sval[] == 1 @test sval_float[] == 1.0 end + @with sval=>2 sval_float=>2.0 begin + @with begin + @test sval[] == 2 + @test sval_float[] == 2.0 + end + end end @testset "isassigned" begin From 87acb9e029c8d0f1a83223737b7539e1db4e4b33 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 7 Oct 2024 17:27:12 +0530 Subject: [PATCH 332/548] LinearAlgebra: make matprod_dest public (#55537) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, in a matrix multiplication `A * B`, we use `B` to construct the destination. However, this may not produce the optimal destination type, and is essentially single-dispatch. Letting packages specialize `matprod_dest` would help us obtain the optimal type by dispatching on both the arguments. This may significantly improve performance in the matrix multiplication. As an example: ```julia julia> using LinearAlgebra, FillArrays, SparseArrays julia> F = Fill(3, 10, 10); julia> s = sprand(10, 10, 0.1); julia> @btime $F * $s; 15.225 μs (10 allocations: 4.14 KiB) julia> typeof(F * s) SparseMatrixCSC{Float64, Int64} julia> nnz(F * s) 80 julia> VERSION v"1.12.0-DEV.1074" ``` In this case, the destination is a sparse matrix with 80% of its elements filled and being set one-by-one, which is terrible for performance. Instead, if we specialize `matprod_dest` to return a dense destination, we may obtain ```julia julia> LinearAlgebra.matprod_dest(F::FillArrays.AbstractFill, S::SparseMatrixCSC, ::Type{T}) where {T} = Matrix{T}(undef, size(F,1), size(S,2)) julia> @btime $F * $s; 754.632 ns (2 allocations: 944 bytes) julia> typeof(F * s) Matrix{Float64} ``` Potentially, this may be improved further by specializing `mul!`, but this is a 20x improvement just by choosing the right destination. Since this is being made public, we may want to bikeshed on an appropriate name for the function. --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 3 ++- stdlib/LinearAlgebra/src/matmul.jl | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 3ecb714a6cfe1..49d73127ba7ba 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -174,7 +174,8 @@ public AbstractTriangular, isbanded, peakflops, symmetric, - symmetric_type + symmetric_type, + matprod_dest const BlasFloat = Union{Float64,Float32,ComplexF64,ComplexF32} const BlasReal = Union{Float64,Float32} diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 9a232d3ad1e51..b70f7d47b28dd 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -123,7 +123,15 @@ function (*)(A::AbstractMatrix, B::AbstractMatrix) mul!(matprod_dest(A, B, TS), A, B) end -matprod_dest(A, B, TS) = similar(B, TS, (size(A, 1), size(B, 2))) +""" + matprod_dest(A, B, T) + +Return an appropriate `AbstractArray` with element type `T` that may be used to store the result of `A * B`. + +!!! compat + This function requires at least Julia 1.11 +""" +matprod_dest(A, B, T) = similar(B, T, (size(A, 1), size(B, 2))) # optimization for dispatching to BLAS, e.g. *(::Matrix{Float32}, ::Matrix{Float64}) # but avoiding the case *(::Matrix{<:BlasComplex}, ::Matrix{<:BlasReal}) From c7071e1eb2369211cf02a1e7dbae365f5fba3fc9 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Mon, 7 Oct 2024 16:01:23 +0200 Subject: [PATCH 333/548] Sockets: Warn when local network access not granted. (#56023) Works around https://github.com/JuliaLang/julia/issues/56022 --- stdlib/Sockets/test/runtests.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 2c50b4a0f8b4a..02a290ddbaa63 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -453,6 +453,8 @@ end catch e if isa(e, Base.IOError) && Base.uverrorname(e.code) == "EPERM" @warn "UDP IPv4 broadcast test skipped (permission denied upon send, restrictive firewall?)" + elseif Sys.isapple() && isa(e, Base.IOError) && Base.uverrorname(e.code) == "EHOSTUNREACH" + @warn "UDP IPv4 broadcast test skipped (local network access not granded?)" else rethrow() end From d4987a368cee336d9d8f3b7baeb54a89c5024e0a Mon Sep 17 00:00:00 2001 From: Zentrik Date: Mon, 7 Oct 2024 19:37:43 +0100 Subject: [PATCH 334/548] Update test due to switch to intel syntax by default in #48103 (#55993) --- stdlib/InteractiveUtils/test/runtests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 8e7090cb53020..851391ec6c249 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -547,9 +547,9 @@ if Sys.ARCH === :x86_64 || occursin(ix86, string(Sys.ARCH)) output = replace(String(take!(buf)), r"#[^\r\n]+" => "") @test !occursin(rgx, output) - code_native(buf, linear_foo, ()) - output = String(take!(buf)) - @test occursin(rgx, output) + code_native(buf, linear_foo, (), debuginfo = :none) + output = replace(String(take!(buf)), r"#[^\r\n]+" => "") + @test !occursin(rgx, output) @testset "binary" begin # check the RET instruction (opcode: C3) From 4cdd864e535b16e928c2eff43e6f1583bd77209d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 7 Oct 2024 19:54:51 -0400 Subject: [PATCH 335/548] add require_lock call to maybe_loaded_precompile (#56027) If we expect this to be a public API (https://github.com/timholy/Revise.jl for some reason is trying to access this state), we should lock around it for consistency with the other similar functions. Needed for https://github.com/timholy/Revise.jl/pull/856 --- base/loading.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index 4eac272848baf..fe4a4770628da 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2397,12 +2397,13 @@ const module_keys = IdDict{Module,PkgId}() # the reverse of loaded_modules root_module_key(m::Module) = @lock require_lock module_keys[m] function maybe_loaded_precompile(key::PkgId, buildid::UInt128) - assert_havelock(require_lock) + @lock require_lock begin mods = get(loaded_precompiles, key, nothing) mods === nothing && return for mod in mods module_build_id(mod) == buildid && return mod end + end end function module_build_id(m::Module) From 24555b81c81e12d19aa59a07b9eb634393f32fcb Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Tue, 8 Oct 2024 15:25:00 +0200 Subject: [PATCH 336/548] fix `power_by_squaring`: use `promote` instead of type inference (#55634) Fixes #53504 Fixes #55633 --- base/intfuncs.jl | 6 +++++- test/math.jl | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 06a0213e7141c..0421877a16623 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -298,7 +298,11 @@ function invmod(n::T) where {T<:BitInteger} end # ^ for any x supporting * -to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) +function to_power_type(x::Number) + T = promote_type(typeof(x), typeof(one(x)), typeof(x*x)) + convert(T, x) +end +to_power_type(x) = oftype(x*x, x) @noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, LazyString( "Cannot raise an integer x to a negative power ", p, ".", "\nConvert input to float."))) diff --git a/test/math.jl b/test/math.jl index c0a2d8bf8c9f8..5a9f3248e59f4 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1498,6 +1498,28 @@ end n = Int64(1024 / log2(E)) @test E^n == Inf @test E^float(n) == Inf + + # #55633 + struct Issue55633_1 <: Number end + struct Issue55633_3 <: Number end + struct Issue55633_9 <: Number end + Base.one(::Issue55633_3) = Issue55633_1() + Base.:(*)(::Issue55633_3, ::Issue55633_3) = Issue55633_9() + Base.promote_rule(::Type{Issue55633_1}, ::Type{Issue55633_3}) = Int + Base.promote_rule(::Type{Issue55633_3}, ::Type{Issue55633_9}) = Int + Base.promote_rule(::Type{Issue55633_1}, ::Type{Issue55633_9}) = Int + Base.promote_rule(::Type{Issue55633_1}, ::Type{Int}) = Int + Base.promote_rule(::Type{Issue55633_3}, ::Type{Int}) = Int + Base.promote_rule(::Type{Issue55633_9}, ::Type{Int}) = Int + Base.convert(::Type{Int}, ::Issue55633_1) = 1 + Base.convert(::Type{Int}, ::Issue55633_3) = 3 + Base.convert(::Type{Int}, ::Issue55633_9) = 9 + for x ∈ (im, pi, Issue55633_3()) + p = promote(one(x), x, x*x) + for y ∈ 0:2 + @test all((t -> ===(t...)), zip(x^y, p[y + 1])) + end + end end # Test that sqrt behaves correctly and doesn't exhibit fp80 double rounding. From 8d515ed8ad443d4bb8c530468d5b7ae0f3b50a0b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 8 Oct 2024 13:03:32 -0400 Subject: [PATCH 337/548] Don't show keymap `@error` for hints (#56041) It's too disruptive to show errors for hints. The error will still be shown if tab is pressed. Helps issues like https://github.com/JuliaLang/julia/issues/56037 --- stdlib/REPL/src/LineEdit.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 5af03e0df9b6d..c92dca8c8e015 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -382,7 +382,13 @@ function check_for_hint(s::MIState) # Requires making space for them earlier in refresh_multi_line return clear_hint(st) end - completions, partial, should_complete = complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} + + completions, partial, should_complete = try + complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} + catch + @debug "error completing line for hint" exception=current_exceptions() + return clear_hint(st) + end isempty(completions) && return clear_hint(st) # Don't complete for single chars, given e.g. `x` completes to `xor` if length(partial) > 1 && should_complete From e516e4c63d383efb24613d27e6fab0ffffed5a88 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 9 Oct 2024 07:35:48 +0200 Subject: [PATCH 338/548] Fix typo in sockets tests. (#56038) --- stdlib/Sockets/test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 02a290ddbaa63..778d9f7415bcc 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -454,7 +454,7 @@ end if isa(e, Base.IOError) && Base.uverrorname(e.code) == "EPERM" @warn "UDP IPv4 broadcast test skipped (permission denied upon send, restrictive firewall?)" elseif Sys.isapple() && isa(e, Base.IOError) && Base.uverrorname(e.code) == "EHOSTUNREACH" - @warn "UDP IPv4 broadcast test skipped (local network access not granded?)" + @warn "UDP IPv4 broadcast test skipped (local network access not granted?)" else rethrow() end From 5117d042b3488eebc929fdfceba98562e18c5ac0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:38:32 +0900 Subject: [PATCH 339/548] EA: use `is_mutation_free_argtype` for the escapability check (#56028) EA has been using `isbitstype` for type-level escapability checks, but a better criterion (`is_mutation_free`) is available these days, so we would like to use that instead. --- .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 10 +- .../compiler/EscapeAnalysis/EscapeAnalysis.jl | 116 +++++++++--------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index 0ad55d6fbcd9e..a0abacb617085 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -24,10 +24,10 @@ using ._TOP_MOD: # Base definitions isempty, ismutabletype, keys, last, length, max, min, missing, pop!, push!, pushfirst!, unwrap_unionall, !, !=, !==, &, *, +, -, :, <, <<, =>, >, |, ∈, ∉, ∩, ∪, ≠, ≤, ≥, ⊆ using Core.Compiler: # Core.Compiler specific definitions - Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice, + AbstractLattice, Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice, argextype, fieldcount_noerror, hasintersect, has_flag, intrinsic_nothrow, - is_meta_expr_head, isbitstype, isexpr, println, setfield!_nothrow, singleton_type, - try_compute_field, try_compute_fieldidx, widenconst, ⊑, AbstractLattice + is_meta_expr_head, is_mutation_free_argtype, isexpr, println, setfield!_nothrow, + singleton_type, try_compute_field, try_compute_fieldidx, widenconst, ⊑ include(x) = _TOP_MOD.include(@__MODULE__, x) if _TOP_MOD === Core.Compiler @@ -859,7 +859,7 @@ function add_escape_change!(astate::AnalysisState, @nospecialize(x), xinfo::Esca xinfo === ⊥ && return nothing # performance optimization xidx = iridx(x, astate.estate) if xidx !== nothing - if force || !isbitstype(widenconst(argextype(x, astate.ir))) + if force || !is_mutation_free_argtype(argextype(x, astate.ir)) push!(astate.changes, EscapeChange(xidx, xinfo)) end end @@ -869,7 +869,7 @@ end function add_liveness_change!(astate::AnalysisState, @nospecialize(x), livepc::Int) xidx = iridx(x, astate.estate) if xidx !== nothing - if !isbitstype(widenconst(argextype(x, astate.ir))) + if !is_mutation_free_argtype(argextype(x, astate.ir)) push!(astate.changes, LivenessChange(xidx, livepc)) end end diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index 99bd86228f50a..9afe49c01562d 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -290,7 +290,7 @@ end let # typeassert result = code_escapes((Any,)) do x - y = x::String + y = x::Base.RefValue{Any} return y end r = only(findall(isreturn, result.ir.stmts.stmt)) @@ -305,11 +305,6 @@ end r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) @test !has_all_escape(result.state[Argument(2)]) - - result = code_escapes((Module,)) do m - isdefined(m, 10) # throws - end - @test has_thrown_escape(result.state[Argument(2)]) end end @@ -685,8 +680,8 @@ end @test has_all_escape(result.state[Argument(2)]) end let result = @eval EATModule() begin - const Rx = SafeRef{String}("Rx") - $code_escapes((String,)) do s + const Rx = SafeRef{Any}(nothing) + $code_escapes((Base.RefValue{String},)) do s setfield!(Rx, :x, s) Core.sizeof(Rx[]) end @@ -712,7 +707,7 @@ end # ------------ # field escape should propagate to :new arguments - let result = code_escapes((String,)) do a + let result = code_escapes((Base.RefValue{String},)) do a o = SafeRef(a) Core.donotdelete(o) return o[] @@ -722,7 +717,7 @@ end @test has_return_escape(result.state[Argument(2)], r) @test is_load_forwardable(result.state[SSAValue(i)]) end - let result = code_escapes((String,)) do a + let result = code_escapes((Base.RefValue{String},)) do a t = SafeRef((a,)) f = t[][1] return f @@ -731,9 +726,8 @@ end r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) @test is_load_forwardable(result.state[SSAValue(i)]) - result.state[SSAValue(i)].AliasInfo end - let result = code_escapes((String, String)) do a, b + let result = code_escapes((Base.RefValue{String}, Base.RefValue{String})) do a, b obj = SafeRefs(a, b) Core.donotdelete(obj) fld1 = obj[1] @@ -748,31 +742,31 @@ end end # field escape should propagate to `setfield!` argument - let result = code_escapes((String,)) do a - o = SafeRef("foo") + let result = code_escapes((Base.RefValue{String},)) do a + o = SafeRef(Ref("foo")) Core.donotdelete(o) o[] = a return o[] end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) @test is_load_forwardable(result.state[SSAValue(i)]) end # propagate escape information imposed on return value of `setfield!` call - let result = code_escapes((String,)) do a - obj = SafeRef("foo") + let result = code_escapes((Base.RefValue{String},)) do a + obj = SafeRef(Ref("foo")) Core.donotdelete(obj) return (obj[] = a) end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) @test is_load_forwardable(result.state[SSAValue(i)]) end # nested allocations - let result = code_escapes((String,)) do a + let result = code_escapes((Base.RefValue{String},)) do a o1 = SafeRef(a) o2 = SafeRef(o1) return o2[] @@ -787,7 +781,7 @@ end end end end - let result = code_escapes((String,)) do a + let result = code_escapes((Base.RefValue{String},)) do a o1 = (a,) o2 = (o1,) return o2[1] @@ -802,7 +796,7 @@ end end end end - let result = code_escapes((String,)) do a + let result = code_escapes((Base.RefValue{String},)) do a o1 = SafeRef(a) o2 = SafeRef(o1) o1′ = o2[] @@ -844,7 +838,7 @@ end @test has_return_escape(result.state[SSAValue(i)], r) end end - let result = code_escapes((String,)) do x + let result = code_escapes((Base.RefValue{String},)) do x o = Ref(x) Core.donotdelete(o) broadcast(identity, o) @@ -892,7 +886,7 @@ end end end # when ϕ-node merges values with different types - let result = code_escapes((Bool,String,String,String)) do cond, x, y, z + let result = code_escapes((Bool,Base.RefValue{String},Base.RefValue{String},Base.RefValue{String})) do cond, x, y, z local out if cond ϕ = SafeRef(x) @@ -904,7 +898,7 @@ end end r = only(findall(isreturn, result.ir.stmts.stmt)) t = only(findall(iscall((result.ir, throw)), result.ir.stmts.stmt)) - ϕ = only(findall(==(Union{SafeRef{String},SafeRefs{String,String}}), result.ir.stmts.type)) + ϕ = only(findall(==(Union{SafeRef{Base.RefValue{String}},SafeRefs{Base.RefValue{String},Base.RefValue{String}}}), result.ir.stmts.type)) @test has_return_escape(result.state[Argument(3)], r) # x @test !has_return_escape(result.state[Argument(4)], r) # y @test has_return_escape(result.state[Argument(5)], r) # z @@ -1038,7 +1032,7 @@ end end # alias via typeassert let result = code_escapes((Any,)) do a - r = a::String + r = a::Base.RefValue{String} return r end r = only(findall(isreturn, result.ir.stmts.stmt)) @@ -1077,11 +1071,11 @@ end @test has_all_escape(result.state[Argument(3)]) # a end # alias via ϕ-node - let result = code_escapes((Bool,String)) do cond, x + let result = code_escapes((Bool,Base.RefValue{String})) do cond, x if cond - ϕ2 = ϕ1 = SafeRef("foo") + ϕ2 = ϕ1 = SafeRef(Ref("foo")) else - ϕ2 = ϕ1 = SafeRef("bar") + ϕ2 = ϕ1 = SafeRef(Ref("bar")) end ϕ2[] = x return ϕ1[] @@ -1094,14 +1088,16 @@ end @test is_load_forwardable(result.state[SSAValue(i)]) end for i in findall(isnew, result.ir.stmts.stmt) - @test is_load_forwardable(result.state[SSAValue(i)]) + if result.ir[SSAValue(i)][:type] <: SafeRef + @test is_load_forwardable(result.state[SSAValue(i)]) + end end end - let result = code_escapes((Bool,Bool,String)) do cond1, cond2, x + let result = code_escapes((Bool,Bool,Base.RefValue{String})) do cond1, cond2, x if cond1 - ϕ2 = ϕ1 = SafeRef("foo") + ϕ2 = ϕ1 = SafeRef(Ref("foo")) else - ϕ2 = ϕ1 = SafeRef("bar") + ϕ2 = ϕ1 = SafeRef(Ref("bar")) end cond2 && (ϕ2[] = x) return ϕ1[] @@ -1114,12 +1110,14 @@ end @test is_load_forwardable(result.state[SSAValue(i)]) end for i in findall(isnew, result.ir.stmts.stmt) - @test is_load_forwardable(result.state[SSAValue(i)]) + if result.ir[SSAValue(i)][:type] <: SafeRef + @test is_load_forwardable(result.state[SSAValue(i)]) + end end end # alias via π-node let result = code_escapes((Any,)) do x - if isa(x, String) + if isa(x, Base.RefValue{String}) return x end throw("error!") @@ -1213,7 +1211,7 @@ end # conservatively handle unknown field: # all fields should be escaped, but the allocation itself doesn't need to be escaped - let result = code_escapes((String, Symbol)) do a, fld + let result = code_escapes((Base.RefValue{String}, Symbol)) do a, fld obj = SafeRef(a) return getfield(obj, fld) end @@ -1222,7 +1220,7 @@ end @test has_return_escape(result.state[Argument(2)], r) # a @test !is_load_forwardable(result.state[SSAValue(i)]) # obj end - let result = code_escapes((String, String, Symbol)) do a, b, fld + let result = code_escapes((Base.RefValue{String}, Base.RefValue{String}, Symbol)) do a, b, fld obj = SafeRefs(a, b) return getfield(obj, fld) # should escape both `a` and `b` end @@ -1232,7 +1230,7 @@ end @test has_return_escape(result.state[Argument(3)], r) # b @test !is_load_forwardable(result.state[SSAValue(i)]) # obj end - let result = code_escapes((String, String, Int)) do a, b, idx + let result = code_escapes((Base.RefValue{String}, Base.RefValue{String}, Int)) do a, b, idx obj = SafeRefs(a, b) return obj[idx] # should escape both `a` and `b` end @@ -1242,33 +1240,33 @@ end @test has_return_escape(result.state[Argument(3)], r) # b @test !is_load_forwardable(result.state[SSAValue(i)]) # obj end - let result = code_escapes((String, String, Symbol)) do a, b, fld - obj = SafeRefs("a", "b") + let result = code_escapes((Base.RefValue{String}, Base.RefValue{String}, Symbol)) do a, b, fld + obj = SafeRefs(Ref("a"), Ref("b")) setfield!(obj, fld, a) return obj[2] # should escape `a` end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) # a @test !has_return_escape(result.state[Argument(3)], r) # b @test !is_load_forwardable(result.state[SSAValue(i)]) # obj end - let result = code_escapes((String, Symbol)) do a, fld - obj = SafeRefs("a", "b") + let result = code_escapes((Base.RefValue{String}, Symbol)) do a, fld + obj = SafeRefs(Ref("a"), Ref("b")) setfield!(obj, fld, a) return obj[1] # this should escape `a` end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) # a @test !is_load_forwardable(result.state[SSAValue(i)]) # obj end - let result = code_escapes((String, String, Int)) do a, b, idx - obj = SafeRefs("a", "b") + let result = code_escapes((Base.RefValue{String}, Base.RefValue{String}, Int)) do a, b, idx + obj = SafeRefs(Ref("a"), Ref("b")) obj[idx] = a return obj[2] # should escape `a` end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[Argument(2)], r) # a @test !has_return_escape(result.state[Argument(3)], r) # b @@ -1280,7 +1278,7 @@ end let result = @eval EATModule() begin @noinline getx(obj) = obj[] - $code_escapes((String,)) do a + $code_escapes((Base.RefValue{String},)) do a obj = SafeRef(a) fld = getx(obj) return fld @@ -1294,8 +1292,8 @@ end end # TODO interprocedural alias analysis - let result = code_escapes((SafeRef{String},)) do s - s[] = "bar" + let result = code_escapes((SafeRef{Base.RefValue{String}},)) do s + s[] = Ref("bar") global GV = s[] nothing end @@ -1335,7 +1333,7 @@ end let result = @eval EATModule() begin @noinline mysetindex!(x, a) = x[1] = a const Ax = Vector{Any}(undef, 1) - $code_escapes((String,)) do s + $code_escapes((Base.RefValue{String},)) do s mysetindex!(Ax, s) end end @@ -1391,11 +1389,11 @@ end end # handle conflicting field information correctly - let result = code_escapes((Bool,String,String,)) do cnd, baz, qux + let result = code_escapes((Bool,Base.RefValue{String},Base.RefValue{String},)) do cnd, baz, qux if cnd - o = SafeRef("foo") + o = SafeRef(Ref("foo")) else - o = SafeRefs("bar", baz) + o = SafeRefs(Ref("bar"), baz) r = getfield(o, 2) end if cnd @@ -1409,12 +1407,14 @@ end @test has_return_escape(result.state[Argument(3)], r) # baz @test has_return_escape(result.state[Argument(4)], r) # qux for new in findall(isnew, result.ir.stmts.stmt) - @test is_load_forwardable(result.state[SSAValue(new)]) + if !(result.ir[SSAValue(new)][:type] <: Base.RefValue) + @test is_load_forwardable(result.state[SSAValue(new)]) + end end end - let result = code_escapes((Bool,String,String,)) do cnd, baz, qux + let result = code_escapes((Bool,Base.RefValue{String},Base.RefValue{String},)) do cnd, baz, qux if cnd - o = SafeRefs("foo", "bar") + o = SafeRefs(Ref("foo"), Ref("bar")) r = setfield!(o, 2, baz) else o = SafeRef(qux) @@ -2141,9 +2141,9 @@ end # propagate escapes imposed on call arguments @noinline broadcast_noescape2(b) = broadcast(identity, b) let result = code_escapes() do - broadcast_noescape2(Ref("Hi")) + broadcast_noescape2(Ref(Ref("Hi"))) end - i = only(findall(isnew, result.ir.stmts.stmt)) + i = last(findall(isnew, result.ir.stmts.stmt)) @test_broken !has_return_escape(result.state[SSAValue(i)]) # TODO interprocedural alias analysis @test !has_thrown_escape(result.state[SSAValue(i)]) end From ecf41b18cce0e41455a8144f220e9e6171987194 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 9 Oct 2024 06:03:41 -0400 Subject: [PATCH 340/548] effects: fix `Base.@_noub_meta` (#56061) This had the incorrect number of arguments to `Expr(:purity, ...)` causing it to be silently ignored. --- base/essentials.jl | 3 ++- src/method.c | 2 ++ test/compiler/effects.jl | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 32c44a9571f23..0e7be924c908c 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -337,7 +337,8 @@ macro _noub_meta() #=:inaccessiblememonly=#false, #=:noub=#true, #=:noub_if_noinbounds=#false, - #=:consistent_overlay=#false)) + #=:consistent_overlay=#false, + #=:nortcall=#false)) end # can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping) macro _notaskstate_meta() diff --git a/src/method.c b/src/method.c index 6aba60e7fe12c..629816319b334 100644 --- a/src/method.c +++ b/src/method.c @@ -491,6 +491,8 @@ jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ir) if (consistent_overlay) li->purity.overrides.ipo_consistent_overlay = consistent_overlay; int8_t nortcall = jl_unbox_bool(jl_exprarg(ma, 10)); if (nortcall) li->purity.overrides.ipo_nortcall = nortcall; + } else { + assert(jl_expr_nargs(ma) == 0); } } else diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 8bc5f27e31766..c8a699b294d37 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -810,7 +810,12 @@ end # @test !Core.Compiler.is_nothrow(effects) # end #end -# + +@test Core.Compiler.is_noub(Base.infer_effects(Base._growbeg!, (Vector{Int}, Int))) +@test Core.Compiler.is_noub(Base.infer_effects(Base._growbeg!, (Vector{Any}, Int))) +@test Core.Compiler.is_noub(Base.infer_effects(Base._growend!, (Vector{Int}, Int))) +@test Core.Compiler.is_noub(Base.infer_effects(Base._growend!, (Vector{Any}, Int))) + # tuple indexing # -------------- From 18046c2c41d4a04a9a9a59d26e230989260f0502 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:10:34 -0400 Subject: [PATCH 341/548] effects: improve `:noub_if_noinbounds` documentation (#56060) Just a small touch-up --- base/compiler/effects.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 7778c96e019e5..b22b9396408e3 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -47,7 +47,8 @@ following meanings: * `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. + under the assumption that its `@checkbounds` code is not elided (which happens when the + caller does not set nor propagate the `@inbounds` context) 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. From 38dbd1149be625ab478229f4532d7378e474981b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 9 Oct 2024 18:09:04 +0530 Subject: [PATCH 342/548] Disallow assigning asymmetric values to SymTridiagonal (#56068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we can assign an asymmetric value to a `SymTridiagonal`, which goes against what `setindex!` is expected to do. This is because `SymTridiagonal` symmetrizes the values along the diagonal, so setting a diagonal entry to an asymmetric value would lead to a subsequent `getindex` producing a different result. ```julia julia> s = SMatrix{2,2}(1:4); julia> S = SymTridiagonal(fill(s,4), fill(s,3)) 4×4 SymTridiagonal{SMatrix{2, 2, Int64, 4}, Vector{SMatrix{2, 2, Int64, 4}}}: [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] [1 3; 2 4] ⋅ ⋅ [1 2; 3 4] [1 3; 3 4] julia> S[1,1] = s 2×2 SMatrix{2, 2, Int64, 4} with indices SOneTo(2)×SOneTo(2): 1 3 2 4 julia> S[1,1] == s false julia> S[1,1] 2×2 Symmetric{Int64, SMatrix{2, 2, Int64, 4}} with indices SOneTo(2)×SOneTo(2): 1 3 3 4 ``` After this PR, ```julia julia> S[1,1] = s ERROR: ArgumentError: cannot set a diagonal entry of a SymTridiagonal to an asymmetric value ``` --- stdlib/LinearAlgebra/src/tridiag.jl | 1 + stdlib/LinearAlgebra/test/tridiag.jl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index ca61eb8519d42..f5df1a4e6a895 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -497,6 +497,7 @@ Base._reverse!(A::SymTridiagonal, dims::Colon) = (reverse!(A.dv); reverse!(A.ev) @inline function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) if i == j + issymmetric(x) || throw(ArgumentError("cannot set a diagonal entry of a SymTridiagonal to an asymmetric value")) @inbounds A.dv[i] = x else throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j)")) diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 826a6e62355d0..b6e93341b1946 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -505,6 +505,9 @@ end @test_throws ArgumentError diag(A, 2) @test_throws ArgumentError diag(A, n+1) @test_throws ArgumentError diag(A, -n-1) + A[1,1] = Symmetric(2M) + @test A[1,1] == Symmetric(2M) + @test_throws ArgumentError A[1,1] = M @test tr(A) == sum(diag(A)) @test issymmetric(tr(A)) From 9c5578315643b356825327613a27f6faaf14c494 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 9 Oct 2024 18:19:12 +0530 Subject: [PATCH 343/548] Remove unused matrix type params in diag methods (#56048) These parameters are not used in the method, and are unnecessary for dispatch. --- stdlib/LinearAlgebra/src/bidiag.jl | 2 +- stdlib/LinearAlgebra/src/diagonal.jl | 2 +- stdlib/LinearAlgebra/src/tridiag.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index e5482cbba5595..c600de147aa39 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -402,7 +402,7 @@ function triu!(M::Bidiagonal{T}, k::Integer=0) where T return M end -function diag(M::Bidiagonal{T}, n::Integer=0) where T +function diag(M::Bidiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n if n == 0 diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 0a95bac5ffb93..dba4d9da79708 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -751,7 +751,7 @@ adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) permutedims(D::Diagonal) = D permutedims(D::Diagonal, perm) = (Base.checkdims_perm(axes(D), axes(D), perm); D) -function diag(D::Diagonal{T}, k::Integer=0) where T +function diag(D::Diagonal, k::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of k if k == 0 diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index f5df1a4e6a895..e75e1e5eefb3d 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -676,7 +676,7 @@ issymmetric(S::Tridiagonal) = all(issymmetric, S.d) && all(Iterators.map((x, y) \(A::Adjoint{<:Any,<:Tridiagonal}, B::Adjoint{<:Any,<:AbstractVecOrMat}) = copy(A) \ B -function diag(M::Tridiagonal{T}, n::Integer=0) where T +function diag(M::Tridiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n if n == 0 From 91da4bf9323b79e00e446d6471d3b43d4c8ee4c4 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 9 Oct 2024 18:21:40 +0530 Subject: [PATCH 344/548] LinearAlgebra: diagzero for non-OneTo axes (#55252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the off-diagonal zeros for a block-`Diagonal` matrix is computed using `diagzero`, which calls `zeros` for the sizes of the elements. This returns an `Array`, unless one specializes `diagzero` for the custom `Diagonal` matrix type. This PR defines a `zeroslike` function that dispatches on the axes of the elements, which lets packages specialize on the axes to return custom `AbstractArray`s. Choosing to specialize on the `eltype` avoids the need to specialize on the container, and allows packages to return appropriate types for custom axis types. With this, ```julia julia> LinearAlgebra.zeroslike(::Type{S}, ax::Tuple{SOneTo, Vararg{SOneTo}}) where {S<:SMatrix} = SMatrix{map(length, ax)...}(ntuple(_->zero(eltype(S)), prod(length, ax))) julia> D = Diagonal(fill(SMatrix{2,3}(1:6), 2)) 2×2 Diagonal{SMatrix{2, 3, Int64, 6}, Vector{SMatrix{2, 3, Int64, 6}}}: [1 3 5; 2 4 6] ⋅ ⋅ [1 3 5; 2 4 6] julia> D[1,2] # now an SMatrix 2×3 SMatrix{2, 3, Int64, 6} with indices SOneTo(2)×SOneTo(3): 0 0 0 0 0 0 julia> LinearAlgebra.zeroslike(::Type{S}, ax::Tuple{SOneTo, Vararg{SOneTo}}) where {S<:MMatrix} = MMatrix{map(length, ax)...}(ntuple(_->zero(eltype(S)), prod(length, ax))) julia> D = Diagonal(fill(MMatrix{2,3}(1:6), 2)) 2×2 Diagonal{MMatrix{2, 3, Int64, 6}, Vector{MMatrix{2, 3, Int64, 6}}}: [1 3 5; 2 4 6] ⋅ ⋅ [1 3 5; 2 4 6] julia> D[1,2] # now an MMatrix 2×3 MMatrix{2, 3, Int64, 6} with indices SOneTo(2)×SOneTo(3): 0 0 0 0 0 0 ``` The reason this can't be the default behavior is that we are not guaranteed that there exists a `similar` method that accepts the combination of axes. This is why we have to fall back to using the sizes, unless a specialized method is provided by a package. One positive outcome of this is that indexing into such a block-diagonal matrix will now usually be type-stable, which mitigates https://github.com/JuliaLang/julia/issues/45535 to some extent (although it doesn't resolve the issue). I've also updated the `getindex` for `Bidiagonal` to use `diagzero`, instead of the similarly defined `bidiagzero` function that it was using. Structured block matrices may now use `diagzero` uniformly to generate the zero elements. --- NEWS.md | 2 ++ stdlib/LinearAlgebra/src/LinearAlgebra.jl | 1 + stdlib/LinearAlgebra/src/bidiag.jl | 15 +++++++-------- stdlib/LinearAlgebra/src/diagonal.jl | 23 +++++++++++++++++++++-- stdlib/LinearAlgebra/test/bidiag.jl | 10 ++++++++++ stdlib/LinearAlgebra/test/diagonal.jl | 7 +++++++ test/testhelpers/SizedArrays.jl | 3 +++ 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index bb22c9f940a78..9aebf5d42d954 100644 --- a/NEWS.md +++ b/NEWS.md @@ -138,6 +138,8 @@ Standard library changes (callable via `cholesky[!](A, RowMaximum())`) ([#54619]). * The number of default BLAS threads now respects process affinity, instead of using total number of logical threads available on the system ([#55574]). +* A new function `zeroslike` is added that is used to generate the zero elements for matrix-valued banded matrices. + Custom array types may specialize this function to return an appropriate result. ([#55252]) #### Logging diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 49d73127ba7ba..15354603943c2 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -175,6 +175,7 @@ public AbstractTriangular, peakflops, symmetric, symmetric_type, + zeroslike, matprod_dest const BlasFloat = Union{Float64,Float32,ComplexF64,ComplexF32} diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index c600de147aa39..381afd2f09a61 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -118,15 +118,14 @@ Bidiagonal(A::Bidiagonal) = A Bidiagonal{T}(A::Bidiagonal{T}) where {T} = A Bidiagonal{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A.dv, A.ev, A.uplo) -bidiagzero(::Bidiagonal{T}, i, j) where {T} = zero(T) -function bidiagzero(A::Bidiagonal{<:AbstractMatrix}, i, j) - Tel = eltype(eltype(A.dv)) +function diagzero(A::Bidiagonal{<:AbstractMatrix}, i, j) + Tel = eltype(A) if i < j && A.uplo == 'U' #= top right zeros =# - return zeros(Tel, size(A.ev[i], 1), size(A.ev[j-1], 2)) + return zeroslike(Tel, axes(A.ev[i], 1), axes(A.ev[j-1], 2)) elseif j < i && A.uplo == 'L' #= bottom left zeros =# - return zeros(Tel, size(A.ev[i-1], 1), size(A.ev[j], 2)) + return zeroslike(Tel, axes(A.ev[i-1], 1), axes(A.ev[j], 2)) else - return zeros(Tel, size(A.dv[i], 1), size(A.dv[j], 2)) + return zeroslike(Tel, axes(A.dv[i], 1), axes(A.dv[j], 2)) end end @@ -161,7 +160,7 @@ end elseif i == j - _offdiagind(A.uplo) return @inbounds A.ev[A.uplo == 'U' ? i : j] else - return bidiagzero(A, i, j) + return diagzero(A, i, j) end end @@ -173,7 +172,7 @@ end # we explicitly compare the possible bands as b.band may be constant-propagated return @inbounds A.ev[b.index] else - return bidiagzero(A, Tuple(_cartinds(b))...) + return diagzero(A, Tuple(_cartinds(b))...) end end diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index dba4d9da79708..0c93024f33a9a 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -185,8 +185,27 @@ end end r end -diagzero(::Diagonal{T}, i, j) where {T} = zero(T) -diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) +""" + diagzero(A::AbstractMatrix, i, j) + +Return the appropriate zero element `A[i, j]` corresponding to a banded matrix `A`. +""" +diagzero(A::AbstractMatrix, i, j) = zero(eltype(A)) +diagzero(D::Diagonal{M}, i, j) where {M<:AbstractMatrix} = + zeroslike(M, axes(D.diag[i], 1), axes(D.diag[j], 2)) +# dispatching on the axes permits specializing on the axis types to return something other than an Array +zeroslike(M::Type, ax::Vararg{Union{AbstractUnitRange, Integer}}) = zeroslike(M, ax) +""" + zeroslike(::Type{M}, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) where {M<:AbstractMatrix} + zeroslike(::Type{M}, sz::Tuple{Integer, Vararg{Integer}}) where {M<:AbstractMatrix} + +Return an appropriate zero-ed array similar to `M`, with either the axes `ax` or the size `sz`. +This will be used as a structural zero element of a matrix-valued banded matrix. +By default, `zeroslike` falls back to using the size along each axis to construct the array. +""" +zeroslike(M::Type, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) = zeroslike(M, map(length, ax)) +zeroslike(M::Type, sz::Tuple{Integer, Vararg{Integer}}) = zeros(M, sz) +zeroslike(::Type{M}, sz::Tuple{Integer, Vararg{Integer}}) where {M<:AbstractMatrix} = zeros(eltype(M), sz) @inline function getindex(D::Diagonal, b::BandIndex) @boundscheck checkbounds(D, b) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index d633a99a2390e..628e59debe8b7 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -839,6 +839,16 @@ end B = Bidiagonal(dv, ev, :U) @test B == Matrix{eltype(B)}(B) end + + @testset "non-standard axes" begin + LinearAlgebra.diagzero(T::Type, ax::Tuple{SizedArrays.SOneTo, Vararg{SizedArrays.SOneTo}}) = + zeros(T, ax) + + s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) + B = Bidiagonal(fill(s,4), fill(s,3), :U) + @test @inferred(B[2,1]) isa typeof(s) + @test all(iszero, B[2,1]) + end end @testset "copyto!" begin diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 98f5498c71033..85fe963e3592b 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -815,6 +815,13 @@ end D = Diagonal(fill(S,3)) @test D * fill(S,2,3)' == fill(S * S', 3, 2) @test fill(S,3,2)' * D == fill(S' * S, 2, 3) + + @testset "indexing with non-standard-axes" begin + s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) + D = Diagonal(fill(s,3)) + @test @inferred(D[1,2]) isa typeof(s) + @test all(iszero, D[1,2]) + end end @testset "Eigensystem for block diagonal (issue #30681)" begin diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index 495fe03347ee7..e52e965a64859 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -99,4 +99,7 @@ mul!(dest::AbstractMatrix, S1::SizedMatrix, S2::SizedMatrix, α::Number, β::Num mul!(dest::AbstractVector, M::AbstractMatrix, v::SizedVector, α::Number, β::Number) = mul!(dest, M, _data(v), α, β) +LinearAlgebra.zeroslike(::Type{S}, ax::Tuple{SizedArrays.SOneTo, Vararg{SizedArrays.SOneTo}}) where {S<:SizedArray} = + zeros(eltype(S), ax) + end From 44620b62614c653a59ac932fc66782d3fefb09b7 Mon Sep 17 00:00:00 2001 From: Thomas Christensen Date: Wed, 9 Oct 2024 16:28:56 +0200 Subject: [PATCH 345/548] Multi-argument `gcdx(a, b, c...)` (#55935) Previously, `gcdx` only worked for two arguments - but the underlying idea extends to any (nonzero) number of arguments. Similarly, `gcd` already works for 1, 2, 3+ arguments. This PR implements the 1 and 3+ argument versions of `gcdx`, following the [wiki page](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#The_case_of_more_than_two_numbers) for the Extended Euclidean algorithm. --- base/intfuncs.jl | 26 ++++++++++++++++++++++++-- test/rational.jl | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 0421877a16623..ec450aff2dff2 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -165,17 +165,24 @@ end # return (gcd(a, b), x, y) such that ax+by == gcd(a, b) """ - gcdx(a, b) + gcdx(a, b...) Computes the greatest common (positive) divisor of `a` and `b` and their Bézout coefficients, i.e. the integer coefficients `u` and `v` that satisfy -``ua+vb = d = gcd(a, b)``. ``gcdx(a, b)`` returns ``(d, u, v)``. +``u*a + v*b = d = gcd(a, b)``. ``gcdx(a, b)`` returns ``(d, u, v)``. + +For more arguments than two, i.e., `gcdx(a, b, c, ...)` the Bézout coefficients are computed +recursively, returning a solution `(d, u, v, w, ...)` to +``u*a + v*b + w*c + ... = d = gcd(a, b, c, ...)``. The arguments may be integer and rational numbers. !!! compat "Julia 1.4" Rational arguments require Julia 1.4 or later. +!!! compat "Julia 1.12" + More or fewer arguments than two require Julia 1.12 or later. + # Examples ```jldoctest julia> gcdx(12, 42) @@ -183,6 +190,9 @@ julia> gcdx(12, 42) julia> gcdx(240, 46) (2, -9, 47) + +julia> gcdx(15, 12, 20) +(1, 7, -7, -1) ``` !!! note @@ -215,6 +225,18 @@ Base.@assume_effects :terminates_locally function gcdx(a::Integer, b::Integer) end gcdx(a::Real, b::Real) = gcdx(promote(a,b)...) gcdx(a::T, b::T) where T<:Real = throw(MethodError(gcdx, (a,b))) +gcdx(a::Real) = (gcd(a), signbit(a) ? -one(a) : one(a)) +function gcdx(a::Real, b::Real, cs::Real...) + # a solution to the 3-arg `gcdx(a,b,c)` problem, `u*a + v*b + w*c = gcd(a,b,c)`, can be + # obtained from the 2-arg problem in three steps: + # 1. `gcdx(a,b)`: solve `i*a + j*b = d′ = gcd(a,b)` for `(i,j)` + # 2. `gcdx(d′,c)`: solve `x*gcd(a,b) + yc = gcd(gcd(a,b),c) = gcd(a,b,c)` for `(x,y)` + # 3. return `d = gcd(a,b,c)`, `u = i*x`, `v = j*x`, and `w = y` + # the N-arg solution proceeds similarly by recursion + d, i, j = gcdx(a, b) + d′, x, ys... = gcdx(d, cs...) + return d′, i*x, j*x, ys... +end # multiplicative inverse of n mod m, error if none diff --git a/test/rational.jl b/test/rational.jl index 20a0971068876..90b5414a6fe89 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -702,6 +702,22 @@ end end end +@testset "gcdx for 1 and 3+ arguments" begin + # one-argument + @test gcdx(7) == (7, 1) + @test gcdx(-7) == (7, -1) + @test gcdx(1//4) == (1//4, 1) + + # 3+ arguments + @test gcdx(2//3) == gcdx(2//3) == (2//3, 1) + @test gcdx(15, 12, 20) == (1, 7, -7, -1) + @test gcdx(60//4, 60//5, 60//3) == (1//1, 7, -7, -1) + abcd = (105, 1638, 2145, 3185) + d, uvwp... = gcdx(abcd...) + @test d == sum(abcd .* uvwp) # u*a + v*b + w*c + p*d == gcd(a, b, c, d) + @test (@inferred gcdx(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) isa NTuple{11, Int} +end + @testset "Binary operations with Integer" begin @test 1//2 - 1 == -1//2 @test -1//2 + 1 == 1//2 From d4ca92c2718c952d1bc2807f1096da3b02a4852a Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Wed, 9 Oct 2024 22:31:24 -0400 Subject: [PATCH 346/548] fix `_growbeg!` unncessary resizing (#56029) This was very explicitly designed such that if there was a bunch of extra space at the end of the array, we would copy rather than allocating, but by making `newmemlen` be at least `overallocation(memlen)` rather than `overallocation(len)`, this branch was never hit. found by https://github.com/JuliaLang/julia/issues/56026 --- base/array.jl | 9 +++++++-- test/arrayops.jl | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/base/array.jl b/base/array.jl index 5b3e6cc398479..d8a9c725c8102 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1071,13 +1071,14 @@ function _growbeg!(a::Vector, delta::Integer) setfield!(a, :ref, @inbounds memoryref(ref, 1 - delta)) else @noinline (function() + @_terminates_locally_meta memlen = length(mem) if offset + len - 1 > memlen || offset < 1 throw(ConcurrencyViolationError("Vector has invalid state. Don't modify internal fields incorrectly, or resize without correct locks")) end # since we will allocate the array in the middle of the memory we need at least 2*delta extra space # the +1 is because I didn't want to have an off by 1 error. - newmemlen = max(overallocation(memlen), len + 2 * delta + 1) + newmemlen = max(overallocation(len), len + 2 * delta + 1) newoffset = div(newmemlen - newlen, 2) + 1 # If there is extra data after the end of the array we can use that space so long as there is enough # space at the end that there won't be quadratic behavior with a mix of growth from both ends. @@ -1086,10 +1087,14 @@ function _growbeg!(a::Vector, delta::Integer) if newoffset + newlen < memlen newoffset = div(memlen - newlen, 2) + 1 newmem = mem + unsafe_copyto!(newmem, newoffset + delta, mem, offset, len) + for j in offset:newoffset+delta-1 + @inbounds _unsetindex!(mem, j) + end else newmem = array_new_memory(mem, newmemlen) + unsafe_copyto!(newmem, newoffset + delta, mem, offset, len) end - unsafe_copyto!(newmem, newoffset + delta, mem, offset, len) if ref !== a.ref @noinline throw(ConcurrencyViolationError("Vector can not be resized concurrently")) end diff --git a/test/arrayops.jl b/test/arrayops.jl index 333b68e287c4c..ec8f54828b965 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -496,6 +496,11 @@ end @test vc == [v[1:(i-1)]; 5; v[i:end]] end @test_throws BoundsError insert!(v, 5, 5) + + # test that data is copied when there is plenty of room to do so + v = empty!(collect(1:100)) + pushfirst!(v, 1) + @test length(v.ref.mem) == 100 end @testset "popat!(::Vector, i, [default])" begin From 84a2458e0504d92b3db32bb367e449377c802593 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 10 Oct 2024 03:09:41 -0400 Subject: [PATCH 347/548] REPL: hide any prints to stdio during `complete_line` (#55959) --- stdlib/REPL/src/LineEdit.jl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index c92dca8c8e015..3ac403df54007 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -366,7 +366,14 @@ end # Prompt Completions & Hints function complete_line(s::MIState) set_action!(s, :complete_line) - if complete_line(state(s), s.key_repeats, s.active_module) + # suppress stderr/stdout prints during completion computation + # i.e. ambiguous qualification warnings that are printed to stderr + # TODO: remove this suppression once such warnings are better handled + # TODO: but before that change Pipe to devnull once devnull redirects work for JL_STDERR etc. + completions_exist = redirect_stdio(;stderr=Pipe(), stdout=Pipe()) do + complete_line(state(s), s.key_repeats, s.active_module) + end + if completions_exist return refresh_line(s) else beep(s) @@ -384,7 +391,13 @@ function check_for_hint(s::MIState) end completions, partial, should_complete = try - complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} + # suppress stderr/stdout prints during completion computation + # i.e. ambiguous qualification warnings that are printed to stderr + # TODO: remove this suppression once such warnings are better handled + # TODO: but before that change Pipe to devnull once devnull redirects work for JL_STDERR etc. + completions, partial, should_complete = redirect_stdio(;stderr=Pipe(), stdout=Pipe()) do + complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} + end catch @debug "error completing line for hint" exception=current_exceptions() return clear_hint(st) From ce83853a86ecbbc84078ea707a0395c34bdf3a13 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Thu, 10 Oct 2024 09:36:39 -0400 Subject: [PATCH 348/548] teach llvm-alloc-helpers about `gc_loaded` (#56030) combined with https://github.com/JuliaLang/julia/pull/55913, the compiler is smart enough to fully remove ``` function f() m = Memory{Int}(undef, 3) @inbounds m[1] = 2 @inbounds m[2] = 2 @inbounds m[3] = 4 @inbounds return m[1] + m[2] + m[3] end ``` --- src/llvm-alloc-helpers.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 9d2fba832839c..75030f8565221 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -245,6 +245,11 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r required.use_info.addrescaped = true; return true; } + if (required.pass.gc_loaded_func == callee) { + required.use_info.haspreserve = true; + required.use_info.hasload = true; + return true; + } if (required.pass.typeof_func == callee) { required.use_info.hastypeof = true; assert(use->get() == I); From 6fa4af56fa888b2474618eade27aeab47ddd097f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 10 Oct 2024 09:49:12 -0400 Subject: [PATCH 349/548] mpfr: prevent changing precision (#56049) Changing precision requires reallocating the data field, which is better done by making a new BigFloat (since they are conceptually immutable anyways). Also do a bit a cleanup while here. Closes #56044 --- base/mpfr.jl | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 9d1a0843ebe06..1e39f52b9d1a3 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -142,7 +142,10 @@ struct BigFloat <: AbstractFloat # Not recommended for general use: # used internally by, e.g. deepcopy - global _BigFloat(d::Memory{Limb}) = new(d) + global function _BigFloat(d::Memory{Limb}) + Base.unsafe_convert(Ref{BigFloat}, BigFloatData(d)) # force early initialization of pointer field of z.d + return new(d) + end function BigFloat(; precision::Integer=_precision_with_base_2(BigFloat)) precision < 1 && throw(DomainError(precision, "`precision` cannot be less than 1.")) @@ -150,14 +153,17 @@ struct BigFloat <: AbstractFloat nl = (nb + offset_p + sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this d = Memory{Limb}(undef, nl % Int) # ccall-based version, inlined below - z = _BigFloat(d) # initialize to +NAN #ccall((:mpfr_custom_init,libmpfr), Cvoid, (Ptr{Limb}, Clong), BigFloatData(d), prec) # currently seems to be a no-op in mpfr #NAN_KIND = Cint(0) #ccall((:mpfr_custom_init_set,libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, BigFloatData(d)) - z.prec = Clong(precision) - z.sign = one(Cint) - z.exp = mpfr_special_exponent_nan - return z + p = Base.unsafe_convert(Ptr{Limb}, d) + GC.@preserve d begin # initialize to +NAN + unsafe_store!(Ptr{Clong}(p) + offset_prec, Clong(precision)) + unsafe_store!(Ptr{Cint}(p) + offset_sign, one(Cint)) + unsafe_store!(Ptr{Clong}(p) + offset_exp, mpfr_special_exponent_nan) + unsafe_store!(Ptr{Ptr{Limb}}(p) + offset_d, p + offset_p) + end + return new(d) end end @@ -186,16 +192,16 @@ end end end +# While BigFloat (like all Numbers) is considered immutable, for practical reasons +# of writing the algorithms on it we allow mutating sign, exp, and the contents of d @inline function Base.setproperty!(x::BigFloat, s::Symbol, v) d = getfield(x, :d) p = Base.unsafe_convert(Ptr{Limb}, d) - if s === :prec - return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_prec, v) - elseif s === :sign + if s === :sign return GC.@preserve d unsafe_store!(Ptr{Cint}(p) + offset_sign, v) elseif s === :exp return GC.@preserve d unsafe_store!(Ptr{Clong}(p) + offset_exp, v) - #elseif s === :d # not mutable + #elseif s === :d || s === :prec # not mutable else return throw(FieldError(x, s)) end @@ -208,7 +214,11 @@ Base.cconvert(::Type{Ref{BigFloat}}, x::BigFloat) = x.d # BigFloatData is the Re function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::BigFloatData) d = getfield(x, :d) p = Base.unsafe_convert(Ptr{Limb}, d) - GC.@preserve d unsafe_store!(Ptr{Ptr{Limb}}(p) + offset_d, p + offset_p, :monotonic) # :monotonic ensure that TSAN knows that this isn't a data race + dptrptr = Ptr{Ptr{Limb}}(p) + offset_d + dptr = p + offset_p + GC.@preserve d if unsafe_load(dptrptr, :monotonic) != dptr # make sure this pointer value was recomputed after any deserialization or copying + unsafe_store!(dptrptr, dptr, :monotonic) # :monotonic ensure that TSAN knows that this isn't a data race + end return Ptr{BigFloat}(p) end Base.unsafe_convert(::Type{Ptr{Limb}}, fd::BigFloatData) = Base.unsafe_convert(Ptr{Limb}, getfield(fd, :d)) + offset_p From 224ff57eafd0f34b00648597bfa5a28455a50b23 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 10 Oct 2024 09:57:47 -0400 Subject: [PATCH 350/548] stackwalk: fix jl_thread_suspend_and_get_state race (#56047) There was a missing re-assignment of old = -1; at the end of that loop which means in the ABA case, we accidentally actually acquire the lock on the thread despite not actually having stopped the thread; or in the counter-case, we try to run through this logic with old==-1 on the next iteration, and that isn't valid either (jl_thread_suspend_and_get_state should return failure and the loop will abort too early). Fix #56046 --- src/stackwalk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stackwalk.c b/src/stackwalk.c index 6aa36fa8b499c..7fb4de0372738 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1196,8 +1196,8 @@ JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, } bt_context_t *context = NULL; bt_context_t c; - int16_t old = -1; - while (!jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid) { + int16_t old; + for (old = -1; !jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid; old = -1) { int lockret = jl_lock_stackwalk(); // if this task is already running somewhere, we need to stop the thread it is running on and query its state if (!jl_thread_suspend_and_get_state(old, 1, &c)) { From d60837f29e861fad4afe6c29dba788e44b627bf4 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 10 Oct 2024 16:10:33 +0200 Subject: [PATCH 351/548] irrationals: restrict assume effects annotations to known types (#55886) Other changes: * replace `:total` with the less powerful `:foldable` * add an `<:Integer` dispatch constraint on the `rationalize` method, closes #55872 * replace `Rational{<:Integer}` with just `Rational`, they're equal Other issues, related to `BigFloat` precision, are still present in irrationals.jl, to be fixed by followup PRs, including #55853. Fixes #55874 --- base/irrationals.jl | 22 ++++++++++++++-------- base/mathconstants.jl | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index b3073c503238a..c51b66045723f 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -51,8 +51,7 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) -# XXX this may change `DEFAULT_PRECISION`, thus not effect free -@assume_effects :total function Rational{T}(x::AbstractIrrational) where T<:Integer +function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer o = precision(BigFloat) p = 256 while true @@ -66,13 +65,16 @@ Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) p += 32 end end -Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) +Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x) +_throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) +Rational{BigInt}(::AbstractIrrational) = _throw_argument_error_irrational_to_rational_bigint() -@assume_effects :total function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} +function _irrational_to_float(::Type{T}, x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} setprecision(BigFloat, 256) do T(BigFloat(x)::BigFloat, r) end end +(::Type{T})(x::AbstractIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}} = _irrational_to_float(T, x, r) float(::Type{<:AbstractIrrational}) = Float64 @@ -110,14 +112,18 @@ end <=(x::AbstractFloat, y::AbstractIrrational) = x < y # Irrational vs Rational -@assume_effects :total function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where T +function _rationalize_irrational(::Type{T}, x::AbstractIrrational, tol::Real) where {T<:Integer} return rationalize(T, big(x), tol=tol) end -@assume_effects :total function lessrational(rx::Rational{<:Integer}, x::AbstractIrrational) - # an @assume_effects :total version of `<` for determining if the rationalization of - # an irrational number required rounding up or down +function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where {T<:Integer} + return _rationalize_irrational(T, x, tol) +end +function _lessrational(rx::Rational, x::AbstractIrrational) return rx < big(x) end +function lessrational(rx::Rational, x::AbstractIrrational) + return _lessrational(rx, x) +end function <(x::AbstractIrrational, y::Rational{T}) where T T <: Unsigned && x < 0.0 && return true rx = rationalize(T, x) diff --git a/base/mathconstants.jl b/base/mathconstants.jl index 4bb8c409acf00..de6b98cea634d 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -16,6 +16,26 @@ Base.@irrational γ euler Base.@irrational φ (1+sqrt(big(5)))/2 Base.@irrational catalan catalan +const _KnownIrrational = Union{ + typeof(π), typeof(ℯ), typeof(γ), typeof(φ), typeof(catalan) +} + +function Rational{BigInt}(::_KnownIrrational) + Base._throw_argument_error_irrational_to_rational_bigint() +end +Base.@assume_effects :foldable function Rational{T}(x::_KnownIrrational) where {T<:Integer} + Base._irrational_to_rational(T, x) +end +Base.@assume_effects :foldable function (::Type{T})(x::_KnownIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}} + Base._irrational_to_float(T, x, r) +end +Base.@assume_effects :foldable function rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer} + Base._rationalize_irrational(T, x, tol) +end +Base.@assume_effects :foldable function Base.lessrational(rx::Rational, x::_KnownIrrational) + Base._lessrational(rx, x) +end + # aliases """ π From e95860c8595934af535398d160a2b461eeccffc5 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 10 Oct 2024 16:56:22 +0200 Subject: [PATCH 352/548] update `hash` doc string: `widen` not required any more (#55867) Implementing `widen` isn't a requirement any more, since #26022. --- base/hashing.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/hashing.jl b/base/hashing.jl index 7de9f47de3182..d4a6217de6edb 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -11,9 +11,7 @@ optional second argument `h` is another hash code to be mixed with the result. New types should implement the 2-argument form, typically by calling the 2-argument `hash` method recursively in order to mix hashes of the contents with each other (and with `h`). Typically, any type that implements `hash` should also implement its own [`==`](@ref) (hence -[`isequal`](@ref)) to guarantee the property mentioned above. Types supporting subtraction -(operator `-`) should also implement [`widen`](@ref), which is required to hash -values inside heterogeneous arrays. +[`isequal`](@ref)) to guarantee the property mentioned above. The hash value may change when a new Julia process is started. From a233425f7afd4529b429a1b9e7a8ebb2e402680e Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 10 Oct 2024 21:31:35 +0530 Subject: [PATCH 353/548] Merge `diag` methods for triangular matrices (#56086) --- stdlib/LinearAlgebra/src/triangular.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index e1d61e4035966..3f06bd2efe29a 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -494,10 +494,8 @@ adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , tr adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true)) adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false)) -diag(A::LowerTriangular) = diag(A.data) -diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1)) -diag(A::UpperTriangular) = diag(A.data) -diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1)) +diag(A::UpperOrLowerTriangular) = diag(A.data) +diag(A::Union{UnitLowerTriangular, UnitUpperTriangular}) = fill(oneunit(eltype(A)), size(A,1)) # Unary operations -(A::LowerTriangular) = LowerTriangular(-A.data) From dc609a7328ffe42552dea8b79456d29557c08ac3 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 10 Oct 2024 18:36:31 +0200 Subject: [PATCH 354/548] slightly improve inference in precompilation code (#56084) Avoids the ``` 11: signature Tuple{typeof(convert), Type{String}, Any} triggered MethodInstance for Base.Precompilation.ExplicitEnv(::String) (84 children) ``` shown in https://github.com/JuliaLang/julia/issues/56080#issuecomment-2404765120 Co-authored-by: KristofferC --- base/precompilation.jl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index b351ce67cfbad..ea98b0c415ab4 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -43,7 +43,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Collect all direct dependencies of the project for key in ["deps", "weakdeps", "extras"] - for (name, _uuid) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} + for (name, _uuid::String) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} v = key == "deps" ? project_deps : key == "weakdeps" ? project_weakdeps : key == "extras" ? project_extras : @@ -107,9 +107,8 @@ function ExplicitEnv(envpath::String=Base.active_project()) sizehint!(name_to_uuid, length(manifest_d)) sizehint!(lookup_strategy, length(manifest_d)) - for (name, pkg_infos) in get_deps(manifest_d) - pkg_infos = pkg_infos::Vector{Any} - for pkg_info in pkg_infos + for (name, pkg_infos::Vector{Any}) in get_deps(manifest_d) + for pkg_info::Dict{String, Any} in pkg_infos m_uuid = UUID(pkg_info["uuid"]::String) # If we have multiple packages with the same name we will overwrite things here @@ -141,8 +140,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Extensions deps_pkg = get(Dict{String, Any}, pkg_info, "extensions")::Dict{String, Any} - for (ext, triggers) in deps_pkg - triggers = triggers::Union{String, Vector{String}} + for (ext, triggers::Union{String, Vector{String}}) in deps_pkg if triggers isa String triggers = [triggers] end @@ -176,7 +174,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) if proj_name !== nothing && proj_uuid !== nothing deps_expanded[proj_uuid] = filter!(!=(proj_uuid), collect(values(project_deps))) extensions_expanded[proj_uuid] = project_extensions - path = get(project_d, "path", nothing) + path = get(project_d, "path", nothing)::Union{String, Nothing} entry_point = path !== nothing ? path : dirname(envpath) lookup_strategy[proj_uuid] = entry_point end From a007e807623f0bbb820315b8ce3340bd3d41262b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 10 Oct 2024 18:37:05 +0200 Subject: [PATCH 355/548] avoid defining `convert(Vector{String}, ...)` in LibGit2 (#56082) This is a weird conversion function to define. Seems cleaner to use the iteration interface for this. Also avoids some invalidations (https://github.com/JuliaLang/julia/issues/56080#issuecomment-2404765120) Co-authored-by: KristofferC --- stdlib/LibGit2/src/reference.jl | 2 +- stdlib/LibGit2/src/remote.jl | 4 ++-- stdlib/LibGit2/src/repository.jl | 2 +- stdlib/LibGit2/src/strarray.jl | 7 ++++--- stdlib/LibGit2/src/tag.jl | 2 +- stdlib/LibGit2/src/types.jl | 2 +- stdlib/LibGit2/test/libgit2-tests.jl | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/stdlib/LibGit2/src/reference.jl b/stdlib/LibGit2/src/reference.jl index 8a9bc5cf1a6de..de6be0dbe9543 100644 --- a/stdlib/LibGit2/src/reference.jl +++ b/stdlib/LibGit2/src/reference.jl @@ -215,7 +215,7 @@ function ref_list(repo::GitRepo) sa_ref = Ref(StrArrayStruct()) @check ccall((:git_reference_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo) - res = convert(Vector{String}, sa_ref[]) + res = collect(sa_ref[]) free(sa_ref) res end diff --git a/stdlib/LibGit2/src/remote.jl b/stdlib/LibGit2/src/remote.jl index 5081eff56dd46..5b815f946fb17 100644 --- a/stdlib/LibGit2/src/remote.jl +++ b/stdlib/LibGit2/src/remote.jl @@ -215,7 +215,7 @@ function fetch_refspecs(rmt::GitRemote) sa_ref = Ref(StrArrayStruct()) @check ccall((:git_remote_get_fetch_refspecs, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, rmt) - res = convert(Vector{String}, sa_ref[]) + res = collect(sa_ref[]) free(sa_ref) res end @@ -245,7 +245,7 @@ function push_refspecs(rmt::GitRemote) sa_ref = Ref(StrArrayStruct()) @check ccall((:git_remote_get_push_refspecs, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, rmt) - res = convert(Vector{String}, sa_ref[]) + res = collect(sa_ref[]) free(sa_ref) res end diff --git a/stdlib/LibGit2/src/repository.jl b/stdlib/LibGit2/src/repository.jl index 192a6870f639b..9c8d379578b96 100644 --- a/stdlib/LibGit2/src/repository.jl +++ b/stdlib/LibGit2/src/repository.jl @@ -518,7 +518,7 @@ function remotes(repo::GitRepo) @assert repo.ptr != C_NULL @check ccall((:git_remote_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo) - res = convert(Vector{String}, sa_ref[]) + res = collect(sa_ref[]) free(sa_ref) return res end diff --git a/stdlib/LibGit2/src/strarray.jl b/stdlib/LibGit2/src/strarray.jl index db0803680f72b..78e38a9502128 100644 --- a/stdlib/LibGit2/src/strarray.jl +++ b/stdlib/LibGit2/src/strarray.jl @@ -1,6 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license - function Base.cconvert(::Type{Ptr{StrArrayStruct}}, x::Vector) str_ref = Base.cconvert(Ref{Cstring}, x) sa_ref = Ref(StrArrayStruct(Base.unsafe_convert(Ref{Cstring}, str_ref), length(x))) @@ -10,6 +9,8 @@ function Base.unsafe_convert(::Type{Ptr{StrArrayStruct}}, rr::Tuple{Ref{StrArray Base.unsafe_convert(Ptr{StrArrayStruct}, first(rr)) end -function Base.convert(::Type{Vector{String}}, sa::StrArrayStruct) - [unsafe_string(unsafe_load(sa.strings, i)) for i = 1:sa.count] +Base.length(sa::StrArrayStruct) = sa.count +function Base.iterate(sa::StrArrayStruct, state=1) + state > sa.count && return nothing + (unsafe_string(unsafe_load(sa.strings, state)), state+1) end diff --git a/stdlib/LibGit2/src/tag.jl b/stdlib/LibGit2/src/tag.jl index bbb0c97a484ec..73f010590e9c1 100644 --- a/stdlib/LibGit2/src/tag.jl +++ b/stdlib/LibGit2/src/tag.jl @@ -10,7 +10,7 @@ function tag_list(repo::GitRepo) sa_ref = Ref(StrArrayStruct()) @check ccall((:git_tag_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo) - res = convert(Vector{String}, sa_ref[]) + res = collect(sa_ref[]) free(sa_ref) res end diff --git a/stdlib/LibGit2/src/types.jl b/stdlib/LibGit2/src/types.jl index 7a4ad37a68ca5..9228bec175737 100644 --- a/stdlib/LibGit2/src/types.jl +++ b/stdlib/LibGit2/src/types.jl @@ -78,7 +78,7 @@ When fetching data from LibGit2, a typical usage would look like: ```julia sa_ref = Ref(StrArrayStruct()) @check ccall(..., (Ptr{StrArrayStruct},), sa_ref) -res = convert(Vector{String}, sa_ref[]) +res = collect(sa_ref[]) free(sa_ref) ``` In particular, note that `LibGit2.free` should be called afterward on the `Ref` object. diff --git a/stdlib/LibGit2/test/libgit2-tests.jl b/stdlib/LibGit2/test/libgit2-tests.jl index 72ca1019ff9e0..9ab75ed1dc39b 100644 --- a/stdlib/LibGit2/test/libgit2-tests.jl +++ b/stdlib/LibGit2/test/libgit2-tests.jl @@ -95,7 +95,7 @@ end p = ["XXX","YYY"] a = Base.cconvert(Ptr{LibGit2.StrArrayStruct}, p) b = Base.unsafe_convert(Ptr{LibGit2.StrArrayStruct}, a) - @test p == convert(Vector{String}, unsafe_load(b)) + @test p == collect(unsafe_load(b)) @noinline gcuse(a) = a gcuse(a) end From 746655287b04b4092e1bf43d82013ffd310ed35c Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:23:14 -0400 Subject: [PATCH 356/548] array: inline `convert` where possible (#56034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves a common scenario, where someone wants to `push!` a poorly-typed object onto a well-typed Vector. For example: ```julia const NT = @NamedTuple{x::Int,y::Any} foo(v::Vector{NT}, x::Int, @nospecialize(y)) = push!(v, (; x, y)) ``` The `(; x, y)` is slightly poorly-typed here. It could have any type for its `.y` field before it is converted inside the `push!` to a NamedTuple with `y::Any` Without this PR, the dispatch for this `push!` cannot be inferred: ```julia julia> code_typed(foo, (Vector{NT}, Int, Any))[1] CodeInfo( 1 ─ ... │ %4 = %new(%3, x, y)::NamedTuple{(:x, :y), <:Tuple{Int64, Any}} │ %5 = Main.push!(v, %4)::Vector{@NamedTuple{x::Int64, y}} └── return %5 ) => Vector{@NamedTuple{x::Int64, y}} ``` With this PR, the above dynamic call is fully statically resolved and inlined (and therefore `--trim` compatible) --- base/array.jl | 42 +++++++++++++++++++++++++++++++++------- base/namedtuple.jl | 5 +++-- test/abstractarray.jl | 9 +++++++++ test/compiler/effects.jl | 2 +- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/base/array.jl b/base/array.jl index d8a9c725c8102..9bd632f794aa5 100644 --- a/base/array.jl +++ b/base/array.jl @@ -324,9 +324,13 @@ copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, leng # N.B: The generic definition in multidimensional.jl covers, this, this is just here # for bootstrapping purposes. function fill!(dest::Array{T}, x) where T - xT = x isa T ? x : convert(T, x)::T + @inline + x = x isa T ? x : convert(T, x)::T + return _fill!(dest, x) +end +function _fill!(dest::Array{T}, x::T) where T for i in eachindex(dest) - @inbounds dest[i] = xT + @inbounds dest[i] = x end return dest end @@ -980,12 +984,22 @@ Dict{String, Int64} with 2 entries: function setindex! end function setindex!(A::Array{T}, x, i::Int) where {T} + @_propagate_inbounds_meta + x = x isa T ? x : convert(T, x)::T + return _setindex!(A, x, i) +end +function _setindex!(A::Array{T}, x::T, i::Int) where {T} @_noub_if_noinbounds_meta @boundscheck (i - 1)%UInt < length(A)%UInt || throw_boundserror(A, (i,)) - memoryrefset!(memoryrefnew(A.ref, i, false), x isa T ? x : convert(T,x)::T, :not_atomic, false) + memoryrefset!(memoryrefnew(A.ref, i, false), x, :not_atomic, false) return A end function setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} + @_propagate_inbounds_meta + x = x isa T ? x : convert(T, x)::T + return _setindex!(A, x, i1, i2, I...) +end +function _setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} @inline @_noub_if_noinbounds_meta @boundscheck checkbounds(A, i1, i2, I...) # generally _to_linear_index requires bounds checking @@ -1267,10 +1281,16 @@ See also [`pushfirst!`](@ref). function push! end function push!(a::Vector{T}, item) where T + @inline # convert first so we don't grow the array if the assignment won't work - itemT = item isa T ? item : convert(T, item)::T + # and also to avoid a dynamic dynamic dispatch in the common case that + # `item` is poorly-typed and `a` is well-typed + item = item isa T ? item : convert(T, item)::T + return _push!(a, item) +end +function _push!(a::Vector{T}, item::T) where T _growend!(a, 1) - @_safeindex a[length(a)] = itemT + @_safeindex a[length(a)] = item return a end @@ -1664,7 +1684,11 @@ julia> pushfirst!([1, 2, 3, 4], 5, 6) ``` """ function pushfirst!(a::Vector{T}, item) where T + @inline item = item isa T ? item : convert(T, item)::T + return _pushfirst!(a, item) +end +function _pushfirst!(a::Vector{T}, item::T) where T _growbeg!(a, 1) @_safeindex a[1] = item return a @@ -1750,12 +1774,16 @@ julia> insert!(Any[1:6;], 3, "here") ``` """ function insert!(a::Array{T,1}, i::Integer, item) where T + @_propagate_inbounds_meta + item = item isa T ? item : convert(T, item)::T + return _insert!(a, i, item) +end +function _insert!(a::Array{T,1}, i::Integer, item::T) where T @_noub_meta # Throw convert error before changing the shape of the array - _item = item isa T ? item : convert(T, item)::T _growat!(a, i, 1) # :noub, because _growat! already did bound check - @inbounds a[i] = _item + @inbounds a[i] = item return a end diff --git a/base/namedtuple.jl b/base/namedtuple.jl index e316dbd37ccf5..a7379121b2ce2 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -179,10 +179,11 @@ nextind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)+1 convert(::Type{NT}, nt::NT) where {names, NT<:NamedTuple{names}} = nt convert(::Type{NT}, nt::NT) where {names, T<:Tuple, NT<:NamedTuple{names,T}} = nt -convert(::Type{NT}, t::Tuple) where {NT<:NamedTuple} = NT(t) +convert(::Type{NT}, t::Tuple) where {NT<:NamedTuple} = (@inline NT(t))::NT function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T<:Tuple} - NamedTuple{names,T}(T(nt))::NamedTuple{names,T} + NT = NamedTuple{names,T} + (@inline NT(nt))::NT end function convert(::Type{NT}, nt::NamedTuple{names}) where {names, NT<:NamedTuple{names}} diff --git a/test/abstractarray.jl b/test/abstractarray.jl index f655d9abe423f..b40956b433630 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -2,6 +2,8 @@ using Random, LinearAlgebra +include("compiler/irutils.jl") + isdefined(Main, :InfiniteArrays) || @eval Main include("testhelpers/InfiniteArrays.jl") using .Main.InfiniteArrays @@ -1403,6 +1405,8 @@ end Base.push!(tpa::TestPushArray{T}, a::T) where T = push!(tpa.data, a) Base.pushfirst!(tpa::TestPushArray{T}, a::T) where T = pushfirst!(tpa.data, a) +push_slightly_abstract_namedtuple(v::Vector{@NamedTuple{x::Int,y::Any}}, x::Int, @nospecialize(y)) = push!(v, (; x, y)) + @testset "push! and pushfirst!" begin a_orig = [1] tpa = TestPushArray{Int, 2}(a_orig) @@ -1412,6 +1416,11 @@ Base.pushfirst!(tpa::TestPushArray{T}, a::T) where T = pushfirst!(tpa.data, a) tpa = TestPushArray{Int, 2}(a_orig) pushfirst!(tpa, 6, 5, 4, 3, 2) @test tpa.data == reverse(collect(1:6)) + + let src = code_typed1(push_slightly_abstract_namedtuple, (Vector{@NamedTuple{x::Int,y::Any}},Int,Any)) + # After optimization, all `push!` and `convert` calls should have been inlined + @test all((x)->!iscall((src, push!))(x) && !iscall((src, convert))(x), src.code) + end end mutable struct SimpleArray{T} <: AbstractVector{T} diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index c8a699b294d37..cdc26cddc440d 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1225,7 +1225,7 @@ end @test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(getindex, (Vector{Int},Int))) @test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(getindex, (Vector{Any},Int))) @test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(setindex!, (Vector{Int},Int,Int))) -@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(setindex!, (Vector{Any},Any,Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(Base._setindex!, (Vector{Any},Any,Int))) @test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(isassigned, (Vector{Int},Int))) @test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(isassigned, (Vector{Any},Int))) @test Base.infer_effects((Vector{Int},Int)) do xs, i From 055e37ead67b04266229bee765ade9e361b8f791 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 11 Oct 2024 07:50:40 +0530 Subject: [PATCH 357/548] Remove some unnecessary `real` specializations for structured matrices (#56083) The `real(::AbstractArray{<:Rea})` fallback method should handle these cases correctly. --- stdlib/LinearAlgebra/src/hessenberg.jl | 1 - stdlib/LinearAlgebra/src/triangular.jl | 1 - 2 files changed, 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 524e57711ce3a..bfe2fdd41aace 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -70,7 +70,6 @@ Base.dataids(A::UpperHessenberg) = Base.dataids(parent(A)) Base.unaliascopy(A::UpperHessenberg) = UpperHessenberg(Base.unaliascopy(parent(A))) copy(H::UpperHessenberg) = UpperHessenberg(copy(H.data)) -real(H::UpperHessenberg{<:Real}) = H real(H::UpperHessenberg{<:Complex}) = UpperHessenberg(triu!(real(H.data),-1)) imag(H::UpperHessenberg) = UpperHessenberg(triu!(imag(H.data),-1)) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 3f06bd2efe29a..ee63865b65d6e 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -45,7 +45,6 @@ for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTr copy(A::$t) = $t(copy(A.data)) Base.unaliascopy(A::$t) = $t(Base.unaliascopy(A.data)) - real(A::$t{<:Real}) = A real(A::$t{<:Complex}) = (B = real(A.data); $t(B)) real(A::$t{<:Complex, <:StridedMaybeAdjOrTransMat}) = $t(real.(A)) end From 41b1778117b26251c11de1ad2f562cc7d00e0d43 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 11 Oct 2024 07:51:33 +0530 Subject: [PATCH 358/548] Combine `diag` methods for `SymTridiagonal` (#56014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, there are two branches, one for an `eltype` that is a `Number`, and the other that deals with generic `eltype`s. They do similar things, so we may combine these, and use branches wherever necessary to retain the performance. We also may replace explicit materialized arrays by generators in `copyto!`. Overall, this improves performance in `diag` for matrices of matrices, whereas the performance in the common case of matrices of numbers remains unchanged. ```julia julia> using StaticArrays, LinearAlgebra julia> s = SMatrix{2,2}(1:4); julia> S = SymTridiagonal(fill(s,100), fill(s,99)); julia> @btime diag($S); 1.292 μs (5 allocations: 7.16 KiB) # nightly, v"1.12.0-DEV.1317" 685.012 ns (3 allocations: 3.19 KiB) # This PR ``` This PR also allows computing the `diag` for more values of the band index `n`: ```julia julia> diag(S,99) 1-element Vector{SMatrix{2, 2, Int64, 4}}: [0 0; 0 0] ``` This would work as long as `getindex` works for the `SymTridiagonal` for that band, and the zero element may be converted to the `eltype`. --- stdlib/LinearAlgebra/src/tridiag.jl | 41 ++++++++-------------------- stdlib/LinearAlgebra/test/tridiag.jl | 29 ++++++++++++++------ 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index e75e1e5eefb3d..c1af12514e020 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -183,44 +183,27 @@ issymmetric(S::SymTridiagonal) = true tr(S::SymTridiagonal) = sum(symmetric, 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 +_diagiter(M::SymTridiagonal{<:Number}) = M.dv +_diagiter(M::SymTridiagonal) = (symmetric(x, :U) for x in M.dv) +_eviter_transposed(M::SymTridiagonal{<:Number}) = _evview(M) +_eviter_transposed(M::SymTridiagonal) = (transpose(x) for x in _evview(M)) -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 - absn = abs(n) - if absn == 0 - return copyto!(similar(M.dv, length(M.dv)), M.dv) - elseif absn == 1 - return copyto!(similar(M.ev, length(M.dv)-1), _evview(M)) - elseif absn <= size(M,1) - v = similar(M.dv, size(M,1)-absn) - for i in eachindex(v) - v[i] = M[BandIndex(n,i)] - end - return v - else - throw_diag_outofboundserror(n, size(M)) - end -end function diag(M::SymTridiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n + v = similar(M.dv, max(0, length(M.dv)-abs(n))) if n == 0 - return copyto!(similar(M.dv, length(M.dv)), symmetric.(M.dv, :U)) + return copyto!(v, _diagiter(M)) elseif n == 1 - return copyto!(similar(M.ev, length(M.dv)-1), _evview(M)) + return copyto!(v, _evview(M)) elseif n == -1 - return copyto!(similar(M.ev, length(M.dv)-1), transpose.(_evview(M))) - elseif n <= size(M,1) - throw(ArgumentError("requested diagonal contains undefined zeros of an array type")) + return copyto!(v, _eviter_transposed(M)) else - throw_diag_outofboundserror(n, size(M)) + for i in eachindex(v) + v[i] = M[BandIndex(n,i)] + end end + return v end +(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv+B.dv, _evview(A)+_evview(B)) diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index b6e93341b1946..b1d52ab8c5679 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -287,8 +287,13 @@ end @test (@inferred diag(A, 1))::typeof(d) == (mat_type == Tridiagonal ? du : dl) @test (@inferred diag(A, -1))::typeof(d) == dl @test (@inferred diag(A, n-1))::typeof(d) == zeros(elty, 1) - @test_throws ArgumentError diag(A, -n - 1) - @test_throws ArgumentError diag(A, n + 1) + if A isa SymTridiagonal + @test isempty(@inferred diag(A, -n - 1)) + @test isempty(@inferred diag(A, n + 1)) + else + @test_throws ArgumentError diag(A, -n - 1) + @test_throws ArgumentError diag(A, n + 1) + end GA = mat_type == Tridiagonal ? mat_type(GenericArray.((dl, d, du))...) : mat_type(GenericArray.((d, dl))...) @test (@inferred diag(GA))::typeof(GenericArray(d)) == GenericArray(d) @test (@inferred diag(GA, -1))::typeof(GenericArray(d)) == GenericArray(dl) @@ -501,10 +506,11 @@ end @test @inferred diag(A, 1) == fill(M, n-1) @test @inferred diag(A, 0) == fill(Symmetric(M), n) @test @inferred diag(A, -1) == fill(transpose(M), n-1) - @test_throws ArgumentError diag(A, -2) - @test_throws ArgumentError diag(A, 2) - @test_throws ArgumentError diag(A, n+1) - @test_throws ArgumentError diag(A, -n-1) + @test_broken diag(A, -2) == fill(M, n-2) + @test_broken diag(A, 2) == fill(M, n-2) + @test isempty(@inferred diag(A, n+1)) + @test isempty(@inferred diag(A, -n-1)) + A[1,1] = Symmetric(2M) @test A[1,1] == Symmetric(2M) @test_throws ArgumentError A[1,1] = M @@ -519,8 +525,8 @@ end @test @inferred diag(A, 1) == fill(M, n-1) @test @inferred diag(A, 0) == fill(M, n) @test @inferred diag(A, -1) == fill(M, n-1) - @test_throws MethodError diag(A, -2) - @test_throws MethodError diag(A, 2) + @test_broken diag(A, -2) == fill(M, n-2) + @test_broken diag(A, 2) == fill(M, n-2) @test_throws ArgumentError diag(A, n+1) @test_throws ArgumentError diag(A, -n-1) @@ -532,6 +538,13 @@ end A = Tridiagonal(ev, dv, ev) @test A == Matrix{eltype(A)}(A) end + + M = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) + S = SymTridiagonal(fill(M,4), fill(M,3)) + @test diag(S,2) == fill(zero(M), 2) + @test diag(S,-2) == fill(zero(M), 2) + @test isempty(diag(S,4)) + @test isempty(diag(S,-4)) end @testset "Issue 12068" begin From d55f38a8d853e6ea8aa07a48c713cb026bd688e2 Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Fri, 11 Oct 2024 07:51:36 +0200 Subject: [PATCH 359/548] fix `Vararg{T,T} where T` crashing `code_typed` (#56081) Not sure this is the right place to fix this error, perhaps `match.spec_types` should always be a tuple of valid types? fixes #55916 --------- Co-authored-by: Jameson Nash --- base/compiler/abstractinterpretation.jl | 1 + test/compiler/inference.jl | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index c8a25be422637..70623453e1666 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -547,6 +547,7 @@ function collect_slot_refinements(𝕃ᵢ::AbstractLattice, applicable::Vector{A sigt = Bottom for j = 1:length(applicable) match = applicable[j]::MethodMatch + valid_as_lattice(match.spec_types, true) || continue sigt = sigt ⊔ fieldtype(match.spec_types, i) end if sigt ⊏ argt # i.e. signature type is strictly more specific than the type of the argument slot diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7c7726413004a..e3b1ac499e986 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6048,3 +6048,10 @@ t255751 = Array{Float32, 3} issue55882_nfields(x::Union{T,Nothing}) where T<:Number = nfields(x) @test Base.infer_return_type(issue55882_nfields) <: Int + +# issue #55916 +f55916(x) = 1 +f55916(::Vararg{T,T}) where {T} = "2" +g55916(x) = f55916(x) +# this shouldn't error +@test only(code_typed(g55916, (Any,); optimize=false))[2] == Int From 8169e012d4cbec569cf85f5c92e0343e335b569f Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 11 Oct 2024 07:54:16 +0200 Subject: [PATCH 360/548] [libblastrampoline_jll] Upgrade to v5.11.1 (#56094) v5.11.1 is a patch release with a couple of RISC-V fixes. --- deps/blastrampoline.version | 6 +- deps/checksums/blastrampoline | 68 +++++++++++------------ stdlib/libblastrampoline_jll/Project.toml | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index fd055e1ae8120..95771acad9ffa 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.11.0 -BLASTRAMPOLINE_BRANCH=v5.11.0 -BLASTRAMPOLINE_SHA1=05083d50611b5538df69706f0a952d8e642b0b4b +BLASTRAMPOLINE_VER := 5.11.1 +BLASTRAMPOLINE_BRANCH=v5.11.1 +BLASTRAMPOLINE_SHA1=b09277feafd342520b8476ce443d35327b5e55b4 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index edb8cadc74846..ac028ceb6e124 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ -blastrampoline-05083d50611b5538df69706f0a952d8e642b0b4b.tar.gz/md5/700b22cb26291736bd1263cd2a7f2d75 -blastrampoline-05083d50611b5538df69706f0a952d8e642b0b4b.tar.gz/sha512/967c16d28834df112916c0904dd4c7231a1c5e4edf279adb26411faa17da28eee4680ce2347b3941520dccbc768944277a8f724b21976960d00f840349b90e36 -libblastrampoline.v5.11.0+0.aarch64-apple-darwin.tar.gz/md5/769458d40e004d6126cae6b34351068f -libblastrampoline.v5.11.0+0.aarch64-apple-darwin.tar.gz/sha512/75a726b9a4f41b70344ceb9e1f1a7ad370bfa84ce44c70b8a965061d777871e3bf2237ae055da7e6202ddef78932ba8baf2a01a675b1b0cec5338ef16ea2081b -libblastrampoline.v5.11.0+0.aarch64-linux-gnu.tar.gz/md5/d92cf3f3fa1e977ea3a1a74acc8442d1 -libblastrampoline.v5.11.0+0.aarch64-linux-gnu.tar.gz/sha512/3354f4eec2a410f81cc0546a04ce98ddd416d441c1701a59ec5bebea99af8823b5af10a85cb4e3377548422c6d6a0a870f2e7a05ad0cda52c6143361d59ba4fb -libblastrampoline.v5.11.0+0.aarch64-linux-musl.tar.gz/md5/41d060c03202b662e47bda5fbf7b1e84 -libblastrampoline.v5.11.0+0.aarch64-linux-musl.tar.gz/sha512/54a05516e12350441c33341fde53bc912aa52dc4b746089c2d21cb75f24f0fb140849a520327db6f52895743eab090b59fa974a2a426a49f8b4e38693340a306 -libblastrampoline.v5.11.0+0.armv6l-linux-gnueabihf.tar.gz/md5/4930dceefac63e7aa5a93e1ba0e00e59 -libblastrampoline.v5.11.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/dafce083c2b409ead61fdbdf4f46b7c93cab00c82a74a181d381c4a93f1e7af035cd6caf407b0199c1f8c2f2f68f93d67938ef092fa4a8d1133f0ea73fb51a9c -libblastrampoline.v5.11.0+0.armv6l-linux-musleabihf.tar.gz/md5/82346cc4ddeaa29ea7a081edfdfcb08b -libblastrampoline.v5.11.0+0.armv6l-linux-musleabihf.tar.gz/sha512/72e387bd661096a46077e8c15e12f8a6f18fd6aaf30af0678d00eca0d83af10758874643f5716539dd38269e831e4649d45db739aeb60996bf1b96277cea1d17 -libblastrampoline.v5.11.0+0.armv7l-linux-gnueabihf.tar.gz/md5/7e8f115268e8c62acaa2a53ecd32e2fe -libblastrampoline.v5.11.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/4210c306ff7ccb53aa6c9f45e134c63b238c563ed753f7536dfc21f6962dfea35d9de62e429e2685b70d0db780ac766b72fd5e76e2d62df74000e3e5d553c30f -libblastrampoline.v5.11.0+0.armv7l-linux-musleabihf.tar.gz/md5/7f388611c477b528a091f697b0d334d9 -libblastrampoline.v5.11.0+0.armv7l-linux-musleabihf.tar.gz/sha512/e9b017dfa8c19cb940395b253f3b28511a6619469fabff7ab1671ed0936e9e0681d1385c3d1f5d6417ccb65ffbdcf53a0c8519d4ef8e89f9500a05ca00296144 -libblastrampoline.v5.11.0+0.i686-linux-gnu.tar.gz/md5/254948ea87a435251b1e064a77b3d635 -libblastrampoline.v5.11.0+0.i686-linux-gnu.tar.gz/sha512/5a51d3c20c49c497a8f0c2d2e7b38b49ec5e367c7013a7f0efa4fc099639da20ef9c0bfdbdfbdc40b27ce61f189b18f5cf617d7a0ed4bc5300da692f7d6b77a4 -libblastrampoline.v5.11.0+0.i686-linux-musl.tar.gz/md5/a9504870af8db1e247be02c5e188f7a5 -libblastrampoline.v5.11.0+0.i686-linux-musl.tar.gz/sha512/5f0109168a16edb8ca66fcf10c2c10b57fe9c3061c0b08dac4dea936538fa5854aa1b66079f127b5d9902288b61772054013256aa307b682de38e350b1bbb367 -libblastrampoline.v5.11.0+0.i686-w64-mingw32.tar.gz/md5/815822f6bacb42c35b80bc77458c5c49 -libblastrampoline.v5.11.0+0.i686-w64-mingw32.tar.gz/sha512/c82f8c6fe0b7917860e5601c79e35d56297c53b6f7f992841d4f048e7981533e459f9db0805a16d82a9e03d452489760def0d9c57181dcfa5dc363102180eecd -libblastrampoline.v5.11.0+0.powerpc64le-linux-gnu.tar.gz/md5/ee30c9cb4c51df03026f9e471040e9cc -libblastrampoline.v5.11.0+0.powerpc64le-linux-gnu.tar.gz/sha512/5055d83a1b0625364ddd97652a4c6fa39c795078123cad33a085283889274f66c9dc053be0591c14be262dc7eef666726afa922c66ae8d05c2791c3d6bd7009e -libblastrampoline.v5.11.0+0.x86_64-apple-darwin.tar.gz/md5/210cd354c9b4a8aa2a2b55723597e58b -libblastrampoline.v5.11.0+0.x86_64-apple-darwin.tar.gz/sha512/1ee65d598f9f8a2cf7137135c8c2c431520b1cde319fc33dddfbdae9fe01d986e979a97c24cf85c090cc40064cfe47c376dfeb088ff417d17868c4df84fb2fd4 -libblastrampoline.v5.11.0+0.x86_64-linux-gnu.tar.gz/md5/e2213c42eebee6e45079ef6831077b3f -libblastrampoline.v5.11.0+0.x86_64-linux-gnu.tar.gz/sha512/ab2c3026d34962a2ca5116d71a4e8eaaca5182d53f21edd3e4be81ce26e74e427c88797308af7fbbf1b9ee615e0383acf0dae1d0eb207ebc64dddaf927f00b48 -libblastrampoline.v5.11.0+0.x86_64-linux-musl.tar.gz/md5/8cde3c618e882ea2b7c8a017a69175c7 -libblastrampoline.v5.11.0+0.x86_64-linux-musl.tar.gz/sha512/8a3aca5691c3936d114c804471b2429b9ae81308f020247765614d2f792f93a012263ce4baa31cf42f4dacc23a7161a4c7f9debfba8d9028879f1ed3fc4e2433 -libblastrampoline.v5.11.0+0.x86_64-unknown-freebsd.tar.gz/md5/b02eb694e1486ef8ffe9534ac2bd5ec6 -libblastrampoline.v5.11.0+0.x86_64-unknown-freebsd.tar.gz/sha512/989273809ae567d7e7193529740423ac1870eae3a0effeecc67f84da914d81649786f393e101f013b7232ef5fe35066d89b3cb776ad0e87394799491ef28a467 -libblastrampoline.v5.11.0+0.x86_64-w64-mingw32.tar.gz/md5/6e7f602ab0bf5a5c28bf4e959a1bbf77 -libblastrampoline.v5.11.0+0.x86_64-w64-mingw32.tar.gz/sha512/556e7ca1a2576c1d7825ac1bc2449ffe2cd40391cf316b10f60681a5c736939c97eb5221c2837640928b5544f89f44cb14ca44ccf54092376390ea1a6012c9e5 +blastrampoline-b09277feafd342520b8476ce443d35327b5e55b4.tar.gz/md5/7516eaaa5777a93cf387da1bf4b14c8a +blastrampoline-b09277feafd342520b8476ce443d35327b5e55b4.tar.gz/sha512/00fea70f713be77be10bb014e7dad957616ea59d882e2bfa75d7b8b7237dd59d735cfb944b9cac3fa34fbe7b0a78c89c25b605bdea33e2c17278f29874e20363 +libblastrampoline.v5.11.1+0.aarch64-apple-darwin.tar.gz/md5/93ee5c360913b8ed7c558a2edeb7014b +libblastrampoline.v5.11.1+0.aarch64-apple-darwin.tar.gz/sha512/3f6e78d8c966fce6eecf82931186907cc10b95ceb71d5cfc3ee958b20a11d0e24d1a399fb7fba4cf7180fa61f3d0965db6e6ca9d99dd8c4ab56d36713fd9a327 +libblastrampoline.v5.11.1+0.aarch64-linux-gnu.tar.gz/md5/aad5e3585f585d54d9ebcf822bbe32cb +libblastrampoline.v5.11.1+0.aarch64-linux-gnu.tar.gz/sha512/11ff9227e16898895ad6cbd36853093941b243a49962785a5ab8b7dc2426831a2750ab5882ee814e3a662e8b9f8aecb273d750b88a4ea5a213e20c93cb121ce1 +libblastrampoline.v5.11.1+0.aarch64-linux-musl.tar.gz/md5/462639b4b21f5b7626febfdd1ae1f824 +libblastrampoline.v5.11.1+0.aarch64-linux-musl.tar.gz/sha512/866004e3fcdb5ab7418c8a2cae8f820c5739a511b9d0b32d0013ef72ff99f87396f5912d8fbd6bf4d01d7432715c6971ad1a5419c34fa7b048d0fbbe0f8520d2 +libblastrampoline.v5.11.1+0.armv6l-linux-gnueabihf.tar.gz/md5/8a48cc8243257362dbc920dcadc42a22 +libblastrampoline.v5.11.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/bb4048c0e1ebbb89fc82b7cdabb0a4d9263b5344390c934b66c3a227631661ae956287870e4b156935f0a3c322049ceed3138fc033c92561fccf3675317af5b8 +libblastrampoline.v5.11.1+0.armv6l-linux-musleabihf.tar.gz/md5/53c12d04337b63d18f4a5469a36132b6 +libblastrampoline.v5.11.1+0.armv6l-linux-musleabihf.tar.gz/sha512/fbb9e1cd3c80cf6eada43c7b3d3e6990a2b54c3f7de492ba5407d64841e705a68a5c7aa8bf4873f3204a7f8a9631a0135e2e08b57d4291b32d0f928e887c1e14 +libblastrampoline.v5.11.1+0.armv7l-linux-gnueabihf.tar.gz/md5/08963ae41481cbd4d7d9c9790b8e161e +libblastrampoline.v5.11.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/428e952b3ec6904c9aa233fab1a860a30b043aa8e7508978406a0aafffee03b4e73b51dcd1eaa8550032edf51bd84e1c8356cdbd180d48791c5c0486c3a925a1 +libblastrampoline.v5.11.1+0.armv7l-linux-musleabihf.tar.gz/md5/fae4f9b44ddca8f74f8999fe3a9f0a91 +libblastrampoline.v5.11.1+0.armv7l-linux-musleabihf.tar.gz/sha512/afd37260ee0ecc0a1fe34f0e78cb1fd563e8d0cad025bc8ad733186a56c1c1faa4ffb4de593aead0b21513c9108847e08734ec14443ab8c0c36468f990bdf38e +libblastrampoline.v5.11.1+0.i686-linux-gnu.tar.gz/md5/3d664f435a559022a8309f271a8376e5 +libblastrampoline.v5.11.1+0.i686-linux-gnu.tar.gz/sha512/60a2863237f0b668237c6b68c0671ecf17d62272b047f2ad5e6b466aeb7e0e92fa1207e9c107de7c96a2b8974925f2af69324104c22fa1c51a9cc207b84e2d22 +libblastrampoline.v5.11.1+0.i686-linux-musl.tar.gz/md5/3d63e967ae8301329e9a79a0882c14f6 +libblastrampoline.v5.11.1+0.i686-linux-musl.tar.gz/sha512/9c3950bccf578b3b3b609398ab7a05c13cb86ded686c585f916c521adb533589166530c825af8095bb6d88b9ae0d14dae992a53b578af502f19811be1aecc185 +libblastrampoline.v5.11.1+0.i686-w64-mingw32.tar.gz/md5/99890890c7e600d0817775026baca09b +libblastrampoline.v5.11.1+0.i686-w64-mingw32.tar.gz/sha512/87904de1637967e1ba6a17b788c7ae3d049934553d14302c715db829f1a2aaa55c35f3c04d3ef0fce7a589e66d41fba939906a5dd5b19daf3ede343d298bc018 +libblastrampoline.v5.11.1+0.powerpc64le-linux-gnu.tar.gz/md5/bda2bbfb9af8eb655fead11a6ce142cb +libblastrampoline.v5.11.1+0.powerpc64le-linux-gnu.tar.gz/sha512/ca318ff7b362ee5f15654c669f4acf45d4530499daa2b8e64da179c2b0ba2bddb0d0b30dc08b3427a55dd2f0ee239b7c00fb93bd27572d14a863677bf22a0173 +libblastrampoline.v5.11.1+0.x86_64-apple-darwin.tar.gz/md5/dec773fbfbf218b35e942325cf9305dc +libblastrampoline.v5.11.1+0.x86_64-apple-darwin.tar.gz/sha512/c7d4828689361c9a8708b7cf1b0b1fa4f237e2a50b45f71457782b84fcc88c757e00bc91f19e9c7bc94d1c69420ec2c4ebe39c62f9fd140e72ff8a408879474c +libblastrampoline.v5.11.1+0.x86_64-linux-gnu.tar.gz/md5/88545391ae715b0f83b786f6eb7a6ee5 +libblastrampoline.v5.11.1+0.x86_64-linux-gnu.tar.gz/sha512/f041dac97783108b6b4e90a74315c3c4074c82ab926b1d3c1b90dac03dd1b7ea60dbb96b0c36b34b9e386732c8f546c7c54ea8111c650d0454cfb6015535ddf2 +libblastrampoline.v5.11.1+0.x86_64-linux-musl.tar.gz/md5/7c8353b779cfae36984a0a806f985a7b +libblastrampoline.v5.11.1+0.x86_64-linux-musl.tar.gz/sha512/5288123a4cb81befac2b2504c503303e0cf7d6eee3e9ba3195378900b0204745ed0e818f31a1d344bd552ff06a9904075b1fb742eea5f1f5de907c0df141b8ca +libblastrampoline.v5.11.1+0.x86_64-unknown-freebsd.tar.gz/md5/7bc51751c09a1772d2f8638e5d3e4655 +libblastrampoline.v5.11.1+0.x86_64-unknown-freebsd.tar.gz/sha512/5fde7423915964e4491f9fc46da9fb046fc85a434408dd4cb61521efe70d090e7b5dd2a995345318b287f03c9f21c15de2f627244332038b5dc99e28c88a29b3 +libblastrampoline.v5.11.1+0.x86_64-w64-mingw32.tar.gz/md5/6e7f602ab0bf5a5c28bf4e959a1bbf77 +libblastrampoline.v5.11.1+0.x86_64-w64-mingw32.tar.gz/sha512/556e7ca1a2576c1d7825ac1bc2449ffe2cd40391cf316b10f60681a5c736939c97eb5221c2837640928b5544f89f44cb14ca44ccf54092376390ea1a6012c9e5 diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 1dd22b7fb8d40..eb71a4a9d532c 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,6 +1,6 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" +version = "5.11.1+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 9c619c311b6b1d1b5151e2d0047a853d5b017380 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 11 Oct 2024 01:56:25 -0400 Subject: [PATCH 361/548] Revert "REPL: hide any prints to stdio during `complete_line`" (#56102) --- stdlib/REPL/src/LineEdit.jl | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 3ac403df54007..c92dca8c8e015 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -366,14 +366,7 @@ end # Prompt Completions & Hints function complete_line(s::MIState) set_action!(s, :complete_line) - # suppress stderr/stdout prints during completion computation - # i.e. ambiguous qualification warnings that are printed to stderr - # TODO: remove this suppression once such warnings are better handled - # TODO: but before that change Pipe to devnull once devnull redirects work for JL_STDERR etc. - completions_exist = redirect_stdio(;stderr=Pipe(), stdout=Pipe()) do - complete_line(state(s), s.key_repeats, s.active_module) - end - if completions_exist + if complete_line(state(s), s.key_repeats, s.active_module) return refresh_line(s) else beep(s) @@ -391,13 +384,7 @@ function check_for_hint(s::MIState) end completions, partial, should_complete = try - # suppress stderr/stdout prints during completion computation - # i.e. ambiguous qualification warnings that are printed to stderr - # TODO: remove this suppression once such warnings are better handled - # TODO: but before that change Pipe to devnull once devnull redirects work for JL_STDERR etc. - completions, partial, should_complete = redirect_stdio(;stderr=Pipe(), stdout=Pipe()) do - complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} - end + complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} catch @debug "error completing line for hint" exception=current_exceptions() return clear_hint(st) From 1438b1578941d0f1cc8f8c958cf3bd2927fd482c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 11 Oct 2024 01:57:36 -0400 Subject: [PATCH 362/548] Remove warning from c when binding is ambiguous (#56103) --- doc/src/manual/modules.md | 4 ++-- src/module.c | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 6b335305ac569..b4f0fd78c816a 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -283,14 +283,14 @@ julia> module B B ``` -The statement `using .A, .B` works, but when you try to call `f`, you get a warning +The statement `using .A, .B` works, but when you try to call `f`, you get an error with a hint ```jldoctest module_manual julia> using .A, .B julia> f -WARNING: both B and A export "f"; uses of it in module Main must be qualified ERROR: UndefVarError: `f` not defined in `Main` +Hint: It looks like two or more modules export different bindings with this name, resulting in ambiguity. Try explicitly importing it from a particular module, or qualifying the name with the module it should come from. ``` Here, Julia cannot decide which `f` you are referring to, so you have to make a choice. The following solutions are commonly used: diff --git a/src/module.c b/src/module.c index f4da7e1e994de..36c35f50b44af 100644 --- a/src/module.c +++ b/src/module.c @@ -430,11 +430,6 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl tempb = jl_get_module_binding(m, var, 1); tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); jl_atomic_store_release(&tempbpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED)); - jl_printf(JL_STDERR, - "WARNING: both %s and %s export \"%s\"; uses of it in module %s must be qualified\n", - jl_symbol_name(owner->name), - jl_symbol_name(imp->name), jl_symbol_name(var), - jl_symbol_name(m->name)); } return NULL; } From 9844d854408d2312d70003fdde247c7195bffa4c Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 11 Oct 2024 13:51:51 +0200 Subject: [PATCH 363/548] make `Base.ANSIIterator` have a concrete field (#56088) Avoids the invalidation ``` backedges: 1: superseding sizeof(s::AbstractString) @ Base strings/basic.jl:177 with MethodInstance for sizeof(::AbstractString) (75 children) ``` shown in https://github.com/JuliaLang/julia/issues/56080#issuecomment-2404765120. Co-authored-by: KristofferC --- base/show.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/show.jl b/base/show.jl index ec6776d81f2d5..66560265e3b42 100644 --- a/base/show.jl +++ b/base/show.jl @@ -72,13 +72,13 @@ ncodeunits(c::ANSIDelimiter) = ncodeunits(c.del) textwidth(::ANSIDelimiter) = 0 # An iterator similar to `pairs(::String)` but whose values are Char or ANSIDelimiter -struct ANSIIterator - captures::RegexMatchIterator +struct ANSIIterator{S} + captures::RegexMatchIterator{S} end ANSIIterator(s::AbstractString) = ANSIIterator(eachmatch(ansi_regex, s)) -IteratorSize(::Type{ANSIIterator}) = SizeUnknown() -eltype(::Type{ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}} +IteratorSize(::Type{<:ANSIIterator}) = SizeUnknown() +eltype(::Type{<:ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}} function iterate(I::ANSIIterator, (i, m_st)=(1, iterate(I.captures))) m_st === nothing && return nothing m, (j, new_m_st) = m_st From f3a36d74eeb1f8c6439affcc33e2a304550dc217 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 11 Oct 2024 23:16:26 +0800 Subject: [PATCH 364/548] Subtype: some performance tuning. (#56007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main motivation of this PR is to fix #55807. dc689fe8700f70f4a4e2dbaaf270f26b87e79e04 tries to remove the slow `may_contain_union_decision` check by re-organizing the code path. Now the fast path has been removed and most of its optimization has been integrated into the preserved slow path. Since the slow path stores all inner ∃ decisions on the outer most R stack, there might be overflow risk. aee69a41441b4306ba3ee5e845bc96cb45d9b327 should fix that concern. The reported MWE now becomes ```julia 0.000002 seconds 0.000040 seconds (105 allocations: 4.828 KiB, 52.00% compilation time) 0.000023 seconds (105 allocations: 4.828 KiB, 49.36% compilation time) 0.000026 seconds (105 allocations: 4.828 KiB, 50.38% compilation time) 0.000027 seconds (105 allocations: 4.828 KiB, 54.95% compilation time) 0.000019 seconds (106 allocations: 4.922 KiB, 49.73% compilation time) 0.000024 seconds (105 allocations: 4.828 KiB, 52.24% compilation time) ``` Local bench also shows that 72855cd slightly accelerates `OmniPackage.jl`'s loading ```julia julia> @time using OmniPackage # v1.11rc4 20.525278 seconds (25.36 M allocations: 1.606 GiB, 8.48% gc time, 12.89% compilation time: 77% of which was recompilation) # v1.11rc4+aee69a4+72855cd 19.527871 seconds (24.92 M allocations: 1.593 GiB, 8.88% gc time, 15.13% compilation time: 82% of which was recompilation) ``` --- src/subtype.c | 298 +++++++++++++++++++++++++++++--------------------- 1 file changed, 173 insertions(+), 125 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 65ee4d5916bce..5edcd100ee8e0 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -39,20 +39,24 @@ extern "C" { // Union type decision points are discovered while the algorithm works. // If a new Union decision is encountered, the `more` flag is set to tell // the forall/exists loop to grow the stack. -// TODO: the stack probably needs to be artificially large because of some -// deeper problem (see #21191) and could be shrunk once that is fixed + +typedef struct jl_bits_stack_t { + uint32_t data[16]; + struct jl_bits_stack_t *next; +} jl_bits_stack_t; + typedef struct { int16_t depth; int16_t more; int16_t used; - uint32_t stack[100]; // stack of bits represented as a bit vector + jl_bits_stack_t stack; } jl_unionstate_t; typedef struct { int16_t depth; int16_t more; int16_t used; - void *stack; + uint8_t *stack; } jl_saved_unionstate_t; // Linked list storing the type variable environment. A new jl_varbinding_t @@ -131,37 +135,111 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J } #endif +// union-stack tools + static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT { - assert(i >= 0 && i < sizeof(st->stack) * 8); + assert(i >= 0 && i <= 32767); // limited by the depth bit. // get the `i`th bit in an array of 32-bit words - return (st->stack[i>>5] & (1u<<(i&31))) != 0; + jl_bits_stack_t *stack = &st->stack; + while (i >= sizeof(stack->data) * 8) { + // We should have set this bit. + assert(stack->next); + stack = stack->next; + i -= sizeof(stack->data) * 8; + } + return (stack->data[i>>5] & (1u<<(i&31))) != 0; } static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT { - assert(i >= 0 && i < sizeof(st->stack) * 8); + assert(i >= 0 && i <= 32767); // limited by the depth bit. + jl_bits_stack_t *stack = &st->stack; + while (i >= sizeof(stack->data) * 8) { + if (__unlikely(stack->next == NULL)) { + stack->next = (jl_bits_stack_t *)malloc(sizeof(jl_bits_stack_t)); + stack->next->next = NULL; + } + stack = stack->next; + i -= sizeof(stack->data) * 8; + } if (val) - st->stack[i>>5] |= (1u<<(i&31)); + stack->data[i>>5] |= (1u<<(i&31)); else - st->stack[i>>5] &= ~(1u<<(i&31)); + stack->data[i>>5] &= ~(1u<<(i&31)); +} + +#define has_next_union_state(e, R) ((((R) ? &(e)->Runions : &(e)->Lunions)->more) != 0) + +static int next_union_state(jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT +{ + jl_unionstate_t *state = R ? &e->Runions : &e->Lunions; + if (state->more == 0) + return 0; + // reset `used` and let `pick_union_decision` clean the stack. + state->used = state->more; + statestack_set(state, state->used - 1, 1); + return 1; } -#define push_unionstate(saved, src) \ - do { \ - (saved)->depth = (src)->depth; \ - (saved)->more = (src)->more; \ - (saved)->used = (src)->used; \ - (saved)->stack = alloca(((src)->used+7)/8); \ - memcpy((saved)->stack, &(src)->stack, ((src)->used+7)/8); \ +static int pick_union_decision(jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT +{ + jl_unionstate_t *state = R ? &e->Runions : &e->Lunions; + if (state->depth >= state->used) { + statestack_set(state, state->used, 0); + state->used++; + } + int ui = statestack_get(state, state->depth); + state->depth++; + if (ui == 0) + state->more = state->depth; // memorize that this was the deepest available choice + return ui; +} + +static jl_value_t *pick_union_element(jl_value_t *u JL_PROPAGATES_ROOT, jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT +{ + do { + if (pick_union_decision(e, R)) + u = ((jl_uniontype_t*)u)->b; + else + u = ((jl_uniontype_t*)u)->a; + } while (jl_is_uniontype(u)); + return u; +} + +#define push_unionstate(saved, src) \ + do { \ + (saved)->depth = (src)->depth; \ + (saved)->more = (src)->more; \ + (saved)->used = (src)->used; \ + jl_bits_stack_t *srcstack = &(src)->stack; \ + int pushbits = ((saved)->used+7)/8; \ + (saved)->stack = (uint8_t *)alloca(pushbits); \ + for (int n = 0; n < pushbits; n += sizeof(srcstack->data)) { \ + assert(srcstack != NULL); \ + int rest = pushbits - n; \ + if (rest > sizeof(srcstack->data)) \ + rest = sizeof(srcstack->data); \ + memcpy(&(saved)->stack[n], &srcstack->data, rest); \ + srcstack = srcstack->next; \ + } \ } while (0); -#define pop_unionstate(dst, saved) \ - do { \ - (dst)->depth = (saved)->depth; \ - (dst)->more = (saved)->more; \ - (dst)->used = (saved)->used; \ - memcpy(&(dst)->stack, (saved)->stack, ((saved)->used+7)/8); \ +#define pop_unionstate(dst, saved) \ + do { \ + (dst)->depth = (saved)->depth; \ + (dst)->more = (saved)->more; \ + (dst)->used = (saved)->used; \ + jl_bits_stack_t *dststack = &(dst)->stack; \ + int popbits = ((saved)->used+7)/8; \ + for (int n = 0; n < popbits; n += sizeof(dststack->data)) { \ + assert(dststack != NULL); \ + int rest = popbits - n; \ + if (rest > sizeof(dststack->data)) \ + rest = sizeof(dststack->data); \ + memcpy(&dststack->data, &(saved)->stack[n], rest); \ + dststack = dststack->next; \ + } \ } while (0); static int current_env_length(jl_stenv_t *e) @@ -264,6 +342,18 @@ static void free_env(jl_savedenv_t *se) JL_NOTSAFEPOINT se->buf = NULL; } +static void free_stenv(jl_stenv_t *e) JL_NOTSAFEPOINT +{ + for (int R = 0; R < 2; R++) { + jl_bits_stack_t *temp = R ? e->Runions.stack.next : e->Lunions.stack.next; + while (temp != NULL) { + jl_bits_stack_t *next = temp->next; + free(temp); + temp = next; + } + } +} + static void restore_env(jl_stenv_t *e, jl_savedenv_t *se, int root) JL_NOTSAFEPOINT { jl_value_t **roots = NULL; @@ -587,44 +677,6 @@ static jl_value_t *simple_meet(jl_value_t *a, jl_value_t *b, int overesi) static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); -#define has_next_union_state(e, R) ((((R) ? &(e)->Runions : &(e)->Lunions)->more) != 0) - -static int next_union_state(jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT -{ - jl_unionstate_t *state = R ? &e->Runions : &e->Lunions; - if (state->more == 0) - return 0; - // reset `used` and let `pick_union_decision` clean the stack. - state->used = state->more; - statestack_set(state, state->used - 1, 1); - return 1; -} - -static int pick_union_decision(jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT -{ - jl_unionstate_t *state = R ? &e->Runions : &e->Lunions; - if (state->depth >= state->used) { - statestack_set(state, state->used, 0); - state->used++; - } - int ui = statestack_get(state, state->depth); - state->depth++; - if (ui == 0) - state->more = state->depth; // memorize that this was the deepest available choice - return ui; -} - -static jl_value_t *pick_union_element(jl_value_t *u JL_PROPAGATES_ROOT, jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT -{ - do { - if (pick_union_decision(e, R)) - u = ((jl_uniontype_t*)u)->b; - else - u = ((jl_uniontype_t*)u)->a; - } while (jl_is_uniontype(u)); - return u; -} - static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow); // subtype for variable bounds consistency check. needs its own forall/exists environment. @@ -1513,37 +1565,12 @@ static int is_definite_length_tuple_type(jl_value_t *x) return k == JL_VARARG_NONE || k == JL_VARARG_INT; } -static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int *count, int *noRmore); - -static int may_contain_union_decision(jl_value_t *x, jl_stenv_t *e, jl_typeenv_t *log) JL_NOTSAFEPOINT +static int is_exists_typevar(jl_value_t *x, jl_stenv_t *e) { - if (x == NULL || x == (jl_value_t*)jl_any_type || x == jl_bottom_type) - return 0; - if (jl_is_unionall(x)) - return may_contain_union_decision(((jl_unionall_t *)x)->body, e, log); - if (jl_is_datatype(x)) { - jl_datatype_t *xd = (jl_datatype_t *)x; - for (int i = 0; i < jl_nparams(xd); i++) { - jl_value_t *param = jl_tparam(xd, i); - if (jl_is_vararg(param)) - param = jl_unwrap_vararg(param); - if (may_contain_union_decision(param, e, log)) - return 1; - } - return 0; - } if (!jl_is_typevar(x)) - return jl_is_type(x); - jl_typeenv_t *t = log; - while (t != NULL) { - if (x == (jl_value_t *)t->var) - return 1; - t = t->prev; - } - jl_typeenv_t newlog = { (jl_tvar_t*)x, NULL, log }; - jl_varbinding_t *xb = lookup(e, (jl_tvar_t *)x); - return may_contain_union_decision(xb ? xb->lb : ((jl_tvar_t *)x)->lb, e, &newlog) || - may_contain_union_decision(xb ? xb->ub : ((jl_tvar_t *)x)->ub, e, &newlog); + return 0; + jl_varbinding_t *vb = lookup(e, (jl_tvar_t *)x); + return vb && vb->right; } static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT @@ -1574,31 +1601,9 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t int kindy = !jl_has_free_typevars(y); if (kindx && kindy) return jl_subtype(x, y); - if (may_contain_union_decision(y, e, NULL) && pick_union_decision(e, 1) == 0) { - jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); - e->Lunions.used = e->Runions.used = 0; - e->Lunions.depth = e->Runions.depth = 0; - e->Lunions.more = e->Runions.more = 0; - int count = 0, noRmore = 0; - sub = _forall_exists_subtype(x, y, e, param, &count, &noRmore); - pop_unionstate(&e->Runions, &oldRunions); - // We could skip the slow path safely if - // 1) `_∀_∃_subtype` has tested all cases - // 2) `_∀_∃_subtype` returns 1 && `x` and `y` contain no ∃ typevar - // Once `limit_slow == 1`, also skip it if - // 1) `_∀_∃_subtype` returns 0 - // 2) the left `Union` looks big - // TODO: `limit_slow` ignores complexity from inner `local_∀_exists_subtype`. - if (limit_slow == -1) - limit_slow = kindx || kindy; - int skip = noRmore || (limit_slow && (count > 3 || !sub)) || - (sub && (kindx || !has_exists_typevar(x, e)) && - (kindy || !has_exists_typevar(y, e))); - if (skip) - e->Runions.more = oldRmore; - } - else { - // slow path + int has_exists = (!kindx && has_exists_typevar(x, e)) || + (!kindy && has_exists_typevar(y, e)); + if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) { e->Lunions.used = 0; while (1) { e->Lunions.more = 0; @@ -1607,7 +1612,51 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t if (!sub || !next_union_state(e, 0)) break; } + return sub; } + if (limit_slow == -1) + limit_slow = kindx || kindy; + jl_savedenv_t se; + save_env(e, &se, has_exists); + int count, limited = 0, ini_count = 0; + jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL}; + while (1) { + count = ini_count; + if (ini_count == 0) + e->Lunions.used = 0; + else + pop_unionstate(&e->Lunions, &latestLunions); + while (1) { + e->Lunions.more = 0; + e->Lunions.depth = 0; + if (count < 4) count++; + sub = subtype(x, y, e, param); + if (limit_slow && count == 4) + limited = 1; + if (!sub || !next_union_state(e, 0)) + break; + if (limited || !has_exists || e->Runions.more == oldRmore) { + // re-save env and freeze the ∃decision for previous ∀Union + // Note: We could ignore the rest `∃Union` decisions if `x` and `y` + // contain no ∃ typevar, as they have no effect on env. + ini_count = count; + push_unionstate(&latestLunions, &e->Lunions); + re_save_env(e, &se, has_exists); + e->Runions.more = oldRmore; + } + } + if (sub || e->Runions.more == oldRmore) + break; + assert(e->Runions.more > oldRmore); + next_union_state(e, 1); + restore_env(e, &se, has_exists); // also restore Rdepth here + e->Runions.more = oldRmore; + } + if (!sub) + assert(e->Runions.more == oldRmore); + else if (limited || !has_exists) + e->Runions.more = oldRmore; + free_env(&se); return sub; } @@ -1677,7 +1726,7 @@ static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_savede } } -static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int *count, int *noRmore) +static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { // The depth recursion has the following shape, after simplification: // ∀₁ @@ -1689,12 +1738,8 @@ static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, i e->Lunions.used = 0; int sub; - if (count) *count = 0; - if (noRmore) *noRmore = 1; while (1) { sub = exists_subtype(x, y, e, &se, param); - if (count) *count = (*count < 4) ? *count + 1 : 4; - if (noRmore) *noRmore = *noRmore && e->Runions.more == 0; if (!sub || !next_union_state(e, 0)) break; re_save_env(e, &se, 1); @@ -1704,11 +1749,6 @@ static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, i return sub; } -static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) -{ - return _forall_exists_subtype(x, y, e, param, NULL, NULL); -} - static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) { e->vars = NULL; @@ -1728,6 +1768,8 @@ static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) e->Lunions.depth = 0; e->Runions.depth = 0; e->Lunions.more = 0; e->Runions.more = 0; e->Lunions.used = 0; e->Runions.used = 0; + e->Lunions.stack.next = NULL; + e->Runions.stack.next = NULL; } // subtyping entry points @@ -2157,6 +2199,7 @@ JL_DLLEXPORT int jl_subtype_env(jl_value_t *x, jl_value_t *y, jl_value_t **env, } init_stenv(&e, env, envsz); int subtype = forall_exists_subtype(x, y, &e, 0); + free_stenv(&e); assert(obvious_subtype == 3 || obvious_subtype == subtype || jl_has_free_typevars(x) || jl_has_free_typevars(y)); #ifndef NDEBUG if (obvious_subtype == 0 || (obvious_subtype == 1 && envsz == 0)) @@ -2249,6 +2292,7 @@ JL_DLLEXPORT int jl_types_equal(jl_value_t *a, jl_value_t *b) { init_stenv(&e, NULL, 0); int subtype = forall_exists_subtype(a, b, &e, 0); + free_stenv(&e); assert(subtype_ab == 3 || subtype_ab == subtype || jl_has_free_typevars(a) || jl_has_free_typevars(b)); #ifndef NDEBUG if (subtype_ab != 0 && subtype_ab != 1) // ensures that running in a debugger doesn't change the result @@ -2265,6 +2309,7 @@ JL_DLLEXPORT int jl_types_equal(jl_value_t *a, jl_value_t *b) { init_stenv(&e, NULL, 0); int subtype = forall_exists_subtype(b, a, &e, 0); + free_stenv(&e); assert(subtype_ba == 3 || subtype_ba == subtype || jl_has_free_typevars(a) || jl_has_free_typevars(b)); #ifndef NDEBUG if (subtype_ba != 0 && subtype_ba != 1) // ensures that running in a debugger doesn't change the result @@ -4230,7 +4275,9 @@ static jl_value_t *intersect_types(jl_value_t *x, jl_value_t *y, int emptiness_o init_stenv(&e, NULL, 0); e.intersection = e.ignore_free = 1; e.emptiness_only = emptiness_only; - return intersect_all(x, y, &e); + jl_value_t *ans = intersect_all(x, y, &e); + free_stenv(&e); + return ans; } JL_DLLEXPORT jl_value_t *jl_intersect_types(jl_value_t *x, jl_value_t *y) @@ -4407,6 +4454,7 @@ jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t * memset(env, 0, szb*sizeof(void*)); e.envsz = szb; *ans = intersect_all(a, b, &e); + free_stenv(&e); if (*ans == jl_bottom_type) goto bot; // TODO: code dealing with method signatures is not able to handle unions, so if // `a` and `b` are both tuples, we need to be careful and may not return a union, From 0d09f3d0baa8843bd41fed013dad3fd9d69ae28d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 11 Oct 2024 13:23:52 -0400 Subject: [PATCH 365/548] rearrange jl_delete_thread to be thread-safe (#56097) Prior to this, especially on macOS, the gc-safepoint here would cause the process to segfault as we had already freed the current_task state. Rearrange this code so that the GC interactions (except for the atomic store to current_task) are all handled before entering GC safe, and then signaling the thread is deleted (via setting current_task = NULL, published by jl_unlock_profile_wr to other threads) is last. ``` ERROR: Exception handler triggered on unmanaged thread. Process 53827 stopped * thread #5, stop reason = EXC_BAD_ACCESS (code=2, address=0x100018008) frame #0: 0x0000000100b74344 libjulia-internal.1.12.0.dylib`jl_delete_thread [inlined] jl_gc_state_set(ptls=0x000000011f8b3200, state='\x02', old_state=) at julia_threads.h:272:9 [opt] 269 assert(old_state != JL_GC_CONCURRENT_COLLECTOR_THREAD); 270 jl_atomic_store_release(&ptls->gc_state, state); 271 if (state == JL_GC_STATE_UNSAFE || old_state == JL_GC_STATE_UNSAFE) -> 272 jl_gc_safepoint_(ptls); 273 return old_state; 274 } 275 STATIC_INLINE int8_t jl_gc_state_save_and_set(jl_ptls_t ptls, Target 0: (julia) stopped. (lldb) up frame #1: 0x0000000100b74320 libjulia-internal.1.12.0.dylib`jl_delete_thread [inlined] jl_gc_state_save_and_set(ptls=0x000000011f8b3200, state='\x02') at julia_threads.h:278:12 [opt] 275 STATIC_INLINE int8_t jl_gc_state_save_and_set(jl_ptls_t ptls, 276 int8_t state) 277 { -> 278 return jl_gc_state_set(ptls, state, jl_atomic_load_relaxed(&ptls->gc_state)); 279 } 280 #ifdef __clang_gcanalyzer__ 281 // these might not be a safepoint (if they are no-op safe=>safe transitions), but we have to assume it could be (statically) (lldb) frame #2: 0x0000000100b7431c libjulia-internal.1.12.0.dylib`jl_delete_thread(value=0x000000011f8b3200) at threading.c:537:11 [opt] 534 ptls->root_task = NULL; 535 jl_free_thread_gc_state(ptls); 536 // then park in safe-region -> 537 (void)jl_gc_safe_enter(ptls); 538 } ``` (test incorporated into https://github.com/JuliaLang/julia/pull/55793) --- src/threading.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/threading.c b/src/threading.c index 44b1192528531..c26028d2f3da2 100644 --- a/src/threading.c +++ b/src/threading.c @@ -464,6 +464,30 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER // prior unsafe-region (before we let it release the stack memory) (void)jl_gc_unsafe_enter(ptls); scheduler_delete_thread(ptls); + // need to clear pgcstack and eh, but we can clear everything now too + jl_task_t *ct = jl_atomic_load_relaxed(&ptls->current_task); + jl_task_frame_noreturn(ct); + if (jl_set_task_tid(ptls->root_task, ptls->tid)) { + // the system will probably free this stack memory soon + // so prevent any other thread from accessing it later + if (ct != ptls->root_task) + jl_task_frame_noreturn(ptls->root_task); + } + else { + // Uh oh. The user cleared the sticky bit so it started running + // elsewhere, then called pthread_exit on this thread from another + // Task, which will free the stack memory of that root task soon. This + // is not recoverable. Though we could just hang here, a fatal message + // is likely better. + jl_safe_printf("fatal: thread exited from wrong Task.\n"); + abort(); + } + ptls->previous_exception = NULL; + // allow the page root_task is on to be freed + ptls->root_task = NULL; + jl_free_thread_gc_state(ptls); + // park in safe-region from here on (this may run GC again) + (void)jl_gc_safe_enter(ptls); // try to free some state we do not need anymore #ifndef _OS_WINDOWS_ void *signal_stack = ptls->signal_stack; @@ -502,21 +526,7 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER #else pthread_mutex_lock(&in_signal_lock); #endif - // need to clear pgcstack and eh, but we can clear everything now too - jl_task_frame_noreturn(jl_atomic_load_relaxed(&ptls->current_task)); - if (jl_set_task_tid(ptls->root_task, ptls->tid)) { - // the system will probably free this stack memory soon - // so prevent any other thread from accessing it later - jl_task_frame_noreturn(ptls->root_task); - } - else { - // Uh oh. The user cleared the sticky bit so it started running - // elsewhere, then called pthread_exit on this thread. This is not - // recoverable. Though we could just hang here, a fatal message is better. - jl_safe_printf("fatal: thread exited from wrong Task.\n"); - abort(); - } - jl_atomic_store_relaxed(&ptls->current_task, NULL); // dead + jl_atomic_store_relaxed(&ptls->current_task, NULL); // indicate dead // finally, release all of the locks we had grabbed #ifdef _OS_WINDOWS_ jl_unlock_profile_wr(); @@ -529,12 +539,6 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER #endif free(ptls->bt_data); small_arraylist_free(&ptls->locks); - ptls->previous_exception = NULL; - // allow the page root_task is on to be freed - ptls->root_task = NULL; - jl_free_thread_gc_state(ptls); - // then park in safe-region - (void)jl_gc_safe_enter(ptls); } //// debugging hack: if we are exiting too fast for error message printing on threads, From 22cde34ae3cee38c2829245e0937e8be173df6cb Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 11 Oct 2024 21:05:00 +0200 Subject: [PATCH 366/548] OpenBLAS: Use dynamic architecture support on AArch64. (#56107) We already do so on Yggdrasil, so this just makes both source and binary builds behave similarly. Closes https://github.com/JuliaLang/julia/issues/56075 --- Make.inc | 4 +--- deps/openblas.mk | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Make.inc b/Make.inc index f078a0c84f806..53aee8a269732 100644 --- a/Make.inc +++ b/Make.inc @@ -1002,16 +1002,14 @@ endif # If we are running on ARM, set certain options automatically ifneq (,$(findstring arm,$(ARCH))) JCFLAGS += -fsigned-char -USE_BLAS64:=0 OPENBLAS_DYNAMIC_ARCH:=0 OPENBLAS_TARGET_ARCH:=ARMV7 +BINARY:=32 endif # If we are running on aarch64 (e.g. ARMv8 or ARM64), set certain options automatically ifneq (,$(findstring aarch64,$(ARCH))) -OPENBLAS_DYNAMIC_ARCH:=0 OPENBLAS_TARGET_ARCH:=ARMV8 -USE_BLAS64:=1 BINARY:=64 endif diff --git a/deps/openblas.mk b/deps/openblas.mk index affd1c7a7aa55..fbaa2e7a0fb92 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -31,6 +31,7 @@ endif endif # 64-bit BLAS interface +$(error USE_BLAS64: $(USE_BLAS64)) ifeq ($(USE_BLAS64), 1) OPENBLAS_BUILD_OPTS += INTERFACE64=1 SYMBOLSUFFIX="$(OPENBLAS_SYMBOLSUFFIX)" LIBPREFIX="libopenblas$(OPENBLAS_LIBNAMESUFFIX)" ifeq ($(OS), Darwin) From ad85277ee468389cc5b09a1d1eb357f1b558b412 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:30:21 -0400 Subject: [PATCH 367/548] IRShow: label builtin / intrinsic / dynamic calls in `code_typed` (#56036) This makes it much easier to spot dynamic dispatches --- base/compiler/abstractinterpretation.jl | 1 + base/compiler/optimize.jl | 25 +++- base/compiler/ssair/ir.jl | 36 ++++-- base/compiler/ssair/show.jl | 126 +++++++++++++++---- base/compiler/types.jl | 2 + base/deprecated.jl | 1 + doc/src/base/reflection.md | 8 +- doc/src/devdocs/inference.md | 12 +- stdlib/InteractiveUtils/src/codeview.jl | 10 +- stdlib/InteractiveUtils/test/highlighting.jl | 5 +- test/compiler/EscapeAnalysis/EAUtils.jl | 2 +- 11 files changed, 179 insertions(+), 49 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 70623453e1666..04a62700e9de7 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -3114,6 +3114,7 @@ end abstract_eval_ssavalue(s::SSAValue, sv::InferenceState) = abstract_eval_ssavalue(s, sv.ssavaluetypes) function abstract_eval_ssavalue(s::SSAValue, ssavaluetypes::Vector{Any}) + (1 ≤ s.id ≤ length(ssavaluetypes)) || throw(InvalidIRError()) typ = ssavaluetypes[s.id] if typ === NOT_FOUND return Bottom diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 02f6b46e2e73f..5f0c5077688f8 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -411,26 +411,35 @@ function argextype(@nospecialize(x), compact::IncrementalCompact, sptypes::Vecto isa(x, AnySSAValue) && return types(compact)[x] return argextype(x, compact, sptypes, compact.ir.argtypes) end -argextype(@nospecialize(x), src::CodeInfo, sptypes::Vector{VarState}) = argextype(x, src, sptypes, src.slottypes::Vector{Any}) +function argextype(@nospecialize(x), src::CodeInfo, sptypes::Vector{VarState}) + return argextype(x, src, sptypes, src.slottypes::Union{Vector{Any},Nothing}) +end function argextype( @nospecialize(x), src::Union{IRCode,IncrementalCompact,CodeInfo}, - sptypes::Vector{VarState}, slottypes::Vector{Any}) + sptypes::Vector{VarState}, slottypes::Union{Vector{Any},Nothing}) if isa(x, Expr) if x.head === :static_parameter - return sptypes[x.args[1]::Int].typ + idx = x.args[1]::Int + (1 ≤ idx ≤ length(sptypes)) || throw(InvalidIRError()) + return sptypes[idx].typ elseif x.head === :boundscheck return Bool elseif x.head === :copyast + length(x.args) == 0 && throw(InvalidIRError()) return argextype(x.args[1], src, sptypes, slottypes) end Core.println("argextype called on Expr with head ", x.head, " which is not valid for IR in argument-position.") @assert false elseif isa(x, SlotNumber) + slottypes === nothing && return Any + (1 ≤ x.id ≤ length(slottypes)) || throw(InvalidIRError()) return slottypes[x.id] elseif isa(x, SSAValue) return abstract_eval_ssavalue(x, src) elseif isa(x, Argument) + slottypes === nothing && return Any + (1 ≤ x.n ≤ length(slottypes)) || throw(InvalidIRError()) return slottypes[x.n] elseif isa(x, QuoteNode) return Const(x.value) @@ -444,7 +453,15 @@ function argextype( return Const(x) end end -abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) = abstract_eval_ssavalue(s, src.ssavaluetypes::Vector{Any}) +function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) + ssavaluetypes = src.ssavaluetypes + if ssavaluetypes isa Int + (1 ≤ s.id ≤ ssavaluetypes) || throw(InvalidIRError()) + return Any + else + return abstract_eval_ssavalue(s, ssavaluetypes::Vector{Any}) + end +end abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = types(src)[s] """ diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index fdcb4621c5c0f..90eab43a3f25b 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -313,6 +313,7 @@ Instruction(is::InstructionStream) = Instruction(is, add_new_idx!(is)) fldarray = getfield(getfield(node, :data), fld) fldidx = getfield(node, :idx) (fld === :line) && return (fldarray[3fldidx-2], fldarray[3fldidx-1], fldarray[3fldidx-0]) + (1 ≤ fldidx ≤ length(fldarray)) || throw(InvalidIRError()) return fldarray[fldidx] end @inline function setindex!(node::Instruction, @nospecialize(val), fld::Symbol) @@ -481,11 +482,16 @@ function block_for_inst(ir::IRCode, inst::Int) end function getindex(ir::IRCode, s::SSAValue) + id = s.id + (id ≥ 1) || throw(InvalidIRError()) nstmts = length(ir.stmts) - if s.id <= nstmts - return ir.stmts[s.id] + if id <= nstmts + return ir.stmts[id] else - return ir.new_nodes.stmts[s.id - nstmts] + id -= nstmts + stmts = ir.new_nodes.stmts + (id ≤ length(stmts)) || throw(InvalidIRError()) + return stmts[id] end end @@ -801,12 +807,13 @@ end types(ir::Union{IRCode, IncrementalCompact}) = TypesView(ir) function getindex(compact::IncrementalCompact, ssa::SSAValue) - @assert ssa.id < compact.result_idx + (1 ≤ ssa.id ≤ compact.result_idx) || throw(InvalidIRError()) return compact.result[ssa.id] end function getindex(compact::IncrementalCompact, ssa::OldSSAValue) id = ssa.id + (id ≥ 1) || throw(InvalidIRError()) if id < compact.idx new_idx = compact.ssa_rename[id]::Int return compact.result[new_idx] @@ -818,12 +825,15 @@ function getindex(compact::IncrementalCompact, ssa::OldSSAValue) return compact.ir.new_nodes.stmts[id] end id -= length(compact.ir.new_nodes) + (id ≤ length(compact.pending_nodes.stmts)) || throw(InvalidIRError()) return compact.pending_nodes.stmts[id] end function getindex(compact::IncrementalCompact, ssa::NewSSAValue) if ssa.id < 0 - return compact.new_new_nodes.stmts[-ssa.id] + stmts = compact.new_new_nodes.stmts + (-ssa.id ≤ length(stmts)) || throw(InvalidIRError()) + return stmts[-ssa.id] else return compact[SSAValue(ssa.id)] end @@ -1069,6 +1079,7 @@ function getindex(view::TypesView, v::OldSSAValue) id = v.id ir = view.ir.ir stmts = ir.stmts + (id ≥ 1) || throw(InvalidIRError()) if id <= length(stmts) return stmts[id][:type] end @@ -1077,7 +1088,9 @@ function getindex(view::TypesView, v::OldSSAValue) return ir.new_nodes.stmts[id][:type] end id -= length(ir.new_nodes) - return view.ir.pending_nodes.stmts[id][:type] + stmts = view.ir.pending_nodes.stmts + (id ≤ length(stmts)) || throw(InvalidIRError()) + return stmts[id][:type] end function kill_current_use!(compact::IncrementalCompact, @nospecialize(val)) @@ -1204,20 +1217,27 @@ end getindex(view::TypesView, idx::SSAValue) = getindex(view, idx.id) function getindex(view::TypesView, idx::Int) + (idx ≥ 1) || throw(InvalidIRError()) if isa(view.ir, IncrementalCompact) && idx < view.ir.result_idx return view.ir.result[idx][:type] elseif isa(view.ir, IncrementalCompact) && view.ir.renamed_new_nodes if idx <= length(view.ir.result) return view.ir.result[idx][:type] else - return view.ir.new_new_nodes.stmts[idx - length(view.ir.result)][:type] + idx -= length(view.ir.result) + stmts = view.ir.new_new_nodes.stmts + (idx ≤ length(stmts)) || throw(InvalidIRError()) + return stmts[idx][:type] end else ir = isa(view.ir, IncrementalCompact) ? view.ir.ir : view.ir if idx <= length(ir.stmts) return ir.stmts[idx][:type] else - return ir.new_nodes.stmts[idx - length(ir.stmts)][:type] + idx -= length(ir.stmts) + stmts = ir.new_nodes.stmts + (idx ≤ length(stmts)) || throw(InvalidIRError()) + return stmts[idx][:type] end end end diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 7d936a1688aba..f3e11445d6c6c 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -14,6 +14,8 @@ end import Base: show_unquoted using Base: printstyled, with_output_color, prec_decl, @invoke +using Core.Compiler: VarState, InvalidIRError, argextype, widenconst, singleton_type, + sptypes_from_meth_instance, EMPTY_SPTYPES function Base.show(io::IO, cfg::CFG) print(io, "CFG with $(length(cfg.blocks)) blocks:") @@ -31,7 +33,50 @@ function Base.show(io::IO, cfg::CFG) end end -function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxlength_idx::Int, color::Bool, show_type::Bool) +function maybe_argextype( + @nospecialize(x), + src::Union{IRCode,IncrementalCompact,CodeInfo}, + sptypes::Vector{VarState}, +) + return try + argextype(x, src, sptypes) + catch err + !(err isa InvalidIRError) && rethrow() + nothing + end +end + +const inlined_apply_iterate_types = Union{Array,Memory,Tuple,NamedTuple,Core.SimpleVector} + +function builtin_call_has_dispatch( + @nospecialize(f), + args::Vector{Any}, + src::Union{IRCode,IncrementalCompact,CodeInfo}, + sptypes::Vector{VarState}, +) + if f === Core._apply_iterate && length(args) >= 3 + # The implementation of _apply_iterate has hand-inlined implementations + # for (v::Union{Tuple,NamedTuple,Memory,Array,SimpleVector}...) + # which perform no dynamic dispatch + constructort = maybe_argextype(args[3], src, sptypes) + if constructort === nothing || !(widenconst(constructort) <: Core.Builtin) + return true + end + for arg in args[4:end] + argt = maybe_argextype(arg, src, sptypes) + if argt === nothing || !(widenconst(argt) <: inlined_apply_iterate_types) + return true + end + end + elseif (f === Core._apply_pure || f === Core._call_in_world || f === Core._call_in_world_total || f === Core._call_latest) + # These apply-like builtins are effectively dynamic calls + return true + end + return false +end + +function print_stmt(io::IO, idx::Int, @nospecialize(stmt), code::Union{IRCode,CodeInfo,IncrementalCompact}, + sptypes::Vector{VarState}, used::BitSet, maxlength_idx::Int, color::Bool, show_type::Bool, label_dynamic_calls::Bool) if idx in used idx_s = string(idx) pad = " "^(maxlength_idx - length(idx_s) + 1) @@ -51,7 +96,7 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxleng elseif isexpr(stmt, :invoke) && length(stmt.args) >= 2 && isa(stmt.args[1], MethodInstance) stmt = stmt::Expr # TODO: why is this here, and not in Base.show_unquoted - print(io, "invoke ") + printstyled(io, " invoke "; color = :light_black) mi = stmt.args[1]::Core.MethodInstance show_unquoted(io, stmt.args[2], indent) print(io, "(") @@ -66,6 +111,28 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxleng end join(io, (print_arg(i) for i = 3:length(stmt.args)), ", ") print(io, ")") + elseif isexpr(stmt, :call) && length(stmt.args) >= 1 && label_dynamic_calls + ft = maybe_argextype(stmt.args[1], code, sptypes) + f = singleton_type(ft) + if isa(f, Core.IntrinsicFunction) + printstyled(io, "intrinsic "; color = :light_black) + elseif isa(f, Core.Builtin) + if builtin_call_has_dispatch(f, stmt.args, code, sptypes) + printstyled(io, "dynamic builtin "; color = :yellow) + else + printstyled(io, " builtin "; color = :light_black) + end + elseif ft === nothing + # This should only happen when, e.g., printing a call that targets + # an out-of-bounds SSAValue or similar + # (i.e. under normal circumstances, dead code) + printstyled(io, " unknown "; color = :light_black) + elseif widenconst(ft) <: Core.Builtin + printstyled(io, "dynamic builtin "; color = :yellow) + else + printstyled(io, " dynamic "; color = :yellow) + end + show_unquoted(io, stmt, indent, show_type ? prec_decl : 0) # given control flow information, we prefer to print these with the basic block #, instead of the ssa % elseif isa(stmt, EnterNode) print(io, "enter #", stmt.catch_dest, "") @@ -563,16 +630,28 @@ end - `should_print_stmt(idx::Int) -> Bool`: whether the statement at index `idx` should be printed as part of the IR or not - `bb_color`: color used for printing the basic block brackets on the left +- `label_dynamic_calls`: whether to label calls as dynamic / builtin / intrinsic """ struct IRShowConfig line_info_preprinter line_info_postprinter should_print_stmt bb_color::Symbol - function IRShowConfig(line_info_preprinter, line_info_postprinter=default_expr_type_printer; - should_print_stmt=Returns(true), bb_color::Symbol=:light_black) - return new(line_info_preprinter, line_info_postprinter, should_print_stmt, bb_color) - end + label_dynamic_calls::Bool + + IRShowConfig( + line_info_preprinter, + line_info_postprinter=default_expr_type_printer; + should_print_stmt=Returns(true), + bb_color::Symbol=:light_black, + label_dynamic_calls=true + ) = new( + line_info_preprinter, + line_info_postprinter, + should_print_stmt, + bb_color, + label_dynamic_calls + ) end struct _UNDEF @@ -628,13 +707,14 @@ end # at index `idx`. This function is repeatedly called until it returns `nothing`. # to iterate nodes that are to be inserted after the statement, set `attach_after=true`. function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, config::IRShowConfig, - used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false) + sptypes::Vector{VarState}, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false) return show_ir_stmt(io, code, idx, config.line_info_preprinter, config.line_info_postprinter, - used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color) + sptypes, used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color, config.label_dynamic_calls) end function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, line_info_preprinter, line_info_postprinter, - used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false, bb_color=:light_black) + sptypes::Vector{VarState}, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false, + bb_color=:light_black, label_dynamic_calls::Bool=true) stmt = _stmt(code, idx) type = _type(code, idx) max_bb_idx_size = length(string(length(cfg.blocks))) @@ -693,7 +773,7 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, show_type = should_print_ssa_type(new_node_inst) let maxlength_idx=maxlength_idx, show_type=show_type with_output_color(:green, io) do io′ - print_stmt(io′, node_idx, new_node_inst, used, maxlength_idx, false, show_type) + print_stmt(io′, node_idx, new_node_inst, code, sptypes, used, maxlength_idx, false, show_type, label_dynamic_calls) end end @@ -722,7 +802,7 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, stmt = statement_indices_to_labels(stmt, cfg) end show_type = type !== nothing && should_print_ssa_type(stmt) - print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) + print_stmt(io, idx, stmt, code, sptypes, used, maxlength_idx, true, show_type, label_dynamic_calls) if type !== nothing # ignore types for pre-inference code if type === UNDEF # This is an error, but can happen if passes don't update their type information @@ -881,10 +961,10 @@ end default_config(code::CodeInfo) = IRShowConfig(statementidx_lineinfo_printer(code)) function show_ir_stmts(io::IO, ir::Union{IRCode, CodeInfo, IncrementalCompact}, inds, config::IRShowConfig, - used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing)) + sptypes::Vector{VarState}, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing)) for idx in inds if config.should_print_stmt(ir, idx, used) - bb_idx = show_ir_stmt(io, ir, idx, config, used, cfg, bb_idx; pop_new_node!) + bb_idx = show_ir_stmt(io, ir, idx, config, sptypes, used, cfg, bb_idx; pop_new_node!) elseif bb_idx <= length(cfg.blocks) && idx == cfg.blocks[bb_idx].stmts.stop bb_idx += 1 end @@ -904,7 +984,7 @@ function show_ir(io::IO, ir::IRCode, config::IRShowConfig=default_config(ir); cfg = ir.cfg maxssaid = length(ir.stmts) + Core.Compiler.length(ir.new_nodes) let io = IOContext(io, :maxssaid=>maxssaid) - show_ir_stmts(io, ir, 1:length(ir.stmts), config, used, cfg, 1; pop_new_node!) + show_ir_stmts(io, ir, 1:length(ir.stmts), config, ir.sptypes, used, cfg, 1; pop_new_node!) end finish_show_ir(io, cfg, config) end @@ -913,8 +993,12 @@ function show_ir(io::IO, ci::CodeInfo, config::IRShowConfig=default_config(ci); pop_new_node! = Returns(nothing)) used = stmts_used(io, ci) cfg = compute_basic_blocks(ci.code) + parent = ci.parent + sptypes = if parent isa MethodInstance + sptypes_from_meth_instance(parent) + else EMPTY_SPTYPES end let io = IOContext(io, :maxssaid=>length(ci.code)) - show_ir_stmts(io, ci, 1:length(ci.code), config, used, cfg, 1; pop_new_node!) + show_ir_stmts(io, ci, 1:length(ci.code), config, sptypes, used, cfg, 1; pop_new_node!) end finish_show_ir(io, cfg, config) end @@ -963,8 +1047,8 @@ function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=defau pop_new_node! = new_nodes_iter(compact) maxssaid = length(compact.result) + Core.Compiler.length(compact.new_new_nodes) bb_idx = let io = IOContext(io, :maxssaid=>maxssaid) - show_ir_stmts(io, compact, 1:compact.result_idx-1, config, used_compacted, - compact_cfg, 1; pop_new_node!) + show_ir_stmts(io, compact, 1:compact.result_idx-1, config, compact.ir.sptypes, + used_compacted, compact_cfg, 1; pop_new_node!) end @@ -995,13 +1079,13 @@ function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=defau let io = IOContext(io, :maxssaid=>maxssaid) # first show any new nodes to be attached after the last compacted statement if compact.idx > 1 - show_ir_stmt(io, compact.ir, compact.idx-1, config, used_uncompacted, - uncompacted_cfg, bb_idx; pop_new_node!, only_after=true) + show_ir_stmt(io, compact.ir, compact.idx-1, config, compact.ir.sptypes, + used_uncompacted, uncompacted_cfg, bb_idx; pop_new_node!, only_after=true) end # then show the actual uncompacted IR - show_ir_stmts(io, compact.ir, compact.idx:length(stmts), config, used_uncompacted, - uncompacted_cfg, bb_idx; pop_new_node!) + show_ir_stmts(io, compact.ir, compact.idx:length(stmts), config, compact.ir.sptypes, + used_uncompacted, uncompacted_cfg, bb_idx; pop_new_node!) end finish_show_ir(io, uncompacted_cfg, config) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index ecf2417fd6199..210adf7be96b2 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -28,6 +28,8 @@ the following methods to satisfy the `AbstractInterpreter` API requirement: abstract type AbstractLattice end +struct InvalidIRError <: Exception end + struct ArgInfo fargs::Union{Nothing,Vector{Any}} argtypes::Vector{Any} diff --git a/base/deprecated.jl b/base/deprecated.jl index b43a4227d42c4..953de358a68ee 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -26,6 +26,7 @@ const __internal_changes_list = ( :miuninferredrm, :codeinfonargs, # #54341 :ocnopartial, + :printcodeinfocalls, # Add new change names above this line ) diff --git a/doc/src/base/reflection.md b/doc/src/base/reflection.md index 0f0af140b605f..9228fb38322df 100644 --- a/doc/src/base/reflection.md +++ b/doc/src/base/reflection.md @@ -100,9 +100,9 @@ as assignments, branches, and calls: ```jldoctest; setup = (using Base: +, sin) julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] )) :($(Expr(:thunk, CodeInfo( -1 ─ %1 = 1 + 2 -│ %2 = sin(0.5) -│ %3 = Base.vect(%1, %2) +1 ─ %1 = dynamic 1 + 2 +│ %2 = dynamic sin(0.5) +│ %3 = dynamic Base.vect(%1, %2) └── return %3 )))) ``` @@ -146,7 +146,7 @@ debug information printed. julia> InteractiveUtils.@code_typed debuginfo=:source +(1,1) CodeInfo( @ int.jl:87 within `+` -1 ─ %1 = Base.add_int(x, y)::Int64 +1 ─ %1 = intrinsic Base.add_int(x, y)::Int64 └── return %1 ) => Int64 ``` diff --git a/doc/src/devdocs/inference.md b/doc/src/devdocs/inference.md index c885441e4dd84..98f020dda1d8e 100644 --- a/doc/src/devdocs/inference.md +++ b/doc/src/devdocs/inference.md @@ -98,16 +98,16 @@ as follows: julia> Base.print_statement_costs(stdout, map, (typeof(sqrt), Tuple{Int},)) # map(sqrt, (2,)) map(f, t::Tuple{Any}) @ Base tuple.jl:281 0 1 ─ %1 = $(Expr(:boundscheck, true))::Bool - 0 │ %2 = Base.getfield(_3, 1, %1)::Int64 - 1 │ %3 = Base.sitofp(Float64, %2)::Float64 - 0 │ %4 = Base.lt_float(%3, 0.0)::Bool + 0 │ %2 = builtin Base.getfield(_3, 1, %1)::Int64 + 1 │ %3 = intrinsic Base.sitofp(Float64, %2)::Float64 + 0 │ %4 = intrinsic Base.lt_float(%3, 0.0)::Bool 0 └── goto #3 if not %4 - 0 2 ─ invoke Base.Math.throw_complex_domainerror(:sqrt::Symbol, %3::Float64)::Union{} + 0 2 ─ invoke Base.Math.throw_complex_domainerror(:sqrt::Symbol, %3::Float64)::Union{} 0 └── unreachable - 20 3 ─ %8 = Base.Math.sqrt_llvm(%3)::Float64 + 20 3 ─ %8 = intrinsic Base.Math.sqrt_llvm(%3)::Float64 0 └── goto #4 0 4 ─ goto #5 - 0 5 ─ %11 = Core.tuple(%8)::Tuple{Float64} + 0 5 ─ %11 = builtin Core.tuple(%8)::Tuple{Float64} 0 └── return %11 ``` diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index 9f1538cd4a7fe..e3ef0a14a6608 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -54,7 +54,7 @@ function is_expected_union(u::Union) return true end -function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter) +function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(rettype), nargs::Int; lineprinter, label_dynamic_calls) if src.slotnames !== nothing slotnames = Base.sourceinfo_slotnames(src) io = IOContext(io, :SOURCE_SLOTNAMES => slotnames) @@ -74,7 +74,7 @@ function print_warntype_codeinfo(io::IO, src::Core.CodeInfo, @nospecialize(retty print(io, "Body") warntype_type_printer(io; type=rettype, used=true) println(io) - irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer) + irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer; label_dynamic_calls) Base.IRShow.show_ir(io, src, irshow_config) println(io) end @@ -154,7 +154,8 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_t nargs::Int = 0 if isa(f, Core.OpaqueClosure) isa(f.source, Method) && (nargs = f.source.nargs) - print_warntype_codeinfo(io, Base.code_typed_opaque_closure(f, tt)[1]..., nargs; lineprinter) + print_warntype_codeinfo(io, Base.code_typed_opaque_closure(f, tt)[1]..., nargs; + lineprinter, label_dynamic_calls = optimize) return nothing end tt = Base.signature_type(f, tt) @@ -167,7 +168,8 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_t mi.def isa Method && (nargs = (mi.def::Method).nargs) print_warntype_mi(io, mi) if src isa Core.CodeInfo - print_warntype_codeinfo(io, src, src.rettype, nargs; lineprinter) + print_warntype_codeinfo(io, src, src.rettype, nargs; + lineprinter, label_dynamic_calls = optimize) else println(io, " inference not successful") end diff --git a/stdlib/InteractiveUtils/test/highlighting.jl b/stdlib/InteractiveUtils/test/highlighting.jl index 3531618e10dfc..f49464557f926 100644 --- a/stdlib/InteractiveUtils/test/highlighting.jl +++ b/stdlib/InteractiveUtils/test/highlighting.jl @@ -34,7 +34,10 @@ end c = Base.text_colors[Base.warn_color()] InteractiveUtils.highlighting[:warntype] = false code_warntype(IOContext(io, :color => true), f, Tuple{Int64}) - @test !occursin(c, String(take!(io))) + @test !any([ + occursin("Body", line) && occursin(c, line) + for line in split(String(take!(io)), "\n") + ]) InteractiveUtils.highlighting[:warntype] = true code_warntype(IOContext(io, :color => true), f, Tuple{Int64}) @test occursin(c, String(take!(io))) diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index b8ad4589db626..c41e61e231892 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -291,7 +291,7 @@ function print_with_info(preprint, postprint, io::IO, ir::IRCode, source::Bool) bb_idx_prev = bb_idx = 1 for idx = 1:length(ir.stmts) preprint(io, idx) - bb_idx = Base.IRShow.show_ir_stmt(io, ir, idx, line_info_preprinter, line_info_postprinter, used, ir.cfg, bb_idx) + bb_idx = Base.IRShow.show_ir_stmt(io, ir, idx, line_info_preprinter, line_info_postprinter, ir.sptypes, used, ir.cfg, bb_idx) postprint(io, idx, bb_idx != bb_idx_prev) bb_idx_prev = bb_idx end From cff9cca8cabefc5289acab9161b523171441abac Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:07:23 -0400 Subject: [PATCH 368/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=2051d4910c1=20to=20fbaa2e337=20(#56124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 | 1 - .../Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 | 1 - .../Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 | 1 + .../Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 create mode 100644 deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 diff --git a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 deleted file mode 100644 index b5b82565470c0..0000000000000 --- a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -88b8a25a8d465ac8cc94d13bc5f51707 diff --git a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 b/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 deleted file mode 100644 index a746b269d91f0..0000000000000 --- a/deps/checksums/Pkg-51d4910c114a863d888659cb8962c1e161b2a421.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -22262687f3bf75292ab0170e19a9c4a494022a653b2811443b8c52bc099dee0fddd09f6632ae42b3193adf3b0693ddcb6679b5d91e50a500f65261df5b7ced7d diff --git a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 new file mode 100644 index 0000000000000..762a180d93031 --- /dev/null +++ b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 @@ -0,0 +1 @@ +4ea351427d5b43617abae557670c3313 diff --git a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 new file mode 100644 index 0000000000000..eef70ab9b62d5 --- /dev/null +++ b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 @@ -0,0 +1 @@ +9e91076974ab1dcb1c85e2c8acaf3404f4e82dcd2118d215d4a8413a1e00462ca47891bdae983441a8621015c082421de1f2e26b9b2ee18c1e3c13d58bd1d261 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 34233c58702b4..fc67189981d59 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 51d4910c114a863d888659cb8962c1e161b2a421 +PKG_SHA1 = fbaa2e3370b4ab922919892640e5d1b0bcb14037 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From dc344285d5be2bfdf4ead01effa95643b7babc8b Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Sat, 12 Oct 2024 07:07:20 +0200 Subject: [PATCH 369/548] Fix type instability of closures capturing types (2) (#40985) Instead of closures lowering to `typeof` for the types of captured fields, this introduces a new function `_typeof_captured_variable` that returns `Type{T}` if `T` is a type (w/o free typevars). - replaces/closes #35970 - fixes #23618 --------- Co-authored-by: Takafumi Arakaki Co-authored-by: Shuhei Kadowaki --- base/boot.jl | 20 ++++++++++++++++++++ base/reflection.jl | 3 ++- src/julia-syntax.scm | 2 +- test/compiler/inline.jl | 12 ++++++------ test/core.jl | 28 ++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 608e273d4b514..861c83a2edac5 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -272,6 +272,21 @@ end Expr(@nospecialize args...) = _expr(args...) _is_internal(__module__) = __module__ === Core +# can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping) +macro _total_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#true, + #=:effect_free=#true, + #=:nothrow=#true, + #=:terminates_globally=#true, + #=:terminates_locally=#false, + #=:notaskstate=#true, + #=:inaccessiblememonly=#true, + #=:noub=#true, + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false, + #=:nortcall=#true)) +end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, @@ -310,6 +325,11 @@ convert(::Type{T}, x::T) where {T} = x cconvert(::Type{T}, x) where {T} = convert(T, x) unsafe_convert(::Type{T}, x::T) where {T} = x +# will be inserted by the frontend for closures +_typeof_captured_variable(@nospecialize t) = (@_total_meta; t isa Type && has_free_typevars(t) ? typeof(t) : Typeof(t)) + +has_free_typevars(@nospecialize t) = (@_total_meta; ccall(:jl_has_free_typevars, Int32, (Any,), t) === Int32(1)) + # dispatch token indicating a kwarg (keyword sorter) call function kwcall end # deprecated internal functions: diff --git a/base/reflection.jl b/base/reflection.jl index 80eeb4c4efb12..49d640ea40bab 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -862,7 +862,8 @@ end iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) -has_free_typevars(@nospecialize(t)) = (@_total_meta; ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0) + +using Core: has_free_typevars # equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} # and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index f1acb9c3250e1..4b3e6ae96898b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4246,7 +4246,7 @@ f(x) = yt(x) (filter identity (map (lambda (v ve) (if (is-var-boxed? v lam) #f - `(call (core typeof) ,ve))) + `(call (core _typeof_captured_variable) ,ve))) capt-vars var-exprs))))) `(new ,(if (null? P) type-name diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 2de6d9950d4e4..99fed00a7269d 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -951,34 +951,34 @@ end end # issue 43104 - +_has_free_typevars(t) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0 @inline isGoodType(@nospecialize x::Type) = - x !== Any && !(@noinline Base.has_free_typevars(x)) + x !== Any && !(@noinline _has_free_typevars(x)) let # aggressive inlining of single, abstract method match src = code_typed((Type, Any,)) do x, y isGoodType(x), isGoodType(y) end |> only |> first # both callsites should be inlined - @test count(isinvoke(:has_free_typevars), src.code) == 2 + @test count(isinvoke(:_has_free_typevars), src.code) == 2 # `isGoodType(y::Any)` isn't fully covered, so the fallback is a method error @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end @inline isGoodType2(cnd, @nospecialize x::Type) = - x !== Any && !(@noinline (cnd ? Core.Compiler.isType : Base.has_free_typevars)(x)) + x !== Any && !(@noinline (cnd ? Core.Compiler.isType : _has_free_typevars)(x)) let # aggressive inlining of single, abstract method match (with constant-prop'ed) src = code_typed((Type, Any,)) do x, y isGoodType2(true, x), isGoodType2(true, y) end |> only |> first # both callsite should be inlined with constant-prop'ed result @test count(isinvoke(:isType), src.code) == 2 - @test count(isinvoke(:has_free_typevars), src.code) == 0 + @test count(isinvoke(:_has_free_typevars), src.code) == 0 # `isGoodType(y::Any)` isn't fully covered, thus a MethodError gets inserted @test count(iscall((src, Core.throw_methoderror)), src.code) == 1 # fallback method error end @noinline function checkBadType!(@nospecialize x::Type) - if x === Any || Base.has_free_typevars(x) + if x === Any || _has_free_typevars(x) println(x) end return nothing diff --git a/test/core.jl b/test/core.jl index b27832209a835..5ba0e99e730d4 100644 --- a/test/core.jl +++ b/test/core.jl @@ -796,6 +796,34 @@ end @test foo21900 == 10 @test bar21900 == 11 +let f = g -> x -> g(x) + @test f(Int)(1.0) === 1 + @test @inferred(f(Int)) isa Function + @test fieldtype(typeof(f(Int)), 1) === Type{Int} + @test @inferred(f(Rational{Int})) isa Function + @test fieldtype(typeof(f(Rational{Int})), 1) === Type{Rational{Int}} + @test_broken @inferred(f(Rational)) isa Function + @test fieldtype(typeof(f(Rational)), 1) === Type{Rational} + @test_broken @inferred(f(Rational{Core.TypeVar(:T)})) isa Function + @test fieldtype(typeof(f(Rational{Core.TypeVar(:T)})), 1) === DataType +end +let f() = (T = Rational{Core.TypeVar(:T)}; () -> T) + @test f() isa Function + @test Base.infer_return_type(f()) == DataType + @test fieldtype(typeof(f()), 1) === DataType + t = f()() + @test t isa DataType + @test t.name.wrapper == Rational + @test length(t.parameters) == 1 + @test t.parameters[1] isa Core.TypeVar +end +function issue23618(a::AbstractVector) + T = eltype(a) + b = Vector{T}() + return [Set{T}() for x in a] +end +@test Base.infer_return_type(issue23618, (Vector{Int},)) == Vector{Set{Int}} + # ? syntax @test (true ? 1 : false ? 2 : 3) == 1 From cb1b83de308a835d089c8231a4d697ab9c761588 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sat, 12 Oct 2024 14:16:39 +0200 Subject: [PATCH 370/548] Remove debug error statement from Makefile. (#56127) --- deps/openblas.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/deps/openblas.mk b/deps/openblas.mk index fbaa2e7a0fb92..affd1c7a7aa55 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -31,7 +31,6 @@ endif endif # 64-bit BLAS interface -$(error USE_BLAS64: $(USE_BLAS64)) ifeq ($(USE_BLAS64), 1) OPENBLAS_BUILD_OPTS += INTERFACE64=1 SYMBOLSUFFIX="$(OPENBLAS_SYMBOLSUFFIX)" LIBPREFIX="libopenblas$(OPENBLAS_LIBNAMESUFFIX)" ifeq ($(OS), Darwin) From b3d587d513cd454122e25c1c6a85620b7ca1af5c Mon Sep 17 00:00:00 2001 From: spaette <111918424+spaette@users.noreply.github.com> Date: Sat, 12 Oct 2024 16:47:46 +0200 Subject: [PATCH 371/548] align markdown table (#56122) @gbaraldi `#51197` @spaette `#56008` fix innocuous malalignment of table after those pulls were merged --- doc/src/devdocs/llvm.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index c4b80f632cd4e..fdecb0472388a 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -11,13 +11,13 @@ The code for lowering Julia AST to LLVM IR or interpreting it directly is in dir | File | Description | |:-------------------------------- |:------------------------------------------------------------------ | -| `aotcompile.cpp` | Compiler C-interface entry and object file emission | +| `aotcompile.cpp` | Compiler C-interface entry and object file emission | | `builtins.c` | Builtin functions | | `ccall.cpp` | Lowering [`ccall`](@ref) | | `cgutils.cpp` | Lowering utilities, notably for array and tuple accesses | | `codegen.cpp` | Top-level of code generation, pass list, lowering builtins | | `debuginfo.cpp` | Tracks debug information for JIT code | -| `disasm.cpp` | Handles native object file and JIT code disassembly | +| `disasm.cpp` | Handles native object file and JIT code disassembly | | `gf.c` | Generic functions | | `intrinsics.cpp` | Lowering intrinsics | | `jitlayers.cpp` | JIT-specific code, ORC compilation layers/utilities | From 1713f797d2d04009b5d678c8d1208a0ef9fd1059 Mon Sep 17 00:00:00 2001 From: David Little Date: Sat, 12 Oct 2024 14:44:51 -0400 Subject: [PATCH 372/548] Improve IOBuffer docs (#56024) Based on the discussion in #55978, I have tried to clarify the documentation of `IOBuffer`. --- base/iobuffer.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index c0c2731eec08b..bd924fd040496 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -59,6 +59,15 @@ It may take optional keyword arguments: When `data` is not given, the buffer will be both readable and writable by default. +!!! warning "Passing `data` as scratch space to `IOBuffer` with `write=true` may give unexpected behavior" + Once `write` is called on an `IOBuffer`, it is best to consider any + previous references to `data` invalidated; in effect `IOBuffer` "owns" + this data until a call to `take!`. Any indirect mutations to `data` + could lead to undefined behavior by breaking the abstractions expected + by `IOBuffer`. If `write=true` the IOBuffer may store data at any + offset leaving behind arbitrary values at other offsets. If `maxsize > length(data)`, + the IOBuffer might re-allocate the data entirely, which + may or may not be visible in any outstanding bindings to `array`. # Examples ```jldoctest julia> io = IOBuffer(); From 80e60c89bb2b4fb193000c78e7f056c303dc6859 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Sun, 13 Oct 2024 07:25:29 +0100 Subject: [PATCH 373/548] Comment out url and fix typo in stackwalk.c (#56131) Introduced in #55623 --- src/stackwalk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stackwalk.c b/src/stackwalk.c index 7fb4de0372738..5377d091cb780 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -933,7 +933,7 @@ extern bt_context_t *jl_to_bt_context(void *sigctx) JL_NOTSAFEPOINT; int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT { #if (defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_)) - https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/hwasan/hwasan_interceptors.cpp + // https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/hwasan/hwasan_interceptors.cpp return 0; #elif defined(_OS_WINDOWS_) _JUMP_BUFFER* _ctx = (_JUMP_BUFFER*)mctx; @@ -1049,7 +1049,7 @@ int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT mc->regs[28] = (*_ctx)[9]; mc->regs[29] = (*_ctx)[10]; // aka fp mc->regs[30] = (*_ctx)[11]; // aka lr - // Yes, they did skip 12 why writing the code originally; and, no, I do not know why. + // Yes, they did skip 12 when writing the code originally; and, no, I do not know why. mc->sp = (*_ctx)[13]; mcfp->vregs[7] = (*_ctx)[14]; // aka d8 mcfp->vregs[8] = (*_ctx)[15]; // aka d9 From 3db1c62d62148e56997da50863b0fd6c0eab35c8 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sun, 13 Oct 2024 12:56:26 +0200 Subject: [PATCH 374/548] libgit2: Always use the bundled PCRE library. (#56129) This is how Yggdrasil builds the library. --- deps/libgit2.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deps/libgit2.mk b/deps/libgit2.mk index b65ac022885a3..022582d48c78e 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -42,6 +42,10 @@ ifneq (,$(findstring $(OS),Linux FreeBSD OpenBSD)) LIBGIT2_OPTS += -DUSE_HTTPS="mbedTLS" -DUSE_SHA1="CollisionDetection" -DCMAKE_INSTALL_RPATH="\$$ORIGIN" endif +# use the bundled distribution of libpcre. we should consider linking against the +# pcre2 library we're building anyway, but this is currently how Yggdrasil does it. +LIBGIT2_OPTS += -DREGEX_BACKEND="builtin" + LIBGIT2_SRC_PATH := $(SRCCACHE)/$(LIBGIT2_SRC_DIR) $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(LIBGIT2_SRC_PATH)/source-extracted From 4609aad97ea5b4ffdc721f9217647481e7c740d3 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Sun, 13 Oct 2024 08:33:45 -0700 Subject: [PATCH 375/548] Update JLL build versions (#56133) This commit encompasses the following changes: - Updating the JLL build version for Clang, dSFMT, GMP, LibUV, LibUnwind, LLD, LLVM, libLLVM, MbedTLS, MPFR, OpenBLAS, OpenLibm, p7zip, PCRE2, SuiteSparse, and Zlib. - Updating CompilerSupportLibraries to v1.2.0. The library versions contained in this release of CSL don't differ from v1.1.1, the only difference is that v1.2.0 includes FreeBSD AArch64. - Updating nghttp2 from 1.60.0 to 1.63.0. See [here](https://github.com/nghttp2/nghttp2/releases) for changes between these versions. - Adding `aarch64-unknown-freebsd` to the list of triplets to check when refreshing checksums. Note that dependencies that link to MbedTLS (Curl, LibSSH2, LibGit2) are excluded here. They'll be updated once a resolution is reached for the OpenSSL switching saga. Once that happens, FreeBSD AArch64 should be able to be built without any dependency source builds. --- contrib/refresh_checksums.mk | 2 +- deps/checksums/blastrampoline | 2 + deps/checksums/clang | 220 ++++---- deps/checksums/compilersupportlibraries | 188 +++---- deps/checksums/dsfmt | 66 +-- deps/checksums/gmp | 118 ++-- deps/checksums/libuv | 66 +-- deps/checksums/lld | 220 ++++---- deps/checksums/llvm | 508 +++++++++--------- deps/checksums/llvmunwind | 32 -- deps/checksums/mbedtls | 66 +-- deps/checksums/mpfr | 66 +-- deps/checksums/nghttp2 | 70 +-- deps/checksums/openblas | 188 +++---- deps/checksums/openlibm | 66 +-- deps/checksums/p7zip | 66 +-- deps/checksums/pcre | 66 +-- deps/checksums/suitesparse | 66 +-- deps/checksums/unwind | 50 +- deps/checksums/zlib | 66 +-- deps/clang.version | 2 +- deps/lld.version | 2 +- deps/llvm-tools.version | 4 +- deps/llvm.version | 2 +- deps/nghttp2.version | 2 +- .../CompilerSupportLibraries_jll/Project.toml | 2 +- stdlib/GMP_jll/Project.toml | 2 +- stdlib/LLD_jll/Project.toml | 2 +- stdlib/LibUV_jll/Project.toml | 2 +- stdlib/LibUnwind_jll/Project.toml | 2 +- stdlib/MPFR_jll/Project.toml | 2 +- stdlib/Manifest.toml | 32 +- stdlib/MbedTLS_jll/Project.toml | 2 +- stdlib/OpenBLAS_jll/Project.toml | 2 +- stdlib/OpenLibm_jll/Project.toml | 2 +- stdlib/PCRE2_jll/Project.toml | 2 +- stdlib/SuiteSparse_jll/Project.toml | 2 +- stdlib/Zlib_jll/Project.toml | 2 +- stdlib/dSFMT_jll/Project.toml | 2 +- stdlib/libLLVM_jll/Project.toml | 2 +- stdlib/nghttp2_jll/Project.toml | 2 +- stdlib/nghttp2_jll/test/runtests.jl | 2 +- stdlib/p7zip_jll/Project.toml | 2 +- 43 files changed, 1144 insertions(+), 1126 deletions(-) diff --git a/contrib/refresh_checksums.mk b/contrib/refresh_checksums.mk index f67088141ccd4..e7bf2fd7c2efc 100644 --- a/contrib/refresh_checksums.mk +++ b/contrib/refresh_checksums.mk @@ -19,7 +19,7 @@ all: checksum pack-checksum # Get this list via: # using BinaryBuilder # print("TRIPLETS=\"$(join(sort(triplet.(BinaryBuilder.supported_platforms(;experimental=true))), " "))\"") -TRIPLETS=aarch64-apple-darwin aarch64-linux-gnu aarch64-linux-musl armv6l-linux-gnueabihf armv6l-linux-musleabihf armv7l-linux-gnueabihf armv7l-linux-musleabihf i686-linux-gnu i686-linux-musl i686-w64-mingw32 powerpc64le-linux-gnu x86_64-apple-darwin x86_64-linux-gnu x86_64-linux-musl x86_64-unknown-freebsd x86_64-w64-mingw32 +TRIPLETS=aarch64-apple-darwin aarch64-linux-gnu aarch64-linux-musl aarch64-unknown-freebsd armv6l-linux-gnueabihf armv6l-linux-musleabihf armv7l-linux-gnueabihf armv7l-linux-musleabihf i686-linux-gnu i686-linux-musl i686-w64-mingw32 powerpc64le-linux-gnu x86_64-apple-darwin x86_64-linux-gnu x86_64-linux-musl x86_64-unknown-freebsd x86_64-w64-mingw32 CLANG_TRIPLETS=$(filter %-darwin %-freebsd,$(TRIPLETS)) NON_CLANG_TRIPLETS=$(filter-out %-darwin %-freebsd,$(TRIPLETS)) diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index ac028ceb6e124..cbde7fa45b1e2 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -6,6 +6,8 @@ libblastrampoline.v5.11.1+0.aarch64-linux-gnu.tar.gz/md5/aad5e3585f585d54d9ebcf8 libblastrampoline.v5.11.1+0.aarch64-linux-gnu.tar.gz/sha512/11ff9227e16898895ad6cbd36853093941b243a49962785a5ab8b7dc2426831a2750ab5882ee814e3a662e8b9f8aecb273d750b88a4ea5a213e20c93cb121ce1 libblastrampoline.v5.11.1+0.aarch64-linux-musl.tar.gz/md5/462639b4b21f5b7626febfdd1ae1f824 libblastrampoline.v5.11.1+0.aarch64-linux-musl.tar.gz/sha512/866004e3fcdb5ab7418c8a2cae8f820c5739a511b9d0b32d0013ef72ff99f87396f5912d8fbd6bf4d01d7432715c6971ad1a5419c34fa7b048d0fbbe0f8520d2 +libblastrampoline.v5.11.1+0.aarch64-unknown-freebsd.tar.gz/md5/b6ce7d6d46d2ae772d4c3f629e754486 +libblastrampoline.v5.11.1+0.aarch64-unknown-freebsd.tar.gz/sha512/b2e7990cd0f7bb1bc376118955e397599c44aa3d09b0e87524ed8fed4bbb1d6a2b9c1bc02806bbeb86812ab0083c8016fe3c38894e0eb339025cf30f0cd64ffc libblastrampoline.v5.11.1+0.armv6l-linux-gnueabihf.tar.gz/md5/8a48cc8243257362dbc920dcadc42a22 libblastrampoline.v5.11.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/bb4048c0e1ebbb89fc82b7cdabb0a4d9263b5344390c934b66c3a227631661ae956287870e4b156935f0a3c322049ceed3138fc033c92561fccf3675317af5b8 libblastrampoline.v5.11.1+0.armv6l-linux-musleabihf.tar.gz/md5/53c12d04337b63d18f4a5469a36132b6 diff --git a/deps/checksums/clang b/deps/checksums/clang index 7dc297db9c05b..2158589b5cef5 100644 --- a/deps/checksums/clang +++ b/deps/checksums/clang @@ -1,108 +1,112 @@ -Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/ce3e582bcf2f92fdaf778339e8c51910 -Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/4f977e8f0912f52b9b4054089a53a05f60bf7ae352c39b2541e68fecf3c21969d6d1b85e40d71d61040b65f7c60a2c33c8d259734bc1d2ddf77392fc425025cb -Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/1eda08774c2f9975de32bdce4ffc72bd -Clang.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/c76ec1de9a25f4f8bd309336830cc07e1113b941ced12cb46976b24aebd4ab3d261c943dbc9cdfb34a01f27073af6f598dded31a4e03c62f229cd2e7d5982af6 -Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/2817b0eeb83eff4e1f580729e02564ab -Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/88242559299836c7a7b7d3a216353fc6880a587a839793ed71d6d053318d6e2071ff218587a082f2b5dd9fb2b0952b4c60e62030d707435607303708bb1e6d81 -Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d3f92998b7cc35a507cb1071baae8b02 -Clang.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/be22296623f604927e2e815a1cc149addda6d567270a50b2cdf77fe5b09f74313210a1ca7b1b3194592da23490ba1ccfdab9f520ce7219989e646f12208e418a -Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/716300acfdee4415f1afa3b5571b102b -Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/b97efb3c461ea7d2736a3a8bb6b6b5c99f02df9a095f11291319c629d44f1fb934b124d38af6be3e5cc7103c6f85793d7f185c607383461de5d0c846560a1d1b -Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/034f44b2fc61791234d9580402002fb2 -Clang.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/0b4ff55afcec0b1e8fbd09fab57de8b44d5ded360d3b53132c7a7df8d3a3b83a495bf6e0c706784e678c6de46be3a72e8bfe562c7f8dfad90b82880849625e35 -Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/54211070d63a2afac6350d06442cb145 -Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/a58f8afe9a20f202cf3956f758dc13a10be240d78877a02cd006d7e972751ed65623eef7e92a7256d9ed9157d6e277302f93b58f583d86d386ed4945f3c7d875 -Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/7084567b3637fe64088fdce357a255de -Clang.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/77ae83e159a814a7117cc859a0b2aa7a5d41f983d45b7eb1ce2fd2e93f8733ee067ac8c9fad9d5af90f852b8802043ef39c29b44430b2594892e57b61ccb680b -Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/9e294d16a6e1c2c76c03f32cbbbfbe23 -Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/b8f83542b51f5cf953f6baed185550394744a8466307ee08525bf18a651fcecd7daafb98e75a0866b0e9a95a524e8940be7ae1878ba80d856182dcb7f7d2254e -Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/70a41c2ffd55d2d87a7b8728287eb9fd -Clang.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/44bb3dea7227ee991b2666c43a88613d5b5d382eb560b5ad1f1184d38680c85a2ef961bac6ad71c2b920702c1ec6e09296198e7ff5e2929f4ba7839e55896e3f -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/95ee1406f8575898eb52e2c86ae18992 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4da66e4d397491836b3e539258844346fe50bff41e6c0628cbb5c0eac76147bd91d1720cec1523452efdb063adf6ef8792dc278244e1f8e194ef60a180442c56 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/6c4e4e892b54ce81d73a8598728083e3 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/53d08fd8b6782867cfa6ce001b14a2fde38bc9ffc85c7e148aebf59dd9c1c535b54eaea816c39fcff42abc456c1047ed13d688917302bcc5a281abe368bd29bb -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5acc5853111bcd529eeb06ea31b329e5 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b1794f7cdfba838a7e43de8f66700ae44fd16d8f06300e8ab955044ae9bc96110c5ea72691841cd3787cdc93dfb91c6b257702c20390689a8d1b45a994db2fd8 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/c4de50252e557fb126360001ddae6a97 -Clang.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/9343a7272c76d5341bb49273ff8d43bed09ad99b2879ec51cfb8946174181b286af82d85e2d3a13a375c7e7859e51e4a4f06031a6a3fe7e540700cfc6a795741 -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/af301478b20e56cb7fa1160cda2573a2 -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/8822c58df101c239221fead6fb523e677da04a065b42849a2e6ffff03dfd81e07f162a9bbdd29490ad9c0e0a33d362eec46608b9e6e42dfb4889da1c22191c91 -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/901d2808599d5ac5ac7b5ca4bc39833d -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/820756cad00b1fe927801a253bd3077709c2b067ae79f9e1812f3cc9e85a0b7ac2ce1534031b7c6f7bda3364b7173c1c508e7c7d316920fb9bb901c16c1b18c7 -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/d1f368604084e907c382aaf00efe452c -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/523b25f6b79e222eb65b5f4cd8f23b0d2c8b25b29af0df88efe45546ea57c7dabd88baef454fa0b76342d8d364739107271f25d3504380fdec5c9d225fcc2521 -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/e57c116b2ad1cf32307eb4e600ac80be -Clang.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/63366b983c7aac9fe1246b25432b2200c8316f569f6930eb12de3c867f448ffccb8756d418f92eae7751d4c9ce6c42cee38237e429b81530819684fd5150c93a -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/645929ce42276db10ab79184a60cd6e3 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/65555ed26d9bd670b8363e5dad949822c2bf0e141a5418e1dc30c3f8a4733dd050620e40be2e7552c2551ecb30d4ef3e8f74cb240f1d441a9720a25f5a3bcaa7 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/8424c6c6318dfa7bebeac33917b29453 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6cf90c253f6b22358c2389a2347af2febd010117b22de0cc91ad713b8c8224627398004567c96b673650212eb5bd40bb97b9a637d46ddfeb3c72388d83445017 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/ea8151dc1dc32befe579c7f9d7f13898 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/ed518423e9ec35afd7983471cf9ff1e971b840f637f34e0f62a1f6c7379ea59d4dafbeb9a311d39761733ecc98c0318ce3d8883298f8998e9c741441c7c9616b -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/70ed39b13bcb0435fee63bc30ae25a39 -Clang.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/b2afa383346875514c62129c2991b3604c4fd3d507ecf4fc4244dec81d08b30218f5aa03dc4977185c2c9fb2d08848ddd373e448883ab472e5221ae5bf285c99 -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/e6798835128f663f0c837aed4463e34b -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c99856e16bd42ff967479e2c89690ea41268f1d1f868e2628482eafdfa53a0d69ed7c21ecc68ff0859eef07d9fe02f4844fad5f13df26cee6cea3a4254446096 -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/92c1bd54b0474244e35c51952966a55b -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/2d7c3b60ba8b11cf903bc5ea720193852027cbe61ea0c8d6fac70be8f97691da3d36663aac6e61b68185dd83b42d09ad61dea973d9390271210d690295e4902c -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/c495d594f8ce1f701d1bab54d0b60521 -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/0261bf45403daccf236723383341dc791e9cb3b291bde97812378d85aed785f083d5deea3bf806480a04ef1b972b00dccfd0537e43532a066c64733b817c3d77 -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/41541de24d625271bdd5fad867b8eb0c -Clang.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/595226ad7ef75ab8ae03adb456b4ee9e884e9554c720b6c4ecbc38c75d446ddba7898be94630673074f09f40c6dc3e18fea9cee5a91b8b0e4727d20a180f670c -Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/8bd8ca0436611e78882939067f6277f7 -Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/27c7b06e93fb0fb516b1b240e0df6c95e8bad6aea04d637ba065c6fafd087bfa94d9136afd39273c8d82d9c467395dcbd7b16f6a4b829acb0c0d4a5677676a5b -Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/424bfbd7b69ddf7b1199afaacde3e028 -Clang.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/9c48d949309aef6ee39371ff39a4f12c31bf3f25ddd288b317b2a17a803db73850cba2886598a1d10c4c154d511a4b79958d1acc012e92491a63f3925c522873 -Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b0b3e045ad64ecdc9848898f30d5f34 -Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6c0f4bdabbbc94fc9e1fedc138b0bce99d383e380ae7222fb70f5935f17701d549f6486956c8a21731061e4bf60bbc52794f6ce6858b4d2adb89bf80f88795c0 -Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3b7a461ebf957756aeb2a2455b0a298c -Clang.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/74641a3636dd58c69415b19f0cb1de444215e22cfa9f0268fd549b5c53b206811d8beecdeb9692285613468d9a0569e836d225fb8361218438346914f6282839 -Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/5e7b9ad5fc3af3bfdf262687cd248dfa -Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/c54835fdf8e3e442b7c774d445c2f13c5dd8b3224f4ae165e72cc893ee5453d0112a9ca6d543b17f2c02a89471e2cff7cf022dc4c8188a5df25d101dd0f954b9 -Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/3204bd8074d42920a6707cc8624c0dfe -Clang.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/74b26c4556ca18645cc15647d8abdbd46fb94c75169934af885e5773a880c066b2ff221402fdb4a53417b2c97ce589783f7fae6a8d56ee89cc1f70577b02b2a1 -Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/7922c04964e0c1a5b44e95480290930d -Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/4f0d675c1b85dc3e5007a62a7cfea412ca432d1276a259db3ed5a1bf0f33d6c555f16010de717a62e0e065e7c1dbaa66c281815eb9629d2b6c720b152820e582 -Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/e023eba0ea0a327f53013d5e4d50d0cb -Clang.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/9fbdebce9c7375a20d1cd10e39a0c26b131af686cb5771034a6afc6cab08855e0cada2add616c01394424383333950d0dde9c55a9477fa139cf0ca3fc438b229 -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/a6c7d64ede931fb19e066a1c191e2f6d -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/1a085a4ea1efb910f2b529f3c0e51be4a5e31debbefd00ceefeddc352b36bea6d0de5a06ea7d509098d16416b536ffed3da8485feefad7a2f11b1bc148a0c8c2 -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/692af94ca3e5c3d229cbb459e266aadf -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/b27f05cfb0ada89cefc5a6f6527583b6b43d03525954d5b1ad1c807712efdb8750ea558a230b587a0c0d9e77c54d9f8978cc2f3884653808c7409eab1b32a055 -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/3b59b6aa4b18b5dbbc632811f2ffa270 -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f8c4b593f969c723ff1931c4875ed52497d83d74b94121890e10c9fcca5f6bddc5067555dee9949e61e426586ae3e568375fc44f318a07b70571ee34fdf7032c -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bc4be32ad57b13c3dabc80684a176ba7 -Clang.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/19a8346547b6c6adc2a9156e4b913b20137593752efa3648ad532b08de67cf015bba1eb023204755f48904c3381a3665c6c54fc8233c50e887a22ceebc652303 -Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/05f37d069c7d59ec245d961d0928cb37 -Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/3b0956fe770fd9230319bfcaefab4922f9aee3df3e8516edf81cb7d322132ee9ab899af4464c75b1042aa99e3bcb07ede6de5646bba2a57995fc2eb32d4d0861 -Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/0304434211ff4101a148fcc0c96455d4 -Clang.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/a033dc589fc95e63547b7ca82964116bec33ad6e78ac131934d4bb16988756d36c24d74761ca93b0e47dada1f3d2a63071cb3721ddb9af457cbeb164fe5f0f54 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/4e5d1064d90f24d57d63f08b61baaab5 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/cbfbe8b6f2be80e59b69d25d6af901ccb4807b12180208b69afa7223dd7d5249255265bc319c9402a1b0d1f0995940e3e72d7ecf1009f60d83021f8d35626a46 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/22fead15b4c45398ca869821d04ce015 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/2ee7a7d3f293f7b63c89bbe3b541722c502a840883804ffe272848f4ac99b7a8ed350ebe92ec434dfdf03d1f4a5531c1367859f4a4603c98325abe5a0ad71177 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/46dd01b10377cc3d45c6a42cac0a07e5 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/957677ce4251938d0c5e066448762b38a21bcce5ed424072ccd58085167d61b7e45a88fe32375f6bbd43dfb579b65a9afc09a886a650fc634a8fb9c81f27c9e3 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bd9a61ea186a39162201341f0739fe84 -Clang.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7a06d2a9ef20e88daa00d627d482ebbb6bf7223219d8b2a24aa60ac9eda24649d206b093d5bdb88b65c1e2b0d1ba0ad7dd927697e2bbac65bc9b42f9d14ad0d9 -Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/60c98c6cc7d4446fb52b7585bc8709f3 -Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/4d55464b4499a45f774e1000a8b015326d114103a3d348fb263367e5506ca6659444ea6ee2767712903757e83939cd446aff6fe2351438b644f0057053422b58 -Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/90a512d1881c4af1f1abfd5e90e37356 -Clang.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/62d6d855aebd49f132d6470c7b0d5a0b965c6489b025046c1ea73fc53336030d6c5b4c867523a9206821f7fcf62fdb37ef0b7ff4b5eb04d07f40b65edd2c8e0f -Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/c9eb9acb605d774db9636b82bf2e5f41 -Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/96e1440b3b0378edf8907d4cf779b1c53d63f6d00fa798efe1b6aaa289135aba8fd00a8d6f55d9678136e9e07d0c189293aec64f46e66788b938e1f8e1fc2199 -Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/5837070450c81d44395468d8e3671dc7 -Clang.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/0e8b674c0360f9586f03c7f5d0ffd5bc73dcde1e88eddf7d6360c1461adb8efffb104d8f454116a6a6cdc909973d0876745590b21009a9de56e12ce6e1c2e8fc -Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/5c198d35df5cf6435f4f5ac91a78be01 -Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/9ba0a532f499933320145834aec2b57a70410bf67af649ed675f00aebfd59de7c80e6f5d19e7ad57029a573090e63c5eba4b42b498a374810b48c8668b50dcaa -Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/8ac88c856d946e29d1121426de44e6bc -Clang.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/94af63ad3fb17d9c07f5256e2d474effc0e3d5ef66f4a9f3ffeb9bdd8f1577c35e4d0aceb8b4746ab857d8f164141790ed494b7f687e644e040d2f3820f9e1fe -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b4be546ff44019cf46d3250dd9a4321f -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/7ce5e4d68e18021392355359f59931219eeec3be4edd01f7a18b7bee499b589414bcea73820ee38dbc3b5ab12d912a93374b4a616b10ba491f5d41b6b33f3d9e -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/4616c348320d8704215d58c7268de6d7 -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/b4c21147ed21d41321e04b092d47f99338c6ac7d50b8328ceb8ae26d6382955cbcd655dddd39f0de3d3c36a5fda7084a33272aad9f6cd9585c87fee68be73a68 -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/bf9cf2efb938b68ac7e1560c464f9051 -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/ca29438393d393912571a96ce59bdaadcacbb329342c42a0de0e8d8ab52f69d4e6966822c0743d99b1a277c8715c1f72ddd490b781b45bd691df2c137ed42a1d -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/94138893eaaa99f37354317bc13cf7e0 -Clang.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/1b03d8d4e407372875667f25f74abdaac9be0b81c6229dc1c4c1714589efde6b1f8c76302a2545b103ee4f9812fa78f9e06e5d5bb5bc3903ce579328899faa2f +Clang.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/1dfebd0db436a282c2ccb01375e48419 +Clang.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/d5a8fc8be8bdcfb98c3f868c1a08cb18bffaca0c9fc6efbb11beaadf40ed5ca7e2a70c3be783a7cc93b23f39e06167784f63e91abe726240ad62d11210337794 +Clang.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/f82250af13bd879486677cbf1ae0b7dd +Clang.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/c4f67a59e30ea7bfb9ac83f07b1e07c856113dbc674d3a7d01cc7bbc326a1529f97d0e1a08a3aa60e110f901dba6d4888bae7060e24065444baaf633482108d7 +Clang.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/2817b0eeb83eff4e1f580729e02564ab +Clang.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/88242559299836c7a7b7d3a216353fc6880a587a839793ed71d6d053318d6e2071ff218587a082f2b5dd9fb2b0952b4c60e62030d707435607303708bb1e6d81 +Clang.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d3f92998b7cc35a507cb1071baae8b02 +Clang.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/be22296623f604927e2e815a1cc149addda6d567270a50b2cdf77fe5b09f74313210a1ca7b1b3194592da23490ba1ccfdab9f520ce7219989e646f12208e418a +Clang.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/716300acfdee4415f1afa3b5571b102b +Clang.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/b97efb3c461ea7d2736a3a8bb6b6b5c99f02df9a095f11291319c629d44f1fb934b124d38af6be3e5cc7103c6f85793d7f185c607383461de5d0c846560a1d1b +Clang.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/034f44b2fc61791234d9580402002fb2 +Clang.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/0b4ff55afcec0b1e8fbd09fab57de8b44d5ded360d3b53132c7a7df8d3a3b83a495bf6e0c706784e678c6de46be3a72e8bfe562c7f8dfad90b82880849625e35 +Clang.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/54211070d63a2afac6350d06442cb145 +Clang.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/a58f8afe9a20f202cf3956f758dc13a10be240d78877a02cd006d7e972751ed65623eef7e92a7256d9ed9157d6e277302f93b58f583d86d386ed4945f3c7d875 +Clang.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/7084567b3637fe64088fdce357a255de +Clang.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/77ae83e159a814a7117cc859a0b2aa7a5d41f983d45b7eb1ce2fd2e93f8733ee067ac8c9fad9d5af90f852b8802043ef39c29b44430b2594892e57b61ccb680b +Clang.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/9e294d16a6e1c2c76c03f32cbbbfbe23 +Clang.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/b8f83542b51f5cf953f6baed185550394744a8466307ee08525bf18a651fcecd7daafb98e75a0866b0e9a95a524e8940be7ae1878ba80d856182dcb7f7d2254e +Clang.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/70a41c2ffd55d2d87a7b8728287eb9fd +Clang.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/44bb3dea7227ee991b2666c43a88613d5b5d382eb560b5ad1f1184d38680c85a2ef961bac6ad71c2b920702c1ec6e09296198e7ff5e2929f4ba7839e55896e3f +Clang.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/1f673de0cc2ec59cc62dee6040b2d6b7 +Clang.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/9b2e64cd2cd510677375f3d07d434f46066adb7464751dfeaebb057129f6b092d8425b0728f60dd9a2ec4cb29625ffc5cda57acf1d5465d5f82765369954c58a +Clang.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/md5/0d91f5a19060c6a1b1dadb3befa0fe6a +Clang.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/sha512/9f9aaa36e1dab2d98a17602ed0b27163729928bfe4ac0f7b565cff1e0a653855b0f3e404830cb77ff35d93c0d5c42ed11d2506aecb5ec8d3752fbdfeb0ff5b4c +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/95ee1406f8575898eb52e2c86ae18992 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4da66e4d397491836b3e539258844346fe50bff41e6c0628cbb5c0eac76147bd91d1720cec1523452efdb063adf6ef8792dc278244e1f8e194ef60a180442c56 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/6c4e4e892b54ce81d73a8598728083e3 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/53d08fd8b6782867cfa6ce001b14a2fde38bc9ffc85c7e148aebf59dd9c1c535b54eaea816c39fcff42abc456c1047ed13d688917302bcc5a281abe368bd29bb +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5acc5853111bcd529eeb06ea31b329e5 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b1794f7cdfba838a7e43de8f66700ae44fd16d8f06300e8ab955044ae9bc96110c5ea72691841cd3787cdc93dfb91c6b257702c20390689a8d1b45a994db2fd8 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/c4de50252e557fb126360001ddae6a97 +Clang.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/9343a7272c76d5341bb49273ff8d43bed09ad99b2879ec51cfb8946174181b286af82d85e2d3a13a375c7e7859e51e4a4f06031a6a3fe7e540700cfc6a795741 +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/af301478b20e56cb7fa1160cda2573a2 +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/8822c58df101c239221fead6fb523e677da04a065b42849a2e6ffff03dfd81e07f162a9bbdd29490ad9c0e0a33d362eec46608b9e6e42dfb4889da1c22191c91 +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/901d2808599d5ac5ac7b5ca4bc39833d +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/820756cad00b1fe927801a253bd3077709c2b067ae79f9e1812f3cc9e85a0b7ac2ce1534031b7c6f7bda3364b7173c1c508e7c7d316920fb9bb901c16c1b18c7 +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/d1f368604084e907c382aaf00efe452c +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/523b25f6b79e222eb65b5f4cd8f23b0d2c8b25b29af0df88efe45546ea57c7dabd88baef454fa0b76342d8d364739107271f25d3504380fdec5c9d225fcc2521 +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/e57c116b2ad1cf32307eb4e600ac80be +Clang.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/63366b983c7aac9fe1246b25432b2200c8316f569f6930eb12de3c867f448ffccb8756d418f92eae7751d4c9ce6c42cee38237e429b81530819684fd5150c93a +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/645929ce42276db10ab79184a60cd6e3 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/65555ed26d9bd670b8363e5dad949822c2bf0e141a5418e1dc30c3f8a4733dd050620e40be2e7552c2551ecb30d4ef3e8f74cb240f1d441a9720a25f5a3bcaa7 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/8424c6c6318dfa7bebeac33917b29453 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6cf90c253f6b22358c2389a2347af2febd010117b22de0cc91ad713b8c8224627398004567c96b673650212eb5bd40bb97b9a637d46ddfeb3c72388d83445017 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/ea8151dc1dc32befe579c7f9d7f13898 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/ed518423e9ec35afd7983471cf9ff1e971b840f637f34e0f62a1f6c7379ea59d4dafbeb9a311d39761733ecc98c0318ce3d8883298f8998e9c741441c7c9616b +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/70ed39b13bcb0435fee63bc30ae25a39 +Clang.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/b2afa383346875514c62129c2991b3604c4fd3d507ecf4fc4244dec81d08b30218f5aa03dc4977185c2c9fb2d08848ddd373e448883ab472e5221ae5bf285c99 +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/e6798835128f663f0c837aed4463e34b +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c99856e16bd42ff967479e2c89690ea41268f1d1f868e2628482eafdfa53a0d69ed7c21ecc68ff0859eef07d9fe02f4844fad5f13df26cee6cea3a4254446096 +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/92c1bd54b0474244e35c51952966a55b +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/2d7c3b60ba8b11cf903bc5ea720193852027cbe61ea0c8d6fac70be8f97691da3d36663aac6e61b68185dd83b42d09ad61dea973d9390271210d690295e4902c +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/c495d594f8ce1f701d1bab54d0b60521 +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/0261bf45403daccf236723383341dc791e9cb3b291bde97812378d85aed785f083d5deea3bf806480a04ef1b972b00dccfd0537e43532a066c64733b817c3d77 +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/41541de24d625271bdd5fad867b8eb0c +Clang.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/595226ad7ef75ab8ae03adb456b4ee9e884e9554c720b6c4ecbc38c75d446ddba7898be94630673074f09f40c6dc3e18fea9cee5a91b8b0e4727d20a180f670c +Clang.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/8bd8ca0436611e78882939067f6277f7 +Clang.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/27c7b06e93fb0fb516b1b240e0df6c95e8bad6aea04d637ba065c6fafd087bfa94d9136afd39273c8d82d9c467395dcbd7b16f6a4b829acb0c0d4a5677676a5b +Clang.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/424bfbd7b69ddf7b1199afaacde3e028 +Clang.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/9c48d949309aef6ee39371ff39a4f12c31bf3f25ddd288b317b2a17a803db73850cba2886598a1d10c4c154d511a4b79958d1acc012e92491a63f3925c522873 +Clang.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b0b3e045ad64ecdc9848898f30d5f34 +Clang.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6c0f4bdabbbc94fc9e1fedc138b0bce99d383e380ae7222fb70f5935f17701d549f6486956c8a21731061e4bf60bbc52794f6ce6858b4d2adb89bf80f88795c0 +Clang.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3b7a461ebf957756aeb2a2455b0a298c +Clang.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/74641a3636dd58c69415b19f0cb1de444215e22cfa9f0268fd549b5c53b206811d8beecdeb9692285613468d9a0569e836d225fb8361218438346914f6282839 +Clang.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/7533ca14f2932c35881ec05a5fb1e550 +Clang.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/a1e55212b92c6b6dffc7e7b316c98e421e8384f65d4339455694c53643a3509b817d2ecb4e8dcd5f147dcf1be3920bcf82c1cb1732b23657bc7e36abb800d21e +Clang.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/5525f1e02315a128195cacb7f6cf7d44 +Clang.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/9ee9fe4b1f52dc6533f177256e60b0579943e8bb5ba34118e5a02d25b6a4419133f3f819aae1e02d916cc17edd09330facdc6625d66564ad3cbd97ebfc439e32 +Clang.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/9f442a545e9c3fbb0898b7a233e5079f +Clang.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/99cf06a5cda26001ed8d8bb4915a6a5993d4c9c5a7a038ccff99a3fa752f207b02095bdf1689f5cb9a2584a7e3ef26436b840896fe9a5b9b626980ebc7d85751 +Clang.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/9910ade7fdfc95ac2db3113fbfde42e0 +Clang.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/6267f1b3dbbf7900bd72cd5700756e1e2c783157b87b1829af552f7dac36f749d9c7d2662235892105c959e1425914e944fbdd2f9521d2da7de321efe6c793a1 +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/a6c7d64ede931fb19e066a1c191e2f6d +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/1a085a4ea1efb910f2b529f3c0e51be4a5e31debbefd00ceefeddc352b36bea6d0de5a06ea7d509098d16416b536ffed3da8485feefad7a2f11b1bc148a0c8c2 +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/692af94ca3e5c3d229cbb459e266aadf +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/b27f05cfb0ada89cefc5a6f6527583b6b43d03525954d5b1ad1c807712efdb8750ea558a230b587a0c0d9e77c54d9f8978cc2f3884653808c7409eab1b32a055 +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/3b59b6aa4b18b5dbbc632811f2ffa270 +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f8c4b593f969c723ff1931c4875ed52497d83d74b94121890e10c9fcca5f6bddc5067555dee9949e61e426586ae3e568375fc44f318a07b70571ee34fdf7032c +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bc4be32ad57b13c3dabc80684a176ba7 +Clang.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/19a8346547b6c6adc2a9156e4b913b20137593752efa3648ad532b08de67cf015bba1eb023204755f48904c3381a3665c6c54fc8233c50e887a22ceebc652303 +Clang.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/13436ae410728f67c914fa7aed304736 +Clang.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/3f83f1659580f4c5085b2da1c1a90581dcb3c45f5da1cf4d1801e230bb56fdb78a98cfe41b755949b34316ae08c55f5b2d558bb4026503ef2afa895b59dc861c +Clang.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/fa79485d88d173e15fb99b2f7fd793bc +Clang.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/4886be75294979cdb55030747c664bd4cc2a2fa1489790d744e918a39fddcc5c214d4f39755d58206fd1bfd077774302b2be506ee80e4d0a2e2e2de642dbf124 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/4e5d1064d90f24d57d63f08b61baaab5 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/cbfbe8b6f2be80e59b69d25d6af901ccb4807b12180208b69afa7223dd7d5249255265bc319c9402a1b0d1f0995940e3e72d7ecf1009f60d83021f8d35626a46 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/22fead15b4c45398ca869821d04ce015 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/2ee7a7d3f293f7b63c89bbe3b541722c502a840883804ffe272848f4ac99b7a8ed350ebe92ec434dfdf03d1f4a5531c1367859f4a4603c98325abe5a0ad71177 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/46dd01b10377cc3d45c6a42cac0a07e5 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/957677ce4251938d0c5e066448762b38a21bcce5ed424072ccd58085167d61b7e45a88fe32375f6bbd43dfb579b65a9afc09a886a650fc634a8fb9c81f27c9e3 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bd9a61ea186a39162201341f0739fe84 +Clang.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7a06d2a9ef20e88daa00d627d482ebbb6bf7223219d8b2a24aa60ac9eda24649d206b093d5bdb88b65c1e2b0d1ba0ad7dd927697e2bbac65bc9b42f9d14ad0d9 +Clang.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/60c98c6cc7d4446fb52b7585bc8709f3 +Clang.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/4d55464b4499a45f774e1000a8b015326d114103a3d348fb263367e5506ca6659444ea6ee2767712903757e83939cd446aff6fe2351438b644f0057053422b58 +Clang.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/90a512d1881c4af1f1abfd5e90e37356 +Clang.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/62d6d855aebd49f132d6470c7b0d5a0b965c6489b025046c1ea73fc53336030d6c5b4c867523a9206821f7fcf62fdb37ef0b7ff4b5eb04d07f40b65edd2c8e0f +Clang.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/c9eb9acb605d774db9636b82bf2e5f41 +Clang.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/96e1440b3b0378edf8907d4cf779b1c53d63f6d00fa798efe1b6aaa289135aba8fd00a8d6f55d9678136e9e07d0c189293aec64f46e66788b938e1f8e1fc2199 +Clang.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/5837070450c81d44395468d8e3671dc7 +Clang.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/0e8b674c0360f9586f03c7f5d0ffd5bc73dcde1e88eddf7d6360c1461adb8efffb104d8f454116a6a6cdc909973d0876745590b21009a9de56e12ce6e1c2e8fc +Clang.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/f94431ce7b8a12774925348a076e39e9 +Clang.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/cdbcf5bd32a9fa4d5204e77f12d60b1fde540fc93243236f26896106d21f3b2106b0c3fcd93b1a7bbd6a9c4688200837f309b216ec9f334f8c8f28144b36d4ca +Clang.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/4ca4824a441d51cd4d1fe3516d7841fb +Clang.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/ac0a046ede4b3c9bc75bbf7d1189e4679df6c35ca50e97fd6dadf437aba00816f66038db5dfddcfe2c49140c8416c79cfa4b67db371b4185ee897e0585b96301 +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/844031bd67137863f8e7dcd65aa6e45b +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/56efe56f02f0d13e03ba029cc2ccf2aaf2d50479d8153b7922392ff90327e3cded2c1e7fc8cd799737cd988e64bb9c74f2c0ea6156a04fc08f22a4dbe6156cba +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/cc2705c3a856574835383aac7185ab32 +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/eb037e996168d6d8987ff50c45e879f5e9779b044075f91cd8bbfe096260cd155b36f80bad840e88e1ab7970517e692875d5e84adc447153f167dfed886e0442 +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/2103b507b6aec55f8cb58a0c86aa461c +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/d9a4d6eeec2aac1bc41a0be40526842e782d0796a306d3c1b5e53f7f146628ed974c8a4c4dce8baff5734d973966b4f3e1310be40b90ced9981ace4c4369a257 +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/daf3d83095fbad33bbb120314d6b53f7 +Clang.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/e68a71d0d89d16e0c5c9182b8a3336c67179f37e247c8eef3f21e362a3258ff4815f258d2430ca3883a52a95bc26c8e2c42e3dd081f4998ed309813f3d0a4aa6 diff --git a/deps/checksums/compilersupportlibraries b/deps/checksums/compilersupportlibraries index 48843f21c0feb..a03ae8ee83f9a 100644 --- a/deps/checksums/compilersupportlibraries +++ b/deps/checksums/compilersupportlibraries @@ -1,92 +1,96 @@ -CompilerSupportLibraries.v1.1.1+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/20ebaad57850393b6ac9fa924e511fe4 -CompilerSupportLibraries.v1.1.1+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/020de4d8b0ff6bedbadaa305ff8445e6849f12053762ea4aa68412d1ec763dbd86f479587a2fbb862487f1feb04d976c38099ddf3887817a3d32b3f029cf85b1 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/d641904255ee412c45b089d92c53262b -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/ace0383fe9bd64faeed1fb05a11bbec932bd56b8460d06d2b7c3e1b5f4f6e9a9b3345937088684e5cd1ca9a85ef1a5ff56a97a1f60449cd6e35247de1e123d81 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/2a71f320d8b9242ad26aabed74cbf404 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/03e2a4482baaca2d6ce5cc207224d03bd7851486ebe8072c7317f5fcdd641395d945552d9462ab44a9f2e4b0ffaa3874a76f314d67bc0f75393a1151ab518611 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/1beec15ad689a5f572040ca2a7b6a880 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/27bbe212a8d43e841cf8f3e9964b72bc220fea03cf5e65721b02d2f3aa5193acdce41e512578ed6be935b413cd0d2224a6bcd2e9624931f39092ba3cfc5cbcc0 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/9e949c2efe48a7b2a62bff7e1ffdede0 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/2947acb250f8ff4936da5ed02ddbfa492fc38bc87baa588a36bb892ba68b6636a912cda976f8fff00cc7a710c3bfb185826b4cd4a726750ef5f161d5f1aa21a2 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/7202764b1a89a748b07460d9c40a9279 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/63236225a9becdd166c4395ea5081c64f57bc51af89c2edb5abeb419d6eb8224a380a633afd861bb84a12435fd19c8554cbe5ffadf8324ff2c7f17021ed53e69 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/f66c30d3cec8057ae47f05df022ead51 -CompilerSupportLibraries.v1.1.1+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/5329d9469bb0f47560e52b15eb21ab70e0e2da0275bdb2f8e6ed4feb132bc9989a6b44984329455104546c95d05a05f8fb4f1cf232856219ba005100f4b16dc3 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/05ff63780f5b7c8c6c590c3626f32ac0 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8d3c4149531f3782f5efbb6a6fbbb7080ba005298ba962b5bc5f66250ea9fde91b34836ed909c16f306d21d2e358f985360962e9362a8e807ccd4254da3bb19b -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/3ca2b6e8101d831e546c1b6ed2ca9a42 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/21a0b9c5acde96c0a91303f4f395e55f272d5585ad18f0365105188d129a3ca94ad66d4dd99b471abdf41a7a7262a3b258fd04b887110ad15255b284cd1612b0 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/d4d560b8ecce0ff2cb4dbc88cb25942a -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/d405f61525af1b2fe85107a70ed67b8a1eb767923487fa71539e0f49d6e70358c8a24f4ef1c224256cf677af99b54a2f8243f1e207350fcb14d426a7a6bb3915 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/8c6eddaa156fd0afee28ac5a154bc3f7 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/b9fc86bb706ad98d61b63eb4cc8bfce6b2c67b58ba2cebecea7574f44790cce044bb1b4db1d20050b59538fa43b51cb352d752c77333a0f0621fde47c63a3596 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/0a54c16fea86c6dadb39eff65c465528 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/c635c636384d3af5b4b078be7398fbc665a185eae69dd223279affb4836fb5c575d6ab296ae940ccbe73777bdb5e355f4f28a2fa27606ac143ff424641c60c65 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/892dfd91703f0f77d170a5371a1c25d4 -CompilerSupportLibraries.v1.1.1+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/8ac59d00192c0e847168e61b3e93957f3909aab59ba8d05e47686a9f8b7226496f89b932151c42198ec966ccd47721cdf547a247ea4e5c61b22bfccce2ec591c -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/05ff63780f5b7c8c6c590c3626f32ac0 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8d3c4149531f3782f5efbb6a6fbbb7080ba005298ba962b5bc5f66250ea9fde91b34836ed909c16f306d21d2e358f985360962e9362a8e807ccd4254da3bb19b -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/3ca2b6e8101d831e546c1b6ed2ca9a42 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/21a0b9c5acde96c0a91303f4f395e55f272d5585ad18f0365105188d129a3ca94ad66d4dd99b471abdf41a7a7262a3b258fd04b887110ad15255b284cd1612b0 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/d4d560b8ecce0ff2cb4dbc88cb25942a -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/d405f61525af1b2fe85107a70ed67b8a1eb767923487fa71539e0f49d6e70358c8a24f4ef1c224256cf677af99b54a2f8243f1e207350fcb14d426a7a6bb3915 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/8c6eddaa156fd0afee28ac5a154bc3f7 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/b9fc86bb706ad98d61b63eb4cc8bfce6b2c67b58ba2cebecea7574f44790cce044bb1b4db1d20050b59538fa43b51cb352d752c77333a0f0621fde47c63a3596 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/0a54c16fea86c6dadb39eff65c465528 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/c635c636384d3af5b4b078be7398fbc665a185eae69dd223279affb4836fb5c575d6ab296ae940ccbe73777bdb5e355f4f28a2fa27606ac143ff424641c60c65 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/892dfd91703f0f77d170a5371a1c25d4 -CompilerSupportLibraries.v1.1.1+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/8ac59d00192c0e847168e61b3e93957f3909aab59ba8d05e47686a9f8b7226496f89b932151c42198ec966ccd47721cdf547a247ea4e5c61b22bfccce2ec591c -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran3.tar.gz/md5/3094705222b6b61fd6a10422a73e1149 -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/27f874cde357ffa45aaa10f2e620ec0f8ab4e5a8bf4607fc023a2ec42040bcc9a724f959237c340d67451f8621402fa05133c1420086b87135f40326c30b97af -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran4.tar.gz/md5/ba0acaff60648efa3915348a8a353df8 -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/0b6aaf75363cbe6133ca3aed351ab58ef1e441f61375f5baf702d8043813c459d48e8af17630f1a07dc22772ec9b02076af33726ed94e6314ae37d5a139d6dcc -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran5.tar.gz/md5/95f1d57cfc43677e40bfc121bce79274 -CompilerSupportLibraries.v1.1.1+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/edacd9960e9de1236c91752e103cddfc018d697e87fabb3cceadf36153b4e97842ef284bd1532290a5620007234882b4c4cd4f36525b61763d97b2f608358262 -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran3.tar.gz/md5/f37fe1818e1634476c44afae478611c8 -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran3.tar.gz/sha512/6e4e3eb5ac9570bfdf5280f59167eb6c4a74f3aa152afb4c5d180b9a6cdbdca557e7dd13f0b5b76943b45a65e848fe77c5b3bbc6ddb0fd846d03fbc9fbedf7ce -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran4.tar.gz/md5/b4ffd52179aa0006c56f279b87cb7556 -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran4.tar.gz/sha512/a047ac7db204c31802f646351af51c55fe06498e851b2df58d7f93f75d9c0067f8736f247f108991ec01ac7f86f3026ecf58b5f2f3a76d7eab00130754e7f704 -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran5.tar.gz/md5/2d38fc835f236f89f457fdf859ccb903 -CompilerSupportLibraries.v1.1.1+0.i686-linux-musl-libgfortran5.tar.gz/sha512/51fbe41efbce33b1cf3728df6fa59fd0e85a13308b3e868fe9f70f4d67857615f83542ba69be824a73e89959503dd7a11335d1c495704bd7d6cad6656d0c5d57 -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/9650002f6729c0964d33afcab334d77d -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/0b7907811a13d09b7b33203c7e46888308c7d6fcf5d69790babafc39f640541551f784264247f159a552f15df1ddd061c421a93b983d838d3bd7f85ba6427f70 -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/47e9fb99906b9647e26e4126a913074e -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/d7285691fbe1318e48e061d678e54890762cc16996652a34b190924cc1462d24ab0b08729945eb25f4bef60e60d50f3e78db57d4cda0302b8ba579db8a1311e1 -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/b588b2710f2b83d2c70c6104e585a3bd -CompilerSupportLibraries.v1.1.1+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b62a63b0c8750f85fc265db88456307b794e912352a68997c7cce06444391307c03edbe5b901833f53c5bd55f5a1e61a586538b08487cc139a2d71fccdce1d31 -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/7cce4f3dc057ebebaa677bf6f0d51e9e -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/a0dd93905f0ede4da5e2fbacf2579154db8ac8e9963c77fb62284489686f2aa372925b3341742d86430a839267421af55f6e1e413473d17f13a1a199e6a904a0 -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/06ee6aaeca78b3e9005f53f1fa32731f -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/ff0e33ce9f93b3a867cf409b95e763efbc8f4dde65ed19107eb14d29460d084f253e03ebd6375f1da996182b3d96e1fda4abff06507258da9a89ece36663db84 -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/483251d28076ee959dff131d13d7e53b -CompilerSupportLibraries.v1.1.1+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/a7c9053a8c1b784cb6459762f26e0c2106a9758cbe2aefe8975a14aaaf61b8a08e51c465e733e44d01537beb59d467c57e536ebd8b27b7b68f46945174c469c7 -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/a147bf3a6d6550c177b8a784b9b02e21 -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/c6f7a13f0195eae8f7ad980a4b24de9b155be69c4437522723411f9866a4aee3c5b350ee2f0c95f41f19aba43acaca78309881157e8498df0664c902d0c05a5d -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/3f19c9d0e723a8d5591357ac3a9452a0 -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/5752bac310d80ed2dc1fc3d6580300d185787b9b933e31c8e0f572099abd0727d9483da8f9af858f706e96a183d2b10702c44381a080438cbb17d6459321ccfb -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/ad0f0e2fe3e7d147a0a27271a2aba0fc -CompilerSupportLibraries.v1.1.1+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/f42231adea3d0b6133c3b5bc5fbf765bc6a7ba8ef0f407fa1b8def36dd8a71d20ef39fb6e57b43208489c2795a96562cdbf15f3d20b3f3a09edb29b99d19a33a -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/4c78d56dbbbff682c0a78d11fb9d1e70 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/0e9d6dcc4b8fddaaa94a26a46e915d33fb474f8a8ee14edd4d1c7e774846c44c5c5d852649a4f70409c99ac0e1d458077b7f0eb7dc0b0326ee8b625644d7074d -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/039d37f813b183c75feebadd21011eb6 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/05e7291de1fd2520247402f0db9d348fdd7a02d8dd9133ac65701f88d237110a3cc6c6e2c5717364ab786b6e6063038ec10c9605e77bc4dbe1064a0e77617f5d -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/a985f13a85eb14d1b6339ba4983dc372 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/27468ccd5642e6e11bd5972684518a0fb883bf4835ac18f5279c3fce97b1779131c7d9e39d8de26a15c293c832946334e964919f51d7679cd0569ce82b938579 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/9d86ce2fe481ea97a1fd098bd47d524c -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/a865a4127bacaedd81b6c81279f6a44bc3497ab29a0401f66da1abfc0738ea459be9f158d06969c161a65925739665084bec5f8650a8cd1e8f0d08f1f44d729f -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/86d9db869a7af6c96dea39f5d9d90505 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/01e0c69b04138989200ded92eddae6ff1873d3a440d17273d08bee40d53b2929e35bfd14be051074fe78671cac34ac2dd7360c1571790ee52f94a5921de42a65 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/e72d28df4bcb60ab2f3389046e7c83a8 -CompilerSupportLibraries.v1.1.1+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/cac193a26328ddeff5f7bcc3d7207101c574f9bdb1bff5c2b925315c5c2404a2fdb6591d1968f30931373fbfcae9bda784c72e65580ad3acc398448cd193f65d -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/35642304a9a2f435cf5214b2715198fe -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/a67f41ba31c99a064f504f508711537f9e90089ca5352bfc2698c3fcd3e499ca716f07ffeac4fb1b88c2c934f7f380f262af8c863d3b16ac7e805d5c805ab358 -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/01df0fbb265e5ff1a480a7a5e23b0835 -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/57a79f2b8e846c1514dcb18420f26ae2889962040f410b746836cab4395749155fa9cd9d00d4c25954c0ffa72f9f3823b1b50688a20ddf675301f64e0d4b5c7e -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/1f1f6380ce8815cc9cedcea0b40860e7 -CompilerSupportLibraries.v1.1.1+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/a88ea8af8c8df792861812bfdf7f1bcaae31582ab78ce78b47a0dc6fd57b93441c0471f529ce23877131ac9701c6eed72ce89241746e18271f3686fbd718138c -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/38fc8c445a1a610db40a7609155e22d6 -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/085652c7ca583c3623611ca9262b70765c9936c9feb5f9034b2c6b6d6677a7a1d7d201b83d82d0d268f3190bd1a62eab0124e8fae3625407dee7f1df89d4106c -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/f3f89eb3c2e441fde6e6b9c1c1a61183 -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/c53f79e20ad043ab099873f38ece98c6bed22950610ba88b9c178a4bd943039cc426473828d509deb8c65c93309da1de87bdf36fb3954b8f8047277c418fe2e0 -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/024f7133425db23e215dc55589bb9171 -CompilerSupportLibraries.v1.1.1+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/819945496ea48dd44d8c0f12a11a358b7d1ebf198d60fbad576d74ddee68cdea98070cdd11ca96567d0c772ec007c03cbc83ff5c7d2ad737cbd486fe0c9afcd5 +CompilerSupportLibraries.v1.2.0+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/20ebaad57850393b6ac9fa924e511fe4 +CompilerSupportLibraries.v1.2.0+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/020de4d8b0ff6bedbadaa305ff8445e6849f12053762ea4aa68412d1ec763dbd86f479587a2fbb862487f1feb04d976c38099ddf3887817a3d32b3f029cf85b1 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/d641904255ee412c45b089d92c53262b +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/ace0383fe9bd64faeed1fb05a11bbec932bd56b8460d06d2b7c3e1b5f4f6e9a9b3345937088684e5cd1ca9a85ef1a5ff56a97a1f60449cd6e35247de1e123d81 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/2a71f320d8b9242ad26aabed74cbf404 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/03e2a4482baaca2d6ce5cc207224d03bd7851486ebe8072c7317f5fcdd641395d945552d9462ab44a9f2e4b0ffaa3874a76f314d67bc0f75393a1151ab518611 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/1beec15ad689a5f572040ca2a7b6a880 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/27bbe212a8d43e841cf8f3e9964b72bc220fea03cf5e65721b02d2f3aa5193acdce41e512578ed6be935b413cd0d2224a6bcd2e9624931f39092ba3cfc5cbcc0 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/9e949c2efe48a7b2a62bff7e1ffdede0 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/2947acb250f8ff4936da5ed02ddbfa492fc38bc87baa588a36bb892ba68b6636a912cda976f8fff00cc7a710c3bfb185826b4cd4a726750ef5f161d5f1aa21a2 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/7202764b1a89a748b07460d9c40a9279 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/63236225a9becdd166c4395ea5081c64f57bc51af89c2edb5abeb419d6eb8224a380a633afd861bb84a12435fd19c8554cbe5ffadf8324ff2c7f17021ed53e69 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/f66c30d3cec8057ae47f05df022ead51 +CompilerSupportLibraries.v1.2.0+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/5329d9469bb0f47560e52b15eb21ab70e0e2da0275bdb2f8e6ed4feb132bc9989a6b44984329455104546c95d05a05f8fb4f1cf232856219ba005100f4b16dc3 +CompilerSupportLibraries.v1.2.0+0.aarch64-unknown-freebsd-libgfortran4.tar.gz/md5/1d8ae93fe000440d00c404ba5044f169 +CompilerSupportLibraries.v1.2.0+0.aarch64-unknown-freebsd-libgfortran4.tar.gz/sha512/6733bd456c389c7c2cd83c5e44aa575552aa7ab5549a5b3efefbc745a6129aa76d78bacb1441208fc77c58b36f1b0775aa3a44bb97e6769ff730744ecf5e8abc +CompilerSupportLibraries.v1.2.0+0.aarch64-unknown-freebsd-libgfortran5.tar.gz/md5/bf1a5a3320a0a38133f04861afab33b8 +CompilerSupportLibraries.v1.2.0+0.aarch64-unknown-freebsd-libgfortran5.tar.gz/sha512/221502795c075f64196dae687a35b83aa83a9a1ecf1ec3e9f51613bd7431c526015e412132a081e00ca13a5730d733330df79baad6fccc8758c17db9877e59dd +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/05ff63780f5b7c8c6c590c3626f32ac0 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8d3c4149531f3782f5efbb6a6fbbb7080ba005298ba962b5bc5f66250ea9fde91b34836ed909c16f306d21d2e358f985360962e9362a8e807ccd4254da3bb19b +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/3ca2b6e8101d831e546c1b6ed2ca9a42 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/21a0b9c5acde96c0a91303f4f395e55f272d5585ad18f0365105188d129a3ca94ad66d4dd99b471abdf41a7a7262a3b258fd04b887110ad15255b284cd1612b0 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/d4d560b8ecce0ff2cb4dbc88cb25942a +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/d405f61525af1b2fe85107a70ed67b8a1eb767923487fa71539e0f49d6e70358c8a24f4ef1c224256cf677af99b54a2f8243f1e207350fcb14d426a7a6bb3915 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/8c6eddaa156fd0afee28ac5a154bc3f7 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/b9fc86bb706ad98d61b63eb4cc8bfce6b2c67b58ba2cebecea7574f44790cce044bb1b4db1d20050b59538fa43b51cb352d752c77333a0f0621fde47c63a3596 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/0a54c16fea86c6dadb39eff65c465528 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/c635c636384d3af5b4b078be7398fbc665a185eae69dd223279affb4836fb5c575d6ab296ae940ccbe73777bdb5e355f4f28a2fa27606ac143ff424641c60c65 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/892dfd91703f0f77d170a5371a1c25d4 +CompilerSupportLibraries.v1.2.0+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/8ac59d00192c0e847168e61b3e93957f3909aab59ba8d05e47686a9f8b7226496f89b932151c42198ec966ccd47721cdf547a247ea4e5c61b22bfccce2ec591c +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/05ff63780f5b7c8c6c590c3626f32ac0 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8d3c4149531f3782f5efbb6a6fbbb7080ba005298ba962b5bc5f66250ea9fde91b34836ed909c16f306d21d2e358f985360962e9362a8e807ccd4254da3bb19b +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/3ca2b6e8101d831e546c1b6ed2ca9a42 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/21a0b9c5acde96c0a91303f4f395e55f272d5585ad18f0365105188d129a3ca94ad66d4dd99b471abdf41a7a7262a3b258fd04b887110ad15255b284cd1612b0 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/d4d560b8ecce0ff2cb4dbc88cb25942a +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/d405f61525af1b2fe85107a70ed67b8a1eb767923487fa71539e0f49d6e70358c8a24f4ef1c224256cf677af99b54a2f8243f1e207350fcb14d426a7a6bb3915 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/8c6eddaa156fd0afee28ac5a154bc3f7 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/b9fc86bb706ad98d61b63eb4cc8bfce6b2c67b58ba2cebecea7574f44790cce044bb1b4db1d20050b59538fa43b51cb352d752c77333a0f0621fde47c63a3596 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/0a54c16fea86c6dadb39eff65c465528 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/c635c636384d3af5b4b078be7398fbc665a185eae69dd223279affb4836fb5c575d6ab296ae940ccbe73777bdb5e355f4f28a2fa27606ac143ff424641c60c65 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/892dfd91703f0f77d170a5371a1c25d4 +CompilerSupportLibraries.v1.2.0+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/8ac59d00192c0e847168e61b3e93957f3909aab59ba8d05e47686a9f8b7226496f89b932151c42198ec966ccd47721cdf547a247ea4e5c61b22bfccce2ec591c +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran3.tar.gz/md5/3094705222b6b61fd6a10422a73e1149 +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/27f874cde357ffa45aaa10f2e620ec0f8ab4e5a8bf4607fc023a2ec42040bcc9a724f959237c340d67451f8621402fa05133c1420086b87135f40326c30b97af +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran4.tar.gz/md5/ba0acaff60648efa3915348a8a353df8 +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/0b6aaf75363cbe6133ca3aed351ab58ef1e441f61375f5baf702d8043813c459d48e8af17630f1a07dc22772ec9b02076af33726ed94e6314ae37d5a139d6dcc +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran5.tar.gz/md5/95f1d57cfc43677e40bfc121bce79274 +CompilerSupportLibraries.v1.2.0+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/edacd9960e9de1236c91752e103cddfc018d697e87fabb3cceadf36153b4e97842ef284bd1532290a5620007234882b4c4cd4f36525b61763d97b2f608358262 +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran3.tar.gz/md5/f37fe1818e1634476c44afae478611c8 +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran3.tar.gz/sha512/6e4e3eb5ac9570bfdf5280f59167eb6c4a74f3aa152afb4c5d180b9a6cdbdca557e7dd13f0b5b76943b45a65e848fe77c5b3bbc6ddb0fd846d03fbc9fbedf7ce +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran4.tar.gz/md5/b4ffd52179aa0006c56f279b87cb7556 +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran4.tar.gz/sha512/a047ac7db204c31802f646351af51c55fe06498e851b2df58d7f93f75d9c0067f8736f247f108991ec01ac7f86f3026ecf58b5f2f3a76d7eab00130754e7f704 +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran5.tar.gz/md5/2d38fc835f236f89f457fdf859ccb903 +CompilerSupportLibraries.v1.2.0+0.i686-linux-musl-libgfortran5.tar.gz/sha512/51fbe41efbce33b1cf3728df6fa59fd0e85a13308b3e868fe9f70f4d67857615f83542ba69be824a73e89959503dd7a11335d1c495704bd7d6cad6656d0c5d57 +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/9650002f6729c0964d33afcab334d77d +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/0b7907811a13d09b7b33203c7e46888308c7d6fcf5d69790babafc39f640541551f784264247f159a552f15df1ddd061c421a93b983d838d3bd7f85ba6427f70 +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/47e9fb99906b9647e26e4126a913074e +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/d7285691fbe1318e48e061d678e54890762cc16996652a34b190924cc1462d24ab0b08729945eb25f4bef60e60d50f3e78db57d4cda0302b8ba579db8a1311e1 +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/b588b2710f2b83d2c70c6104e585a3bd +CompilerSupportLibraries.v1.2.0+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b62a63b0c8750f85fc265db88456307b794e912352a68997c7cce06444391307c03edbe5b901833f53c5bd55f5a1e61a586538b08487cc139a2d71fccdce1d31 +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/7cce4f3dc057ebebaa677bf6f0d51e9e +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/a0dd93905f0ede4da5e2fbacf2579154db8ac8e9963c77fb62284489686f2aa372925b3341742d86430a839267421af55f6e1e413473d17f13a1a199e6a904a0 +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/06ee6aaeca78b3e9005f53f1fa32731f +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/ff0e33ce9f93b3a867cf409b95e763efbc8f4dde65ed19107eb14d29460d084f253e03ebd6375f1da996182b3d96e1fda4abff06507258da9a89ece36663db84 +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/483251d28076ee959dff131d13d7e53b +CompilerSupportLibraries.v1.2.0+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/a7c9053a8c1b784cb6459762f26e0c2106a9758cbe2aefe8975a14aaaf61b8a08e51c465e733e44d01537beb59d467c57e536ebd8b27b7b68f46945174c469c7 +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/a147bf3a6d6550c177b8a784b9b02e21 +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/c6f7a13f0195eae8f7ad980a4b24de9b155be69c4437522723411f9866a4aee3c5b350ee2f0c95f41f19aba43acaca78309881157e8498df0664c902d0c05a5d +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/3f19c9d0e723a8d5591357ac3a9452a0 +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/5752bac310d80ed2dc1fc3d6580300d185787b9b933e31c8e0f572099abd0727d9483da8f9af858f706e96a183d2b10702c44381a080438cbb17d6459321ccfb +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/ad0f0e2fe3e7d147a0a27271a2aba0fc +CompilerSupportLibraries.v1.2.0+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/f42231adea3d0b6133c3b5bc5fbf765bc6a7ba8ef0f407fa1b8def36dd8a71d20ef39fb6e57b43208489c2795a96562cdbf15f3d20b3f3a09edb29b99d19a33a +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/4c78d56dbbbff682c0a78d11fb9d1e70 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/0e9d6dcc4b8fddaaa94a26a46e915d33fb474f8a8ee14edd4d1c7e774846c44c5c5d852649a4f70409c99ac0e1d458077b7f0eb7dc0b0326ee8b625644d7074d +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/039d37f813b183c75feebadd21011eb6 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/05e7291de1fd2520247402f0db9d348fdd7a02d8dd9133ac65701f88d237110a3cc6c6e2c5717364ab786b6e6063038ec10c9605e77bc4dbe1064a0e77617f5d +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/a985f13a85eb14d1b6339ba4983dc372 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/27468ccd5642e6e11bd5972684518a0fb883bf4835ac18f5279c3fce97b1779131c7d9e39d8de26a15c293c832946334e964919f51d7679cd0569ce82b938579 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/9d86ce2fe481ea97a1fd098bd47d524c +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/a865a4127bacaedd81b6c81279f6a44bc3497ab29a0401f66da1abfc0738ea459be9f158d06969c161a65925739665084bec5f8650a8cd1e8f0d08f1f44d729f +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/86d9db869a7af6c96dea39f5d9d90505 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/01e0c69b04138989200ded92eddae6ff1873d3a440d17273d08bee40d53b2929e35bfd14be051074fe78671cac34ac2dd7360c1571790ee52f94a5921de42a65 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/e72d28df4bcb60ab2f3389046e7c83a8 +CompilerSupportLibraries.v1.2.0+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/cac193a26328ddeff5f7bcc3d7207101c574f9bdb1bff5c2b925315c5c2404a2fdb6591d1968f30931373fbfcae9bda784c72e65580ad3acc398448cd193f65d +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/35642304a9a2f435cf5214b2715198fe +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/a67f41ba31c99a064f504f508711537f9e90089ca5352bfc2698c3fcd3e499ca716f07ffeac4fb1b88c2c934f7f380f262af8c863d3b16ac7e805d5c805ab358 +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/01df0fbb265e5ff1a480a7a5e23b0835 +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/57a79f2b8e846c1514dcb18420f26ae2889962040f410b746836cab4395749155fa9cd9d00d4c25954c0ffa72f9f3823b1b50688a20ddf675301f64e0d4b5c7e +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/1f1f6380ce8815cc9cedcea0b40860e7 +CompilerSupportLibraries.v1.2.0+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/a88ea8af8c8df792861812bfdf7f1bcaae31582ab78ce78b47a0dc6fd57b93441c0471f529ce23877131ac9701c6eed72ce89241746e18271f3686fbd718138c +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/38fc8c445a1a610db40a7609155e22d6 +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/085652c7ca583c3623611ca9262b70765c9936c9feb5f9034b2c6b6d6677a7a1d7d201b83d82d0d268f3190bd1a62eab0124e8fae3625407dee7f1df89d4106c +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/f3f89eb3c2e441fde6e6b9c1c1a61183 +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/c53f79e20ad043ab099873f38ece98c6bed22950610ba88b9c178a4bd943039cc426473828d509deb8c65c93309da1de87bdf36fb3954b8f8047277c418fe2e0 +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/024f7133425db23e215dc55589bb9171 +CompilerSupportLibraries.v1.2.0+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/819945496ea48dd44d8c0f12a11a358b7d1ebf198d60fbad576d74ddee68cdea98070cdd11ca96567d0c772ec007c03cbc83ff5c7d2ad737cbd486fe0c9afcd5 diff --git a/deps/checksums/dsfmt b/deps/checksums/dsfmt index 0666e51efa994..99ba378adcd4c 100644 --- a/deps/checksums/dsfmt +++ b/deps/checksums/dsfmt @@ -1,34 +1,36 @@ -dSFMT.v2.2.5+0.aarch64-apple-darwin.tar.gz/md5/36284767f523bb297633d7da17a7db5a -dSFMT.v2.2.5+0.aarch64-apple-darwin.tar.gz/sha512/e6434c154db4c7187f227a550b159a8db8cfffc514323ca31112744a80a007ba5c95f2274cee30c0aa8caf1b20fb643cb814651a622b8e4bb2e5652878e504d2 -dSFMT.v2.2.5+0.aarch64-linux-gnu.tar.gz/md5/260e14855dbc7773a2ca906d58cc57f2 -dSFMT.v2.2.5+0.aarch64-linux-gnu.tar.gz/sha512/820ca4c6afde931e855b74015150f4ffbb513276c3fa7dbcc1ec8d34c02d4989fb7424a6e4f81f93d054811b5f54f8633d955b05acdb088387ee90f1c3b00915 -dSFMT.v2.2.5+0.aarch64-linux-musl.tar.gz/md5/7ddccbad6b5c9de4be187fe76637a0d8 -dSFMT.v2.2.5+0.aarch64-linux-musl.tar.gz/sha512/e3c225da00927096e3a6cd4abc681fba8f469cb74828e7054d4f5684d71dcb8e75c9a81f14fa10bfbb78f62f9567a31a92edcca8d797e5810a2a44a3fc17bc84 -dSFMT.v2.2.5+0.armv6l-linux-gnueabihf.tar.gz/md5/a70329e0a6c57009c6b6950fd34089f6 -dSFMT.v2.2.5+0.armv6l-linux-gnueabihf.tar.gz/sha512/4418c42165660adc050e872ef834f920c89ed6a0d2b816821672b1e862e947aad7efd023289da9bf05bb2eb9ec4b9d2561c403e2d5384d5314a4ba016b1f9cfc -dSFMT.v2.2.5+0.armv6l-linux-musleabihf.tar.gz/md5/6ffc798b8a0c847fa5cb93640bd66ab3 -dSFMT.v2.2.5+0.armv6l-linux-musleabihf.tar.gz/sha512/94e5ae07d0b1420abd7290519bce6f77deae634bbb4df31e3f02416bf509e555a9b1c9d19dd77ca76a308c2b86d5c9d4718b9ef83c13167b88a8181d8ca7e73a -dSFMT.v2.2.5+0.armv7l-linux-gnueabihf.tar.gz/md5/660d95aa08580ca1716a89c4d8b1eb24 -dSFMT.v2.2.5+0.armv7l-linux-gnueabihf.tar.gz/sha512/bc757a9f805047be5375f92c10a3f3eab69345a4ec5cc997f763e66be36144a74d414ff926df8e17b9d5a2394189269c3188c55e0b7c75a72495394d65510cef -dSFMT.v2.2.5+0.armv7l-linux-musleabihf.tar.gz/md5/78c487049092fe61949d506637c713bb -dSFMT.v2.2.5+0.armv7l-linux-musleabihf.tar.gz/sha512/03ddada4478f05eab7d2971b2deaf2cba91f084d7ce66fc8219bcb3cf5c308ea13959fed95568ca80f4ce11794e197092984919265716de8f2558e2cb30d94ce -dSFMT.v2.2.5+0.i686-linux-gnu.tar.gz/md5/11463fd3981a8c143d7aed691d18d4e0 -dSFMT.v2.2.5+0.i686-linux-gnu.tar.gz/sha512/db946a4fbd8a3163b8b1c25e02bfc4a841da7d2532892a99037bd48ac98e1840691e8cc0127d9457a82667a0131e4826cb4e9d0a13f127afc62da4eb68af5a3e -dSFMT.v2.2.5+0.i686-linux-musl.tar.gz/md5/a61405f72c9a3bba5718f078c68e61a5 -dSFMT.v2.2.5+0.i686-linux-musl.tar.gz/sha512/726f130bbbfd0dece4185b89a25a73f3b5b950ebfb7f86aea6e9cbcf9ae932e591d20b854de0b4985103dbf8b4b7cb3560661c5070af971cd2c1f3ec3e1ea7d2 -dSFMT.v2.2.5+0.i686-w64-mingw32.tar.gz/md5/3bc27ef8f26c7a26f096cf1d558d408d -dSFMT.v2.2.5+0.i686-w64-mingw32.tar.gz/sha512/ea3608d3ae3874ea57a1a08f69abe2a1638bc340db71c6fe3c4fd5637d8c54943bf16b099a46817387c1ed4cb5f3cd1c0ff19ae8a4ed85dd555555821af06374 -dSFMT.v2.2.5+0.powerpc64le-linux-gnu.tar.gz/md5/fd8c73961ef7c82201e6d86e8bf4324c -dSFMT.v2.2.5+0.powerpc64le-linux-gnu.tar.gz/sha512/1bd0ebd019cfc6f25f7ba007547c5ee297854655b93c55e90d8ead420875de5a087e38956693d5e901ff2abf667c72aa66fb34f587b82adf4b91b3d5d666b5c7 -dSFMT.v2.2.5+0.x86_64-apple-darwin.tar.gz/md5/6be9f2d3cd8d45a3fc1c3feeebcbbf00 -dSFMT.v2.2.5+0.x86_64-apple-darwin.tar.gz/sha512/5d17c2c0eedad6739b41b8a613e9e452df484136ecd11aed1f6b9f426ae1deaef9faf721810080ebc1701a88a3e7ae91b1992893598c33b342c3f876661f2f8e -dSFMT.v2.2.5+0.x86_64-linux-gnu.tar.gz/md5/fa671f4ca14b171d53c8866d03f9162a -dSFMT.v2.2.5+0.x86_64-linux-gnu.tar.gz/sha512/2e242a1448da0508ea88cc1a106f1e74f8d7e7562cd82b80d86abf9a8b454653ad7612e25c30ce00c23757e8a5b7b5736253b00a52f9473af6c5d4df768138f2 -dSFMT.v2.2.5+0.x86_64-linux-musl.tar.gz/md5/c648294163882ec539ab646542c74880 -dSFMT.v2.2.5+0.x86_64-linux-musl.tar.gz/sha512/9e96a47d660854b6517364f0db40a2f4e0e3b814499a0349f7cf550b1c8d04589fca5eb4a75bf34f36d1b5d1b2277b3e9a961c887092abedd08f438e025329e7 -dSFMT.v2.2.5+0.x86_64-unknown-freebsd.tar.gz/md5/5b53e6c5b78102f563742b4b3d888ec6 -dSFMT.v2.2.5+0.x86_64-unknown-freebsd.tar.gz/sha512/5db5902c7ec2624add768b9e2866f9aac224a31bcb4114d450c45717e2b244521b7c511c059527d557a71639cff98e190a38cd3e28db5be0b1faf0a1762cb1a5 -dSFMT.v2.2.5+0.x86_64-w64-mingw32.tar.gz/md5/386adb3b7593c222dc7a1060a1356b21 -dSFMT.v2.2.5+0.x86_64-w64-mingw32.tar.gz/sha512/fe2ab5021126807b37042e89a22ef9a869c6a0a028680df445773b2affd11c2b02148be07d53504ea3842bb38bb62fe039529688266c1cba3545a892bd4dc185 +dSFMT.v2.2.5+1.aarch64-apple-darwin.tar.gz/md5/1ac287cb891e0bb758e5ae1195e661b7 +dSFMT.v2.2.5+1.aarch64-apple-darwin.tar.gz/sha512/c604d55fb955e9d707e26b654670f07f18ddd0dc93c1a2b678b9cea9b84a24e21c88eb49d39e3e74c930cdffa35e45f5a63e96ecb0a098e8ea538438dc7281bd +dSFMT.v2.2.5+1.aarch64-linux-gnu.tar.gz/md5/260e14855dbc7773a2ca906d58cc57f2 +dSFMT.v2.2.5+1.aarch64-linux-gnu.tar.gz/sha512/820ca4c6afde931e855b74015150f4ffbb513276c3fa7dbcc1ec8d34c02d4989fb7424a6e4f81f93d054811b5f54f8633d955b05acdb088387ee90f1c3b00915 +dSFMT.v2.2.5+1.aarch64-linux-musl.tar.gz/md5/7ddccbad6b5c9de4be187fe76637a0d8 +dSFMT.v2.2.5+1.aarch64-linux-musl.tar.gz/sha512/e3c225da00927096e3a6cd4abc681fba8f469cb74828e7054d4f5684d71dcb8e75c9a81f14fa10bfbb78f62f9567a31a92edcca8d797e5810a2a44a3fc17bc84 +dSFMT.v2.2.5+1.aarch64-unknown-freebsd.tar.gz/md5/84f560104ab5eac8f214559645235350 +dSFMT.v2.2.5+1.aarch64-unknown-freebsd.tar.gz/sha512/3668a37d2516c304b296e2dd7b93a45decb37774088b03438b6d7dec71766d98b2ca1d61c1b317f86ca118d078f53817b6bc86f0ed487185e18b5cc786060592 +dSFMT.v2.2.5+1.armv6l-linux-gnueabihf.tar.gz/md5/a70329e0a6c57009c6b6950fd34089f6 +dSFMT.v2.2.5+1.armv6l-linux-gnueabihf.tar.gz/sha512/4418c42165660adc050e872ef834f920c89ed6a0d2b816821672b1e862e947aad7efd023289da9bf05bb2eb9ec4b9d2561c403e2d5384d5314a4ba016b1f9cfc +dSFMT.v2.2.5+1.armv6l-linux-musleabihf.tar.gz/md5/6ffc798b8a0c847fa5cb93640bd66ab3 +dSFMT.v2.2.5+1.armv6l-linux-musleabihf.tar.gz/sha512/94e5ae07d0b1420abd7290519bce6f77deae634bbb4df31e3f02416bf509e555a9b1c9d19dd77ca76a308c2b86d5c9d4718b9ef83c13167b88a8181d8ca7e73a +dSFMT.v2.2.5+1.armv7l-linux-gnueabihf.tar.gz/md5/660d95aa08580ca1716a89c4d8b1eb24 +dSFMT.v2.2.5+1.armv7l-linux-gnueabihf.tar.gz/sha512/bc757a9f805047be5375f92c10a3f3eab69345a4ec5cc997f763e66be36144a74d414ff926df8e17b9d5a2394189269c3188c55e0b7c75a72495394d65510cef +dSFMT.v2.2.5+1.armv7l-linux-musleabihf.tar.gz/md5/78c487049092fe61949d506637c713bb +dSFMT.v2.2.5+1.armv7l-linux-musleabihf.tar.gz/sha512/03ddada4478f05eab7d2971b2deaf2cba91f084d7ce66fc8219bcb3cf5c308ea13959fed95568ca80f4ce11794e197092984919265716de8f2558e2cb30d94ce +dSFMT.v2.2.5+1.i686-linux-gnu.tar.gz/md5/11463fd3981a8c143d7aed691d18d4e0 +dSFMT.v2.2.5+1.i686-linux-gnu.tar.gz/sha512/db946a4fbd8a3163b8b1c25e02bfc4a841da7d2532892a99037bd48ac98e1840691e8cc0127d9457a82667a0131e4826cb4e9d0a13f127afc62da4eb68af5a3e +dSFMT.v2.2.5+1.i686-linux-musl.tar.gz/md5/a61405f72c9a3bba5718f078c68e61a5 +dSFMT.v2.2.5+1.i686-linux-musl.tar.gz/sha512/726f130bbbfd0dece4185b89a25a73f3b5b950ebfb7f86aea6e9cbcf9ae932e591d20b854de0b4985103dbf8b4b7cb3560661c5070af971cd2c1f3ec3e1ea7d2 +dSFMT.v2.2.5+1.i686-w64-mingw32.tar.gz/md5/3bc27ef8f26c7a26f096cf1d558d408d +dSFMT.v2.2.5+1.i686-w64-mingw32.tar.gz/sha512/ea3608d3ae3874ea57a1a08f69abe2a1638bc340db71c6fe3c4fd5637d8c54943bf16b099a46817387c1ed4cb5f3cd1c0ff19ae8a4ed85dd555555821af06374 +dSFMT.v2.2.5+1.powerpc64le-linux-gnu.tar.gz/md5/fd8c73961ef7c82201e6d86e8bf4324c +dSFMT.v2.2.5+1.powerpc64le-linux-gnu.tar.gz/sha512/1bd0ebd019cfc6f25f7ba007547c5ee297854655b93c55e90d8ead420875de5a087e38956693d5e901ff2abf667c72aa66fb34f587b82adf4b91b3d5d666b5c7 +dSFMT.v2.2.5+1.x86_64-apple-darwin.tar.gz/md5/c8c0cd02cb1aa5e363b0c28a3fc4cf65 +dSFMT.v2.2.5+1.x86_64-apple-darwin.tar.gz/sha512/ac29d4b8aae51349474c9191822f92f69105e19521afe2bd9fc6b16385256610ae31e34cd70d894ed03299f1fd155f0a1db79969d1ed35eea44d11521e2030ab +dSFMT.v2.2.5+1.x86_64-linux-gnu.tar.gz/md5/fa671f4ca14b171d53c8866d03f9162a +dSFMT.v2.2.5+1.x86_64-linux-gnu.tar.gz/sha512/2e242a1448da0508ea88cc1a106f1e74f8d7e7562cd82b80d86abf9a8b454653ad7612e25c30ce00c23757e8a5b7b5736253b00a52f9473af6c5d4df768138f2 +dSFMT.v2.2.5+1.x86_64-linux-musl.tar.gz/md5/c648294163882ec539ab646542c74880 +dSFMT.v2.2.5+1.x86_64-linux-musl.tar.gz/sha512/9e96a47d660854b6517364f0db40a2f4e0e3b814499a0349f7cf550b1c8d04589fca5eb4a75bf34f36d1b5d1b2277b3e9a961c887092abedd08f438e025329e7 +dSFMT.v2.2.5+1.x86_64-unknown-freebsd.tar.gz/md5/4960e4ab2ecb6ae1025f9e7bf4c9a7b8 +dSFMT.v2.2.5+1.x86_64-unknown-freebsd.tar.gz/sha512/a2e8bbe382a0ebdd7b69fafdc901f33767f53b9f8b37a89104f2ef897bb5ec27bc8d3bc21f5cff52ca4f29b3a6a10535f7e5f16ef917a9323858c75f1569ea60 +dSFMT.v2.2.5+1.x86_64-w64-mingw32.tar.gz/md5/386adb3b7593c222dc7a1060a1356b21 +dSFMT.v2.2.5+1.x86_64-w64-mingw32.tar.gz/sha512/fe2ab5021126807b37042e89a22ef9a869c6a0a028680df445773b2affd11c2b02148be07d53504ea3842bb38bb62fe039529688266c1cba3545a892bd4dc185 dsfmt-2.2.5.tar.gz/md5/d22e476b52cdee7d5b90d2f289570073 dsfmt-2.2.5.tar.gz/sha512/951e8669350f750b8915a819e704eae0a9b9c9518b3e3b9a1905f9ca0d25cc4c2486cb479e258a4a114e9c26ceb73a6c4e9f1cc02ed19173aeb8f20189754f6b diff --git a/deps/checksums/gmp b/deps/checksums/gmp index 0c7dd415e6f16..c786fddafef5e 100644 --- a/deps/checksums/gmp +++ b/deps/checksums/gmp @@ -1,60 +1,62 @@ -GMP.v6.3.0+0.aarch64-apple-darwin.tar.gz/md5/70a730ecf64eefb5a13f4524e29a6388 -GMP.v6.3.0+0.aarch64-apple-darwin.tar.gz/sha512/51791b4ae0ede1db4c6e7759072d125ca56f6a3a3e43fd5970981a3b2d651f28fe0abefce4b3ad0589d3a46c143054d20fee801bbd423bd2a4c12ba97314c39c -GMP.v6.3.0+0.aarch64-linux-gnu-cxx03.tar.gz/md5/e2b0bf1317259972cdc4f0e6fc3c2bc8 -GMP.v6.3.0+0.aarch64-linux-gnu-cxx03.tar.gz/sha512/8de1dd5d6971c76693c67222725c9eb0a1d276a55a28cd49d94115123100bfe45144652421d4cde468dce67a5630736f4174c9491c8a6e2543aadcb44f1f2d12 -GMP.v6.3.0+0.aarch64-linux-gnu-cxx11.tar.gz/md5/2017b6215ed99c3aed8b04abe75cb3e9 -GMP.v6.3.0+0.aarch64-linux-gnu-cxx11.tar.gz/sha512/78b22106f96348f0d9222279fdf8d1e3f5bd400f771fb0c54dd4045985ee05b896e3097f788739eefab9a9ab09a885aad65c4adb31ae5ba59b7ab22ca10bb574 -GMP.v6.3.0+0.aarch64-linux-musl-cxx03.tar.gz/md5/6477f35f92203db871f56f047b99a1fe -GMP.v6.3.0+0.aarch64-linux-musl-cxx03.tar.gz/sha512/66a6d18979c1ee9a5d06323a717d0a5dd73efc196087349408e739d7aa0444e8ee1af4bd634f85dfd4cfa4c97c24dda4ba472b490f50409581aff967c81b0750 -GMP.v6.3.0+0.aarch64-linux-musl-cxx11.tar.gz/md5/4648558f1e42b8e679f5be494a910402 -GMP.v6.3.0+0.aarch64-linux-musl-cxx11.tar.gz/sha512/9b7ff68a412bccd423b3cffefbc6350db6db8f3f7657713767187c2c2ea3b09d835e1c80d34ab4407f79fccbec82594e024787def27b9ad2ee7ea01ef1607b53 -GMP.v6.3.0+0.armv6l-linux-gnueabihf-cxx03.tar.gz/md5/6cabb238d148b3e2e76e8527e65893cd -GMP.v6.3.0+0.armv6l-linux-gnueabihf-cxx03.tar.gz/sha512/07b5673b4680781b7d42399213ecd491ede8883bbf1825689ad6678986a76581f6c4e53f17353f63bec8db8df5ed3fbddc228694eecc54ae7fc949f106bb8f14 -GMP.v6.3.0+0.armv6l-linux-gnueabihf-cxx11.tar.gz/md5/0257216ad4e96b404d456f07fcc30b09 -GMP.v6.3.0+0.armv6l-linux-gnueabihf-cxx11.tar.gz/sha512/ae8bbbbe3992f78186fe7535e450330e94e6630540eefbdfb51bb5014afd90feac0b1583e3fd2bbf226e61523647b3ec6324188bd6267c353a2a98594566c02b -GMP.v6.3.0+0.armv6l-linux-musleabihf-cxx03.tar.gz/md5/48b949c062ea27dc0dbcc07ea5387821 -GMP.v6.3.0+0.armv6l-linux-musleabihf-cxx03.tar.gz/sha512/03699c20b5c50dbd44f45a0f5f115c6b10b4e8de68d747bceba605c3090469c819b82ad7e57fe7702c1700c25aae6ab9394a22ded319bc58c80e9d20692b610e -GMP.v6.3.0+0.armv6l-linux-musleabihf-cxx11.tar.gz/md5/847ba3116072a523e1ff4ce83e5a18a8 -GMP.v6.3.0+0.armv6l-linux-musleabihf-cxx11.tar.gz/sha512/402548acd57f4112bf2435803f35ea93fd8d07f3df0e2f053b0bec6b08aa3dff4052990a724e2547ce35a29ee376b17d34b7e7e2ab45ecb4981ffc99c56f1a9f -GMP.v6.3.0+0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5/5cc75b66059c3b8b5fbf9b8fcb781b10 -GMP.v6.3.0+0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512/1ef583d014c825e1d4e6d5f7e2d84c3ba183ba9490410f5d424760e275b7032e98f8377d87ed349d4969c6ef8f9b961a1e8df6f40efb406d41983446a9510303 -GMP.v6.3.0+0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5/c0295c143bcb6b53d6184e2852ce35c5 -GMP.v6.3.0+0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512/3c74edb123a6f4147b416e5f7f25903bc859ac5f58f141bd463d3dff8cc2928fedf176f20869a1018a2731c1d7170444b3b3405c8f89c3fc22dc2edf9c036c24 -GMP.v6.3.0+0.armv7l-linux-musleabihf-cxx03.tar.gz/md5/a67696b02a7f67405dd84252c908e071 -GMP.v6.3.0+0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512/73ba1809cfc68199401974f73e7a37b1fe00d4c0cf3e58ed85d161a8fbac4390aeb28591c3108fc503ef8fb5b131d027cb76dcf5d7731698997c2f377d929dce -GMP.v6.3.0+0.armv7l-linux-musleabihf-cxx11.tar.gz/md5/484f00cd5b0beec20f63cd6734d02611 -GMP.v6.3.0+0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512/46fc56f945647f5c8577ad45f540a034f747604e5a89230d9d419b10d5f0571c7580e18e1138ea920efc08b25798c0c7110e15359de17dce3b6db7f07b8ceb3a -GMP.v6.3.0+0.i686-linux-gnu-cxx03.tar.gz/md5/d36d84638e2e5f927d15f07c55919f5f -GMP.v6.3.0+0.i686-linux-gnu-cxx03.tar.gz/sha512/61c62084ab90d25f7168281c7fb672f5bcafdf909afbf66847cfaa1077dd5474b2c27464eb76cac45f5e319aca0c4f7367fc238b83d2dde46ba90a7c1f396dfb -GMP.v6.3.0+0.i686-linux-gnu-cxx11.tar.gz/md5/d87627470bdcac981f7b004c27ac9a89 -GMP.v6.3.0+0.i686-linux-gnu-cxx11.tar.gz/sha512/2a34028687f75422b43f5365b0a8c9530b29473d41bfec4fb9822f074f813b8c6c1fc9efbfbb17a7e4d3d66f2549b5589b3fdbd08711a365330deb72be4958d0 -GMP.v6.3.0+0.i686-linux-musl-cxx03.tar.gz/md5/a2f2fc663bcacfc3e7d6aff29a52de23 -GMP.v6.3.0+0.i686-linux-musl-cxx03.tar.gz/sha512/a30a5d0ee78e747f074b3a5f0a26b9ba99b7553b3c83411a3cb9298814e605509194e9f0d8934caaa1cb7b78eef521805bbc86a297aebd06473ba80a20ffc443 -GMP.v6.3.0+0.i686-linux-musl-cxx11.tar.gz/md5/246b24935442815ff75a13b3dcf24756 -GMP.v6.3.0+0.i686-linux-musl-cxx11.tar.gz/sha512/ca351c4b93adf3f3e40f93c7b0cd61b33ec10049d39e8d33975f46d509efcded67600e6b19d8018a29ee893027d7a28edef0b19c1d70451d072a7a0989e9317d -GMP.v6.3.0+0.i686-w64-mingw32-cxx03.tar.gz/md5/c3b321ae48db0cb8dac4e09e2722e56c -GMP.v6.3.0+0.i686-w64-mingw32-cxx03.tar.gz/sha512/6a6feeb8baf6d499409a9010295b474a8c6de461fa0e34562d53e58190b66c50e278fae7560495cd85ea6f5b41f9e8c6e950ff4f451d26d0757e1d1696e8bca5 -GMP.v6.3.0+0.i686-w64-mingw32-cxx11.tar.gz/md5/3f633b0ff74c2a44350855fc6ce310b8 -GMP.v6.3.0+0.i686-w64-mingw32-cxx11.tar.gz/sha512/eecb17dec70fe84d90f47e1958672d273c865da9607ba3056c9c923a6ff9a3cab5b30414389d8f0c7f5ae5d87c05999964ed0900c80ae5afb525eaec00f401e2 -GMP.v6.3.0+0.powerpc64le-linux-gnu-cxx03.tar.gz/md5/8b5f113ad7fd4a312229cfe8c2d1abca -GMP.v6.3.0+0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512/36525ffc0ac5c363810c47945c34c81daabf88cf1f9c60d236447249d06332d3f5a130b431ab2d1c0148eb5413a4fa66bdd50671f2e7fcb77858d9fcdf83a94c -GMP.v6.3.0+0.powerpc64le-linux-gnu-cxx11.tar.gz/md5/7f1237e9668136b00dd719a5cad3b6aa -GMP.v6.3.0+0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512/46a6efe23173a12299da371121847d16d7950ffe5c87d1221b54c5e95dafbf723c4a327b1c2e832d4742a91254aa40fd5d8152d6d0801769b2efd4f83a042afd -GMP.v6.3.0+0.x86_64-apple-darwin.tar.gz/md5/cd2d1b309aea2c781a9c28470fd2f0eb -GMP.v6.3.0+0.x86_64-apple-darwin.tar.gz/sha512/d7f94d80f1ba170c9553601d1af323bef7bbb98575b80b58b3d7b37d69d81cdee0e132fb4fa20393a0e8719984c785d0c7e5c8ae2c29c62ffbd82b00375993d4 -GMP.v6.3.0+0.x86_64-linux-gnu-cxx03.tar.gz/md5/5be8efef65dafe52e5726ef24238ae36 -GMP.v6.3.0+0.x86_64-linux-gnu-cxx03.tar.gz/sha512/f4c303fe915c89fecdb5a333a30412e0cfb04e07b4f1bc2f726179243dbc61d60ae5b0773a6bd5da8a10cb8764e448bc88035a639ea88d2e06f04e55074d8551 -GMP.v6.3.0+0.x86_64-linux-gnu-cxx11.tar.gz/md5/66f9a3858d07591227f2bc057c3c988b -GMP.v6.3.0+0.x86_64-linux-gnu-cxx11.tar.gz/sha512/5611b9bfd24efac0a189bbd85533e1cd2bee7f833f5ae0a06343f2c1d92925e0d0f0758b99c43520293348ad61f98e1b470829514c35d208697988d8b469fc41 -GMP.v6.3.0+0.x86_64-linux-musl-cxx03.tar.gz/md5/edaa83f6432ff7e75e106d8bfd03d509 -GMP.v6.3.0+0.x86_64-linux-musl-cxx03.tar.gz/sha512/1587e7b91e387da9c23559826c161fa4d447250bd7b6565f0b9fedc36e7502dc2b59caa8157abcb7e7862d24d696470289bd650511b07e8711ecf5a462330b6d -GMP.v6.3.0+0.x86_64-linux-musl-cxx11.tar.gz/md5/e668c4f0c1246aa1510c36f246b1b483 -GMP.v6.3.0+0.x86_64-linux-musl-cxx11.tar.gz/sha512/cf4bd47a5ddb067a57e852855fbd637a93f3652c3327af256f74e9e265c9e0de7c5be78b3e7bcbf08a03916876ecdc05cc294149e2c3d472a30fedc2e6dded47 -GMP.v6.3.0+0.x86_64-unknown-freebsd.tar.gz/md5/4cbf56d2884aa357291321b182d07cb8 -GMP.v6.3.0+0.x86_64-unknown-freebsd.tar.gz/sha512/0c723b8e0f5fabf9e43945d3fb355c3d7b036662a8d6542629aaff27164f12d13b2a19f5c4964f165466705b231884b7f7193d7a01a0e9d3644da1d79af79631 -GMP.v6.3.0+0.x86_64-w64-mingw32-cxx03.tar.gz/md5/02e8f5d66c15731117cf805e0a4c4976 -GMP.v6.3.0+0.x86_64-w64-mingw32-cxx03.tar.gz/sha512/1f94805fe9f34f4e77c54e92625615d91ade617468483409037d0693c3bf106187916d9d21e92681673faae158b376133c0ede643f31bfc9f73ac29c9fd13bcc -GMP.v6.3.0+0.x86_64-w64-mingw32-cxx11.tar.gz/md5/10752137fccc73175872db07749d6f49 -GMP.v6.3.0+0.x86_64-w64-mingw32-cxx11.tar.gz/sha512/3a5d7e8125f3b538a2e59e9c6919db36c974575e6b1950451cb60307da68dc092c4ce21b8f49c40871aadf3bd07681b43eea9c7bf37ba383da9a0e80c30b176e +GMP.v6.3.0+1.aarch64-apple-darwin.tar.gz/md5/70a730ecf64eefb5a13f4524e29a6388 +GMP.v6.3.0+1.aarch64-apple-darwin.tar.gz/sha512/51791b4ae0ede1db4c6e7759072d125ca56f6a3a3e43fd5970981a3b2d651f28fe0abefce4b3ad0589d3a46c143054d20fee801bbd423bd2a4c12ba97314c39c +GMP.v6.3.0+1.aarch64-linux-gnu-cxx03.tar.gz/md5/e2b0bf1317259972cdc4f0e6fc3c2bc8 +GMP.v6.3.0+1.aarch64-linux-gnu-cxx03.tar.gz/sha512/8de1dd5d6971c76693c67222725c9eb0a1d276a55a28cd49d94115123100bfe45144652421d4cde468dce67a5630736f4174c9491c8a6e2543aadcb44f1f2d12 +GMP.v6.3.0+1.aarch64-linux-gnu-cxx11.tar.gz/md5/2017b6215ed99c3aed8b04abe75cb3e9 +GMP.v6.3.0+1.aarch64-linux-gnu-cxx11.tar.gz/sha512/78b22106f96348f0d9222279fdf8d1e3f5bd400f771fb0c54dd4045985ee05b896e3097f788739eefab9a9ab09a885aad65c4adb31ae5ba59b7ab22ca10bb574 +GMP.v6.3.0+1.aarch64-linux-musl-cxx03.tar.gz/md5/6477f35f92203db871f56f047b99a1fe +GMP.v6.3.0+1.aarch64-linux-musl-cxx03.tar.gz/sha512/66a6d18979c1ee9a5d06323a717d0a5dd73efc196087349408e739d7aa0444e8ee1af4bd634f85dfd4cfa4c97c24dda4ba472b490f50409581aff967c81b0750 +GMP.v6.3.0+1.aarch64-linux-musl-cxx11.tar.gz/md5/4648558f1e42b8e679f5be494a910402 +GMP.v6.3.0+1.aarch64-linux-musl-cxx11.tar.gz/sha512/9b7ff68a412bccd423b3cffefbc6350db6db8f3f7657713767187c2c2ea3b09d835e1c80d34ab4407f79fccbec82594e024787def27b9ad2ee7ea01ef1607b53 +GMP.v6.3.0+1.aarch64-unknown-freebsd.tar.gz/md5/362bc3fdbcd6d74b9fddb8a4d640d99a +GMP.v6.3.0+1.aarch64-unknown-freebsd.tar.gz/sha512/8e560b4d1014382d784ccf7c9dc6365526566301ec6a28d115170c0be92b8e6033b6c08f922104e405cf978204579754f0740aae97d0a334e47ed6f684aa4af4 +GMP.v6.3.0+1.armv6l-linux-gnueabihf-cxx03.tar.gz/md5/6cabb238d148b3e2e76e8527e65893cd +GMP.v6.3.0+1.armv6l-linux-gnueabihf-cxx03.tar.gz/sha512/07b5673b4680781b7d42399213ecd491ede8883bbf1825689ad6678986a76581f6c4e53f17353f63bec8db8df5ed3fbddc228694eecc54ae7fc949f106bb8f14 +GMP.v6.3.0+1.armv6l-linux-gnueabihf-cxx11.tar.gz/md5/0257216ad4e96b404d456f07fcc30b09 +GMP.v6.3.0+1.armv6l-linux-gnueabihf-cxx11.tar.gz/sha512/ae8bbbbe3992f78186fe7535e450330e94e6630540eefbdfb51bb5014afd90feac0b1583e3fd2bbf226e61523647b3ec6324188bd6267c353a2a98594566c02b +GMP.v6.3.0+1.armv6l-linux-musleabihf-cxx03.tar.gz/md5/48b949c062ea27dc0dbcc07ea5387821 +GMP.v6.3.0+1.armv6l-linux-musleabihf-cxx03.tar.gz/sha512/03699c20b5c50dbd44f45a0f5f115c6b10b4e8de68d747bceba605c3090469c819b82ad7e57fe7702c1700c25aae6ab9394a22ded319bc58c80e9d20692b610e +GMP.v6.3.0+1.armv6l-linux-musleabihf-cxx11.tar.gz/md5/847ba3116072a523e1ff4ce83e5a18a8 +GMP.v6.3.0+1.armv6l-linux-musleabihf-cxx11.tar.gz/sha512/402548acd57f4112bf2435803f35ea93fd8d07f3df0e2f053b0bec6b08aa3dff4052990a724e2547ce35a29ee376b17d34b7e7e2ab45ecb4981ffc99c56f1a9f +GMP.v6.3.0+1.armv7l-linux-gnueabihf-cxx03.tar.gz/md5/5cc75b66059c3b8b5fbf9b8fcb781b10 +GMP.v6.3.0+1.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512/1ef583d014c825e1d4e6d5f7e2d84c3ba183ba9490410f5d424760e275b7032e98f8377d87ed349d4969c6ef8f9b961a1e8df6f40efb406d41983446a9510303 +GMP.v6.3.0+1.armv7l-linux-gnueabihf-cxx11.tar.gz/md5/c0295c143bcb6b53d6184e2852ce35c5 +GMP.v6.3.0+1.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512/3c74edb123a6f4147b416e5f7f25903bc859ac5f58f141bd463d3dff8cc2928fedf176f20869a1018a2731c1d7170444b3b3405c8f89c3fc22dc2edf9c036c24 +GMP.v6.3.0+1.armv7l-linux-musleabihf-cxx03.tar.gz/md5/a67696b02a7f67405dd84252c908e071 +GMP.v6.3.0+1.armv7l-linux-musleabihf-cxx03.tar.gz/sha512/73ba1809cfc68199401974f73e7a37b1fe00d4c0cf3e58ed85d161a8fbac4390aeb28591c3108fc503ef8fb5b131d027cb76dcf5d7731698997c2f377d929dce +GMP.v6.3.0+1.armv7l-linux-musleabihf-cxx11.tar.gz/md5/484f00cd5b0beec20f63cd6734d02611 +GMP.v6.3.0+1.armv7l-linux-musleabihf-cxx11.tar.gz/sha512/46fc56f945647f5c8577ad45f540a034f747604e5a89230d9d419b10d5f0571c7580e18e1138ea920efc08b25798c0c7110e15359de17dce3b6db7f07b8ceb3a +GMP.v6.3.0+1.i686-linux-gnu-cxx03.tar.gz/md5/d36d84638e2e5f927d15f07c55919f5f +GMP.v6.3.0+1.i686-linux-gnu-cxx03.tar.gz/sha512/61c62084ab90d25f7168281c7fb672f5bcafdf909afbf66847cfaa1077dd5474b2c27464eb76cac45f5e319aca0c4f7367fc238b83d2dde46ba90a7c1f396dfb +GMP.v6.3.0+1.i686-linux-gnu-cxx11.tar.gz/md5/d87627470bdcac981f7b004c27ac9a89 +GMP.v6.3.0+1.i686-linux-gnu-cxx11.tar.gz/sha512/2a34028687f75422b43f5365b0a8c9530b29473d41bfec4fb9822f074f813b8c6c1fc9efbfbb17a7e4d3d66f2549b5589b3fdbd08711a365330deb72be4958d0 +GMP.v6.3.0+1.i686-linux-musl-cxx03.tar.gz/md5/a2f2fc663bcacfc3e7d6aff29a52de23 +GMP.v6.3.0+1.i686-linux-musl-cxx03.tar.gz/sha512/a30a5d0ee78e747f074b3a5f0a26b9ba99b7553b3c83411a3cb9298814e605509194e9f0d8934caaa1cb7b78eef521805bbc86a297aebd06473ba80a20ffc443 +GMP.v6.3.0+1.i686-linux-musl-cxx11.tar.gz/md5/246b24935442815ff75a13b3dcf24756 +GMP.v6.3.0+1.i686-linux-musl-cxx11.tar.gz/sha512/ca351c4b93adf3f3e40f93c7b0cd61b33ec10049d39e8d33975f46d509efcded67600e6b19d8018a29ee893027d7a28edef0b19c1d70451d072a7a0989e9317d +GMP.v6.3.0+1.i686-w64-mingw32-cxx03.tar.gz/md5/c3b321ae48db0cb8dac4e09e2722e56c +GMP.v6.3.0+1.i686-w64-mingw32-cxx03.tar.gz/sha512/6a6feeb8baf6d499409a9010295b474a8c6de461fa0e34562d53e58190b66c50e278fae7560495cd85ea6f5b41f9e8c6e950ff4f451d26d0757e1d1696e8bca5 +GMP.v6.3.0+1.i686-w64-mingw32-cxx11.tar.gz/md5/3f633b0ff74c2a44350855fc6ce310b8 +GMP.v6.3.0+1.i686-w64-mingw32-cxx11.tar.gz/sha512/eecb17dec70fe84d90f47e1958672d273c865da9607ba3056c9c923a6ff9a3cab5b30414389d8f0c7f5ae5d87c05999964ed0900c80ae5afb525eaec00f401e2 +GMP.v6.3.0+1.powerpc64le-linux-gnu-cxx03.tar.gz/md5/8b5f113ad7fd4a312229cfe8c2d1abca +GMP.v6.3.0+1.powerpc64le-linux-gnu-cxx03.tar.gz/sha512/36525ffc0ac5c363810c47945c34c81daabf88cf1f9c60d236447249d06332d3f5a130b431ab2d1c0148eb5413a4fa66bdd50671f2e7fcb77858d9fcdf83a94c +GMP.v6.3.0+1.powerpc64le-linux-gnu-cxx11.tar.gz/md5/7f1237e9668136b00dd719a5cad3b6aa +GMP.v6.3.0+1.powerpc64le-linux-gnu-cxx11.tar.gz/sha512/46a6efe23173a12299da371121847d16d7950ffe5c87d1221b54c5e95dafbf723c4a327b1c2e832d4742a91254aa40fd5d8152d6d0801769b2efd4f83a042afd +GMP.v6.3.0+1.x86_64-apple-darwin.tar.gz/md5/cd2d1b309aea2c781a9c28470fd2f0eb +GMP.v6.3.0+1.x86_64-apple-darwin.tar.gz/sha512/d7f94d80f1ba170c9553601d1af323bef7bbb98575b80b58b3d7b37d69d81cdee0e132fb4fa20393a0e8719984c785d0c7e5c8ae2c29c62ffbd82b00375993d4 +GMP.v6.3.0+1.x86_64-linux-gnu-cxx03.tar.gz/md5/5be8efef65dafe52e5726ef24238ae36 +GMP.v6.3.0+1.x86_64-linux-gnu-cxx03.tar.gz/sha512/f4c303fe915c89fecdb5a333a30412e0cfb04e07b4f1bc2f726179243dbc61d60ae5b0773a6bd5da8a10cb8764e448bc88035a639ea88d2e06f04e55074d8551 +GMP.v6.3.0+1.x86_64-linux-gnu-cxx11.tar.gz/md5/66f9a3858d07591227f2bc057c3c988b +GMP.v6.3.0+1.x86_64-linux-gnu-cxx11.tar.gz/sha512/5611b9bfd24efac0a189bbd85533e1cd2bee7f833f5ae0a06343f2c1d92925e0d0f0758b99c43520293348ad61f98e1b470829514c35d208697988d8b469fc41 +GMP.v6.3.0+1.x86_64-linux-musl-cxx03.tar.gz/md5/edaa83f6432ff7e75e106d8bfd03d509 +GMP.v6.3.0+1.x86_64-linux-musl-cxx03.tar.gz/sha512/1587e7b91e387da9c23559826c161fa4d447250bd7b6565f0b9fedc36e7502dc2b59caa8157abcb7e7862d24d696470289bd650511b07e8711ecf5a462330b6d +GMP.v6.3.0+1.x86_64-linux-musl-cxx11.tar.gz/md5/e668c4f0c1246aa1510c36f246b1b483 +GMP.v6.3.0+1.x86_64-linux-musl-cxx11.tar.gz/sha512/cf4bd47a5ddb067a57e852855fbd637a93f3652c3327af256f74e9e265c9e0de7c5be78b3e7bcbf08a03916876ecdc05cc294149e2c3d472a30fedc2e6dded47 +GMP.v6.3.0+1.x86_64-unknown-freebsd.tar.gz/md5/4cbf56d2884aa357291321b182d07cb8 +GMP.v6.3.0+1.x86_64-unknown-freebsd.tar.gz/sha512/0c723b8e0f5fabf9e43945d3fb355c3d7b036662a8d6542629aaff27164f12d13b2a19f5c4964f165466705b231884b7f7193d7a01a0e9d3644da1d79af79631 +GMP.v6.3.0+1.x86_64-w64-mingw32-cxx03.tar.gz/md5/02e8f5d66c15731117cf805e0a4c4976 +GMP.v6.3.0+1.x86_64-w64-mingw32-cxx03.tar.gz/sha512/1f94805fe9f34f4e77c54e92625615d91ade617468483409037d0693c3bf106187916d9d21e92681673faae158b376133c0ede643f31bfc9f73ac29c9fd13bcc +GMP.v6.3.0+1.x86_64-w64-mingw32-cxx11.tar.gz/md5/10752137fccc73175872db07749d6f49 +GMP.v6.3.0+1.x86_64-w64-mingw32-cxx11.tar.gz/sha512/3a5d7e8125f3b538a2e59e9c6919db36c974575e6b1950451cb60307da68dc092c4ce21b8f49c40871aadf3bd07681b43eea9c7bf37ba383da9a0e80c30b176e gmp-6.3.0.tar.bz2/md5/c1cd6ef33085e9cb818b9b08371f9000 gmp-6.3.0.tar.bz2/sha512/3b684c9bcb9ede2b7e54d0ba4c9764bfa17c20d4f3000017c553b6f1e135b536949580ff37341680c25dc236cfe0ba1db8cfdfe619ce013656189ef0871b89f8 diff --git a/deps/checksums/libuv b/deps/checksums/libuv index 6887c3fe62f41..49869af795d45 100644 --- a/deps/checksums/libuv +++ b/deps/checksums/libuv @@ -1,34 +1,36 @@ -LibUV.v2.0.1+18.aarch64-apple-darwin.tar.gz/md5/f176c76e5e2096dea8443302cf9550b8 -LibUV.v2.0.1+18.aarch64-apple-darwin.tar.gz/sha512/4301b13953a08a758b86e30af3261fd9a291ce3829b4d98e71e2a2c040e322e284c5a6eb4bc7189cc352f4b1cf7041e2cfd3380d511d88c151df3101ad74594e -LibUV.v2.0.1+18.aarch64-linux-gnu.tar.gz/md5/c81515783363702a1bd4b65fd6d7f36b -LibUV.v2.0.1+18.aarch64-linux-gnu.tar.gz/sha512/011429365337f5a45e56ca7a42709866bb994c747a1170d870f5f3ddfff2d36138866ee9278ac01172bc71bde8dee404bcb9cae9c7b44222bf1cc883659df269 -LibUV.v2.0.1+18.aarch64-linux-musl.tar.gz/md5/e74d5ea4912dd326b2705638faa7b805 -LibUV.v2.0.1+18.aarch64-linux-musl.tar.gz/sha512/a26a9f2c9051816230324071c502321f7af3885d581a400615858a93a4cd457226048d15b0e7f6a73d12659763c705b5ab519e9f5b35c6d886b9fd5babbfe352 -LibUV.v2.0.1+18.armv6l-linux-gnueabihf.tar.gz/md5/6df38bcf5d0a61dee63d16b73d0c9a24 -LibUV.v2.0.1+18.armv6l-linux-gnueabihf.tar.gz/sha512/d5354a6532061de0a58965ce0e427bde52f9ae0ee39a98e1a33de4c414fddcba9ba139ddf91be7321a4ccc97bbf7a8a8357ff10cf60f83c0a6bff7d839d6d7a8 -LibUV.v2.0.1+18.armv6l-linux-musleabihf.tar.gz/md5/6f02a24cfbfae3032fadceaea1faed39 -LibUV.v2.0.1+18.armv6l-linux-musleabihf.tar.gz/sha512/7fd107eb9a5ea84b488ea02e4fbedc9fe13bb11be859986a47af38f40ad775dd9f738c790878a3503437bcac1eb26ad9fe26f4aa0d3cb45c980b4c5abc9aec99 -LibUV.v2.0.1+18.armv7l-linux-gnueabihf.tar.gz/md5/96b09dec72f7e9b7409fa2920e67c866 -LibUV.v2.0.1+18.armv7l-linux-gnueabihf.tar.gz/sha512/6a0f79fc15c944fabba5c65180b665bc9769c6ff25863e330049f48b3a4394b448492f5a9a76bb7f8dbd3ce44dfc6f9ccdc2c71c42e1c749e88070fe99b1db69 -LibUV.v2.0.1+18.armv7l-linux-musleabihf.tar.gz/md5/f44e4b2521a813181f943895bdb0dd3c -LibUV.v2.0.1+18.armv7l-linux-musleabihf.tar.gz/sha512/cda1413dca817f772e8b343db0c6de0ef6b8f269e9a6a2ef3403c2582aeab554f46281bbb1eb4659c259198ef47fe26aab648a281e66f80aaf2f2cda0a23ac05 -LibUV.v2.0.1+18.i686-linux-gnu.tar.gz/md5/1f231d89cf9c04515d2d107a5d786cc8 -LibUV.v2.0.1+18.i686-linux-gnu.tar.gz/sha512/089cb8a372cdee0cbc0e78fc201611bb9bafd99af9a78e09d6097a6b70e7c4aa001ebd86f944b0a885c072093c529bf86bcaa32bde4fc1934407a858c1a5a764 -LibUV.v2.0.1+18.i686-linux-musl.tar.gz/md5/01cfc2a9e2536dbd330267917abb19ce -LibUV.v2.0.1+18.i686-linux-musl.tar.gz/sha512/72f3588cb464a60e61f8998242aaa2abdf93df920a2feba5e1d66ef0f2498488df0ec415cbb499d7f75c47bdfc7e3a2fdda6a94383492e0ad13e464eb1314ff8 -LibUV.v2.0.1+18.i686-w64-mingw32.tar.gz/md5/8c6599aab9ed4c46e52f03683aac664e -LibUV.v2.0.1+18.i686-w64-mingw32.tar.gz/sha512/13f0565f7244a8bcf1ab43fac91a856dc86d214877033a3cefee8c2179c1a275dfd7dda32e9017763acac2ba42ab6799934a58f5feaa38fb6cf2253dd713f57a -LibUV.v2.0.1+18.powerpc64le-linux-gnu.tar.gz/md5/af0e43d9d0aa91dd82b63220d96991ef -LibUV.v2.0.1+18.powerpc64le-linux-gnu.tar.gz/sha512/9fabe3089e4fc60e910770c32d36300ce8ef36c28e8cc9c72fbecba6eb80285ee8174e84e4452fb4ce90ee7c7f94e99b03fce47d8c579bd614bfffd553f93666 -LibUV.v2.0.1+18.x86_64-apple-darwin.tar.gz/md5/871040e874eedae54553d8f1c91b9133 -LibUV.v2.0.1+18.x86_64-apple-darwin.tar.gz/sha512/d5eee08b65e4bb8b444c61ac277bec9ef944b9279dd7f0732b3cd91d47788c77938e5db71e019e01bbe7785a75df95faf14368764f700c6b7a6b9e4d96d6b4c2 -LibUV.v2.0.1+18.x86_64-linux-gnu.tar.gz/md5/d2d186952c6d017fe33f6a6bea63a3ea -LibUV.v2.0.1+18.x86_64-linux-gnu.tar.gz/sha512/15501534bf5721e6bb668aabe6dc6375349f7a284e28df0609d00982e7e456908bd6868722391afa7f44a5c82faedc8cf544f69a0e4fb9fb0d529b3ae3d44d78 -LibUV.v2.0.1+18.x86_64-linux-musl.tar.gz/md5/271d4d40a1ae53ed5b2376e5936cfcf9 -LibUV.v2.0.1+18.x86_64-linux-musl.tar.gz/sha512/1956f059ed01f66b72349d6561b04e6a89b7257c0f838d7fbdd2cee79bd126bb46b93bf944a042b5a6a235762a7a0cdd117207342dd55a0c58653a70b4a38d48 -LibUV.v2.0.1+18.x86_64-unknown-freebsd.tar.gz/md5/62fe8523948914fbe7e28bf0b8d73594 -LibUV.v2.0.1+18.x86_64-unknown-freebsd.tar.gz/sha512/e6486888028c96975f74bc9313cba9706f6bf2be085aa776c44cbb2886753b2eee62469a0be92eb0542df1d0f51db3b34c7ba5e46842e16c6ff1d20e11b75322 -LibUV.v2.0.1+18.x86_64-w64-mingw32.tar.gz/md5/ae103f24b6e1830cdbe02143826fe551 -LibUV.v2.0.1+18.x86_64-w64-mingw32.tar.gz/sha512/f814085c135815947f342ff24fa0e1015e283ccece84a5b8dd5ccec0f5928a129e5fd79100a33b131376ad696f70b5acadcc5a02a7e6544635ecf7e18003ba1c +LibUV.v2.0.1+19.aarch64-apple-darwin.tar.gz/md5/f176c76e5e2096dea8443302cf9550b8 +LibUV.v2.0.1+19.aarch64-apple-darwin.tar.gz/sha512/4301b13953a08a758b86e30af3261fd9a291ce3829b4d98e71e2a2c040e322e284c5a6eb4bc7189cc352f4b1cf7041e2cfd3380d511d88c151df3101ad74594e +LibUV.v2.0.1+19.aarch64-linux-gnu.tar.gz/md5/c81515783363702a1bd4b65fd6d7f36b +LibUV.v2.0.1+19.aarch64-linux-gnu.tar.gz/sha512/011429365337f5a45e56ca7a42709866bb994c747a1170d870f5f3ddfff2d36138866ee9278ac01172bc71bde8dee404bcb9cae9c7b44222bf1cc883659df269 +LibUV.v2.0.1+19.aarch64-linux-musl.tar.gz/md5/e74d5ea4912dd326b2705638faa7b805 +LibUV.v2.0.1+19.aarch64-linux-musl.tar.gz/sha512/a26a9f2c9051816230324071c502321f7af3885d581a400615858a93a4cd457226048d15b0e7f6a73d12659763c705b5ab519e9f5b35c6d886b9fd5babbfe352 +LibUV.v2.0.1+19.aarch64-unknown-freebsd.tar.gz/md5/f2fe50ada3b6935af4f6b28fbc3940b2 +LibUV.v2.0.1+19.aarch64-unknown-freebsd.tar.gz/sha512/c4ba0190d21c6edb561062b2615792e9b4c2474dfc200d9dba12a3add44e1fbc0b74989748d85576f0a6e42d8e0bc02f6cb13b5963f3a56b00edffe6348a9f26 +LibUV.v2.0.1+19.armv6l-linux-gnueabihf.tar.gz/md5/6df38bcf5d0a61dee63d16b73d0c9a24 +LibUV.v2.0.1+19.armv6l-linux-gnueabihf.tar.gz/sha512/d5354a6532061de0a58965ce0e427bde52f9ae0ee39a98e1a33de4c414fddcba9ba139ddf91be7321a4ccc97bbf7a8a8357ff10cf60f83c0a6bff7d839d6d7a8 +LibUV.v2.0.1+19.armv6l-linux-musleabihf.tar.gz/md5/6f02a24cfbfae3032fadceaea1faed39 +LibUV.v2.0.1+19.armv6l-linux-musleabihf.tar.gz/sha512/7fd107eb9a5ea84b488ea02e4fbedc9fe13bb11be859986a47af38f40ad775dd9f738c790878a3503437bcac1eb26ad9fe26f4aa0d3cb45c980b4c5abc9aec99 +LibUV.v2.0.1+19.armv7l-linux-gnueabihf.tar.gz/md5/96b09dec72f7e9b7409fa2920e67c866 +LibUV.v2.0.1+19.armv7l-linux-gnueabihf.tar.gz/sha512/6a0f79fc15c944fabba5c65180b665bc9769c6ff25863e330049f48b3a4394b448492f5a9a76bb7f8dbd3ce44dfc6f9ccdc2c71c42e1c749e88070fe99b1db69 +LibUV.v2.0.1+19.armv7l-linux-musleabihf.tar.gz/md5/f44e4b2521a813181f943895bdb0dd3c +LibUV.v2.0.1+19.armv7l-linux-musleabihf.tar.gz/sha512/cda1413dca817f772e8b343db0c6de0ef6b8f269e9a6a2ef3403c2582aeab554f46281bbb1eb4659c259198ef47fe26aab648a281e66f80aaf2f2cda0a23ac05 +LibUV.v2.0.1+19.i686-linux-gnu.tar.gz/md5/1f231d89cf9c04515d2d107a5d786cc8 +LibUV.v2.0.1+19.i686-linux-gnu.tar.gz/sha512/089cb8a372cdee0cbc0e78fc201611bb9bafd99af9a78e09d6097a6b70e7c4aa001ebd86f944b0a885c072093c529bf86bcaa32bde4fc1934407a858c1a5a764 +LibUV.v2.0.1+19.i686-linux-musl.tar.gz/md5/01cfc2a9e2536dbd330267917abb19ce +LibUV.v2.0.1+19.i686-linux-musl.tar.gz/sha512/72f3588cb464a60e61f8998242aaa2abdf93df920a2feba5e1d66ef0f2498488df0ec415cbb499d7f75c47bdfc7e3a2fdda6a94383492e0ad13e464eb1314ff8 +LibUV.v2.0.1+19.i686-w64-mingw32.tar.gz/md5/8c6599aab9ed4c46e52f03683aac664e +LibUV.v2.0.1+19.i686-w64-mingw32.tar.gz/sha512/13f0565f7244a8bcf1ab43fac91a856dc86d214877033a3cefee8c2179c1a275dfd7dda32e9017763acac2ba42ab6799934a58f5feaa38fb6cf2253dd713f57a +LibUV.v2.0.1+19.powerpc64le-linux-gnu.tar.gz/md5/af0e43d9d0aa91dd82b63220d96991ef +LibUV.v2.0.1+19.powerpc64le-linux-gnu.tar.gz/sha512/9fabe3089e4fc60e910770c32d36300ce8ef36c28e8cc9c72fbecba6eb80285ee8174e84e4452fb4ce90ee7c7f94e99b03fce47d8c579bd614bfffd553f93666 +LibUV.v2.0.1+19.x86_64-apple-darwin.tar.gz/md5/871040e874eedae54553d8f1c91b9133 +LibUV.v2.0.1+19.x86_64-apple-darwin.tar.gz/sha512/d5eee08b65e4bb8b444c61ac277bec9ef944b9279dd7f0732b3cd91d47788c77938e5db71e019e01bbe7785a75df95faf14368764f700c6b7a6b9e4d96d6b4c2 +LibUV.v2.0.1+19.x86_64-linux-gnu.tar.gz/md5/d2d186952c6d017fe33f6a6bea63a3ea +LibUV.v2.0.1+19.x86_64-linux-gnu.tar.gz/sha512/15501534bf5721e6bb668aabe6dc6375349f7a284e28df0609d00982e7e456908bd6868722391afa7f44a5c82faedc8cf544f69a0e4fb9fb0d529b3ae3d44d78 +LibUV.v2.0.1+19.x86_64-linux-musl.tar.gz/md5/271d4d40a1ae53ed5b2376e5936cfcf9 +LibUV.v2.0.1+19.x86_64-linux-musl.tar.gz/sha512/1956f059ed01f66b72349d6561b04e6a89b7257c0f838d7fbdd2cee79bd126bb46b93bf944a042b5a6a235762a7a0cdd117207342dd55a0c58653a70b4a38d48 +LibUV.v2.0.1+19.x86_64-unknown-freebsd.tar.gz/md5/62fe8523948914fbe7e28bf0b8d73594 +LibUV.v2.0.1+19.x86_64-unknown-freebsd.tar.gz/sha512/e6486888028c96975f74bc9313cba9706f6bf2be085aa776c44cbb2886753b2eee62469a0be92eb0542df1d0f51db3b34c7ba5e46842e16c6ff1d20e11b75322 +LibUV.v2.0.1+19.x86_64-w64-mingw32.tar.gz/md5/ae103f24b6e1830cdbe02143826fe551 +LibUV.v2.0.1+19.x86_64-w64-mingw32.tar.gz/sha512/f814085c135815947f342ff24fa0e1015e283ccece84a5b8dd5ccec0f5928a129e5fd79100a33b131376ad696f70b5acadcc5a02a7e6544635ecf7e18003ba1c libuv-af4172ec713ee986ba1a989b9e33993a07c60c9e.tar.gz/md5/c1a7d3c74ef3999052f3bfe426264353 libuv-af4172ec713ee986ba1a989b9e33993a07c60c9e.tar.gz/sha512/a3f16863b711ddeeb5ab8d135d7df7a4be19cc2b9821fc78c8cd3ba421231d39b7d8bd9965321455094fda01584842a58f60612d93082b4fe32210b8aa44d999 diff --git a/deps/checksums/lld b/deps/checksums/lld index cdcae063f68ff..fff3140025e8d 100644 --- a/deps/checksums/lld +++ b/deps/checksums/lld @@ -1,108 +1,112 @@ -LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/64c9a9f1758b9b292e0a3ef37f16ea41 -LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/cc740aaeb6ed29c56b2881e1488606338e4bd0e049ca4a5b8312b1d9129b778224570336698347e4562d632db9049e0e91ecce34ef68acb23a8bbf62455a81cc -LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/1a8e11dba5cb574cde42de2b9703ff79 -LLD.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/290300229576bb9155fe6bd24c0ee21beb41d0f2a46b208ab5a657b0199a7376c1f4cb07204c8ee1e6d202efe30ca040a6fff63c69b174120de3eb9866e344f4 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/cea134f347bae257cf5f55b6388cef81 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/16b59143e929791b0c3e56cfb4970d8b3c87adf6e847fa9e2aac17c4ff2aa311ba2c7511c1b0ae2f39d9aa92f87ad4d99c042fe35bec391ac865fedb72bd3b1e -LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/5f903bab0e38fa608e8965acce6f020e -LLD.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/01e5f6a32958e04174c545f57c6c3b1bc88ccfd5ab18dcb9d67b92b55ebc7655a03bf963c4eaf7e5c3792d4691427a89db372e7534c6c8f965f8a715a32d9284 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/241a55374fd067f3736a2bb929e47015 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f1fedea4e6b5f6f3bbf4d705034d6c51b06f011c2ecec1ae49c5b7bd123891eee8b991462d60be7fffd58f7c773afe910a06ec0b55b37eed9b4d09b9fdbd5068 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/ff018c7448a7589935333e46739ee2c4 -LLD.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b646c6a945b8f42b396164a8e87fc2e54b1ad05681f438dfba83fdd3712a60167aaffcb0300bc42d904eb4bd34c002a71642b59540ca01e64d6fecc6daaacdd8 -LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/e6ee9423a82322b9233cafb1c92eed2d -LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/c915582a9ce2dfa8721741fb1ed19b719ba40f0092c2d29ebd68829ee558cef0b044a5e40985cff88e89129cbeed052d85fa5c6b6d87f9b3a68a6e89079ab4f3 -LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/cc55112e2db358cf26d7bae3211d8e4f -LLD.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/0ecb43045419020eea911f1767dae23a6b1e81bb155ec493e911a9412e45f7ec71461aea2e6fe346e641747139cae43d9435ccecaa7fd6a234e4d69bb06606ed -LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/498b2909f80b20588135466d5211bc80 -LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/120fff24e85cf970670b20b5f4509475a3ae0d7621f8f67d018f3a7625548d736a3abc89f015966b1329c6b0a08a1db832e035ee3bae384e2c5864b73a856600 -LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/1bcd298d5292f8e51f19b97fa4b27ab0 -LLD.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/695c42557f9ee53b2e10bbf74653fbad4d02124b962a1f50cf719d2821607dfbb9c1bf638dbbc9e0e544f3020a9ef4a82decd13f886cc41ddf47c07a5e40eaa1 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/2323ff933feaf3754b442bee48a63607 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/47b8e490b89e04fb8886dae438e3ddcd53c4e98045de2b0def3988671827528c8e9ae296411464c0f17cc64bd3956644673f47a3817237f27e1c3ed16ac8ef01 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/37cf8528666064a434296f2e0039e9c6 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/ea1504a859509f8a16030db7a65f42f0e78d67adf5946497f2178bf25456c0f2583af72c636881a4bdd1210dc0d377bdf300ef55aef5db8c56995424a1886059 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/1c341f2b161e2320d3d1a74685887f54 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/4f6fc099293deb1a2cf729ea7edd6e17fea0dc8b9fae9acfe34e00b1f5c798933df9538c805c8d28c6279eb38f9ebae2a1aeb1a2f23087352c6eeb3b27b63ddc -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/e306d59c71b0958c77108e650fac2612 -LLD.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/79fd7cec0e169a9555ec9b0acc3248991e2e37a1d5bb422808ffcfd4f47e79321560b7985c82dfe070fb0b5ded5c160d83e358399c6e7608eeb62cd4a1406f88 -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c1d080f1aebb58778d730578fb113290 -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1f420da1897bd0a61413321aaaf032e8ed38d59e6dfe3313ca3a6ee6582ae6c566e3761ca8fcd1f5a964337ba8a9b3e73dc55ad68aca139beeb45e43d51e862b -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/6f4e0c7d2fe9ac254650dcd2842dafa8 -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/bbc71b334250e5e6429766d88595adbb671a206630987ec2a27e05711ff0f844487dffc1c136052ec11394e9d5c51c70d1b75d5348f97d3bf7fab463291e9dc8 -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/76925b9a7bc249b2227390c479c54f8d -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/20643ecb79732e3ae9666116dbd0763c18b808afa78e6a14998aadc7265cccd6efd28670592db61d3d27b8d3023be4c5f3df41fff9e1b38d61abf76829090b4f -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/399b9aac140d9050088fdb187ed4645f -LLD.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/8bab65965670fe392e78d0b9dc78c92cdcf202898f6d5a3174eb89ca5cb95b995675c8a7d81bbc4e95e490ad1a43d9d29d7907b7006789c0143a1d8f24cccaeb -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/026a4f5ae9eb3ac05e5e8fa894d77a5b -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4bca8bd558619260cddf4e2f4593cbb2a0691b5ccc6d1dea6dfcc5a2b5f51d7d1a76c35e481244e211e2eacf32bd628df5ad0e6c75e5185bb1d9b569f6acbfd3 -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f898ceabcba052b7e6713a2b2c208a92 -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/92be1910f795390be5f15ba5b2c220a3209a5f7ac04fca3f5229486628bcf5d2f20cf6e4dda8b41d6beaaff42a68a9ddb95fdacc6eae33b9183b581e9a194895 -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/e366058cf69a4367945bdba9523f2a0b -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/45a786e8d0162bd5bd01c029691d2928d3744ef4a7a1efc2e39755dee2f9a9ae23ee725f0454ca601cb9c082a342209e9129df851314b5757c74767b13508fc4 -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/665a8502170729c86ea95a7ea2fcce0f -LLD.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/c1a2a85c9ce14af8c91bc9a599393c52c0b8a585057366b1ceeed34c5db44641ecd0c9b377bee80cb4951fc7102fbb4f21fd050126bfa5bb4e582ffefee17035 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b90b2130262f63f5189cc8e4a65e4433 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c1cbfd38c82d676c3fdbec486691334cf7bf4115d9ef2665012b71725c28545a49f34edf5689ea0352822c811c24c89cc152d1fccd1586b17ae8e6b2503641df -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/2d5360da4b2c9ffcea5d0a646a7c114b -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/73323e0937fe4423883480294c8df44744acde4f47380e35535cbe69c855c0e35e86a1eced3085ae0285f284f47af5ef237f4650bf2b6a8b9d5308efce88fa02 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/a9b9a65938a7701aaac6fa706481c867 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/fe8243aa131ad8be54f0fca5754c2e68ec39049004ec8feed499731c5228a7a46e303ba866b9f9a55e5318c73d8a46d964673e111f6c60e5ae1628c568d7d894 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/0d9592a287c9231ae2db65000be2cea2 -LLD.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/4ee192dd33f518d2735a829ac8f822b5672b39e8c2254987aea6e5f2f0056213bd85d84c4050d52ba9ac8c35762521c324fe2d6e18db0396e7142af9cb61a561 -LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/d487598dec9969485dcf785fc0968bd4 -LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d3117739919696b9b0c9ae398f1b1e9db8bd3e2e27839f62b3551c22ae2517f8abb69e57e23d125002bb466889b7352e69c1e9dfd9abf1c5433f274e928b341 -LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/943434b08dffb54e8cf04ae7bee34923 -LLD.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/77b7bbc5d988cf36ecd10609e091cf24dea134cd32c7ee96dec7bfe1a4942553b6205653edc16c8454261f621966daeb267f42562172bab4cec9693ad733d867 -LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cb9e371947ad415de048636ed78ca48f -LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/c00b696fa146e8c29b37f15f78ab3325db9b3f5b3514e615f145b4eb7c9c8788662cfb6255b7dead596cad8c576b378f7459c2c85d462b597ba5e21adbac0536 -LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/485f061ee8425f042e4dd3042388bf8a -LLD.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/845a47a36c61b305bb70b1249f6fb7c4e8f740acff90d3e850ab2e887f7d959ae263431a02305bf7587e4194463f9932769d500a19709bc479eb6e6168325421 -LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/f234526410e779188f3d22da438a4926 -LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/12e2c9fc5385ff142bf82956268230fb01a6f1a1fdb3a6c1e13afd341dd2eea970b707168d5f45960dc9ebbf6d6598af0ceba371172f624ae823ea1eef4e9031 -LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/e68cab4aec1abcfce12a13e3d1f67dac -LLD.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/67755b34ebe04f4d28be3be2a37df46b5e900f38dc4908875f66671fbb740cf033f2fd9af5116635f55025f330f7b1a478cd4900db9d00e4699b591a16269100 -LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/4a71aef80b75b2ea1a5b7f8521afcf5f -LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9deb3e9615ae15dba8c744b22416243304d30f100c8d17538fcedbc18787147505f74ecc2f933fc54101527847503142cfe84a46a95ca3c57987996e3b8583f1 -LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/9b28ee75d05cbaabff57fd45cc0d1cf7 -LLD.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/bfd3d6cfd4a5a2fbfe940f64d47a86a598360e90619f8175a2d1306f0894610f13fc44ba099ad59d2989beabf491df08a5611bcef3fd61b6391ea0230b11a432 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7962fc6f08531f0dcfa44bd667f31582 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/2c936064685f12ed6764c187192023118e97dcbff6ca1656f0304a40772b4ecf55ee0296b3c2a00760f5bb437162e2b737635fdd59b889d35756d697fc7e6b72 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3eb4d78af670d446f696449a5e71e3ba -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/315dc76799f3e443fdb5ebbecf96a08070f8251930a26995de892b8e67bd35bbb365f2cc5fd93bc7cbcbc9edd08280ee8d2a36b28a704866dd3fdddae4969455 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/e73cadd0354897bd5bb611cc1c027858 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6f444a4ea22e7108ab75686ce9cd78c0db0a677e39e8434896fb1ec90b9dc013abf7de1024d329a9726dabf229a8a68c27a11f211092e676715d282efb7b8504 -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/aeb310f106f31126dbe53459e36d33bd -LLD.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/cd18c115415dd92bc7fbb5c29cacc5848b1f3851c3a526ff9c0813ad46824df0a4f13a66b1e6641ed11b44b5b937390619f01666fe6d5a047f1772f0ad03c941 -LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/300dc28f7af6aaa69cec9a214a57fdb8 -LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/dcb40c5934118c204968cb963a3fae91179eb1e31f5397975ca98c8a7aaecaf2a7f81847bc426fd306bb76970794502ed4f94d8f461b96ea90362130f44520e7 -LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/e1f23fef82fbfcbc28899677f12658b3 -LLD.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/b6b585060832d53827376ac6c00cc8bd5dfbf091c38c87020f6de42adc86dbe4fc33ec2c17f4433176c79a509681d694ed1502b179efcee2c4dd4c10a26e87a2 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/5dc96eef71dc28611bc998ef966371c6 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/781993c75bb07db96d02b5a7e779116864730a9bb941b64420a435956a7ecd501b5b2673f1854c09ece5f0c73559d5723c271d6352be57ddae6801a695629362 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/8a1fe0ccf7699ab7a7a514b620112a70 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/d002083045d3eb7c749f2e97527c1228cd317a8138ff254228e43594a6cabee47fa363785466ebc2874cc438457640ff08a836eec7334afac451506ea7bbed03 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/331be92bd3d76bb8e86991b7832ad41a -LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7b1c6df53311a17a92a41cb67ec476f947949c4ca5d15a643badaf9f01e76a186abbb6e156f95ad1605d83250df4e081164986a6b7fcb3238076b0ec5a3bb565 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/97c7f5267ad6927f699a25ce44f55a70 -LLD.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7b847c6026fd7daeb17a4459b852562ce6664b2f406664be672bcc384dd5a79b9505561fc61dd8fb78a903a2ed4978f322cccad19f5a3966bac856e877c11ef7 -LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/c86da6a396fcdddbd26cfd71c0f70458 -LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d5b75b43167080b8ea456e516c9ace02ee6066ce715a56f0b42cb8045b965b1cf8d4ebc0786c23be4544693ff858816a6257b0638ec11e077df32ead62f7efb -LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/d72e175272ed893688d18e868120c575 -LLD.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/9a46eeca8c7a8be65ed487a74227534e08a257e404814c44730f12a5bebc8cd160998cfd5ed30189aa606ddbe602e1b1788e465e4a210103c6726a7fd230abc3 -LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0206fdaa9582ae3bddaed1b6fd7a8cb5 -LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/584a67f603f656ca5d27aa0ef2e425ad385612aff06cdc1d534b5944939a09246c93954fc153b8a89acff721e657a8903af9a640abc252d4e452f348781bca98 -LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/0dd14af342467eac2e13cad4acbc881f -LLD.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/918f2c66898f828414009fa6ee273da5bd654e4b787ebb4d703f0be27e388b46870d68bd58c4f45638d276c61c1bfe2f3c67fbf34dfb5578158d072f82d927de -LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/b373e1bf2a24f34496754438e563a5e9 -LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/a739f29e332be74cbcc544903d08bbcc12c3e9f5c3d02d130ef1c69426ead2c74b14f98ac79e88ba29fb2e2dc3b28b7d81c9d42f2e27e0ce9442f6a0e81bb8f0 -LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/1fdbf6aa0751788611054f7e98024104 -LLD.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/2015b8e84642b2434d1089908354b47b080d5683f1c7eb2c09de09abb3674e7119ce4ecfa858bf8129fd9e9075bb45f2e53a997421f2457aa9b5c4a9d7edfec8 -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/85bd5a9e312e83a09fa5b7fd6abfba76 -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/0a5cba5c65abc72361a780f64e64c463797aefe52994699d8d785437b773530e49a5fc2746e36bc5a31aabf4eb4343870aa448f8fa2b119ede3e1c4ea228cc9d -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/ab07ed76a796d86cb6ac2ae4fc563eab -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/485117c7e1daca401c1cfe77324e8f5961f6f33ed2a3c907f4c4a2bf9c45c14d929959cf8e4d9cca9ad112a3ce6a851e336cd793bd5ee284c87b9fe487700ecb -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/852449a26af61d8554fb1b4c22c4384a -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/a080d2da5ff4b832822e099f150f0c15b985d54678a9508711f7f435d6ceec68eba12b5f8c25db0b4841dc5c5edb003b74b4fef391292b9407d7bda73d35c4f5 -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/eb999bcb67f789b6443dbfe907bc61e4 -LLD.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/811f72ce250184ccdfa30aa992884f1bdd0a791fa125f089037bf1af45b844d76807c5662a904ec9312b79efc565fd0957f195a70a39248eed99ff53f3284cba +LLD.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/229323a0b31c29b4221d79ace1a76820 +LLD.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/c00fb8bf309f0cc6c8cb4465cc0062a8b1a848d9460c53241be654d88c598847b4590b4afa4b71c4859cfc67490942eddd79ae9ac4d75a9b0e392fbf67389a92 +LLD.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/ce7804a6a846d0d951aae34607c43bdc +LLD.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/164adec7649a36b2967872884866de1c57f6f54e1c24f955593f9f6a10cd89c69493a64a37bf9f001ce3576baed867423d138dfb1df0139b4c1312e81001b167 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/cea134f347bae257cf5f55b6388cef81 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/16b59143e929791b0c3e56cfb4970d8b3c87adf6e847fa9e2aac17c4ff2aa311ba2c7511c1b0ae2f39d9aa92f87ad4d99c042fe35bec391ac865fedb72bd3b1e +LLD.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/5f903bab0e38fa608e8965acce6f020e +LLD.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/01e5f6a32958e04174c545f57c6c3b1bc88ccfd5ab18dcb9d67b92b55ebc7655a03bf963c4eaf7e5c3792d4691427a89db372e7534c6c8f965f8a715a32d9284 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/241a55374fd067f3736a2bb929e47015 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/f1fedea4e6b5f6f3bbf4d705034d6c51b06f011c2ecec1ae49c5b7bd123891eee8b991462d60be7fffd58f7c773afe910a06ec0b55b37eed9b4d09b9fdbd5068 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/ff018c7448a7589935333e46739ee2c4 +LLD.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b646c6a945b8f42b396164a8e87fc2e54b1ad05681f438dfba83fdd3712a60167aaffcb0300bc42d904eb4bd34c002a71642b59540ca01e64d6fecc6daaacdd8 +LLD.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/e6ee9423a82322b9233cafb1c92eed2d +LLD.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/c915582a9ce2dfa8721741fb1ed19b719ba40f0092c2d29ebd68829ee558cef0b044a5e40985cff88e89129cbeed052d85fa5c6b6d87f9b3a68a6e89079ab4f3 +LLD.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/cc55112e2db358cf26d7bae3211d8e4f +LLD.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/0ecb43045419020eea911f1767dae23a6b1e81bb155ec493e911a9412e45f7ec71461aea2e6fe346e641747139cae43d9435ccecaa7fd6a234e4d69bb06606ed +LLD.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/498b2909f80b20588135466d5211bc80 +LLD.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/120fff24e85cf970670b20b5f4509475a3ae0d7621f8f67d018f3a7625548d736a3abc89f015966b1329c6b0a08a1db832e035ee3bae384e2c5864b73a856600 +LLD.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/1bcd298d5292f8e51f19b97fa4b27ab0 +LLD.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/695c42557f9ee53b2e10bbf74653fbad4d02124b962a1f50cf719d2821607dfbb9c1bf638dbbc9e0e544f3020a9ef4a82decd13f886cc41ddf47c07a5e40eaa1 +LLD.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/f0e0668d29253cd834418c88ad63df31 +LLD.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/f910fd8ca972b1cbe0704d4d73273e2d6911d31ae5fe842250802cd33453e4fa2ed03ae4b4df43ea4df13711cf2409c16b1c44832b44cb05f7681488c4402681 +LLD.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/md5/84f79f1ce1fcd57ec4bd499a684da120 +LLD.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/sha512/d0e4a7ecff0e3f499dc22a9409ab8bff9099d4fdf191916426be917695c7fd55043b41cb0fa21541c3d6a6c202736b5c7b8fce53244e3ac713560a47a0ed6588 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/2323ff933feaf3754b442bee48a63607 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/47b8e490b89e04fb8886dae438e3ddcd53c4e98045de2b0def3988671827528c8e9ae296411464c0f17cc64bd3956644673f47a3817237f27e1c3ed16ac8ef01 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/37cf8528666064a434296f2e0039e9c6 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/ea1504a859509f8a16030db7a65f42f0e78d67adf5946497f2178bf25456c0f2583af72c636881a4bdd1210dc0d377bdf300ef55aef5db8c56995424a1886059 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/1c341f2b161e2320d3d1a74685887f54 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/4f6fc099293deb1a2cf729ea7edd6e17fea0dc8b9fae9acfe34e00b1f5c798933df9538c805c8d28c6279eb38f9ebae2a1aeb1a2f23087352c6eeb3b27b63ddc +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/e306d59c71b0958c77108e650fac2612 +LLD.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/79fd7cec0e169a9555ec9b0acc3248991e2e37a1d5bb422808ffcfd4f47e79321560b7985c82dfe070fb0b5ded5c160d83e358399c6e7608eeb62cd4a1406f88 +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c1d080f1aebb58778d730578fb113290 +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1f420da1897bd0a61413321aaaf032e8ed38d59e6dfe3313ca3a6ee6582ae6c566e3761ca8fcd1f5a964337ba8a9b3e73dc55ad68aca139beeb45e43d51e862b +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/6f4e0c7d2fe9ac254650dcd2842dafa8 +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/bbc71b334250e5e6429766d88595adbb671a206630987ec2a27e05711ff0f844487dffc1c136052ec11394e9d5c51c70d1b75d5348f97d3bf7fab463291e9dc8 +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/76925b9a7bc249b2227390c479c54f8d +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/20643ecb79732e3ae9666116dbd0763c18b808afa78e6a14998aadc7265cccd6efd28670592db61d3d27b8d3023be4c5f3df41fff9e1b38d61abf76829090b4f +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/399b9aac140d9050088fdb187ed4645f +LLD.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/8bab65965670fe392e78d0b9dc78c92cdcf202898f6d5a3174eb89ca5cb95b995675c8a7d81bbc4e95e490ad1a43d9d29d7907b7006789c0143a1d8f24cccaeb +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/026a4f5ae9eb3ac05e5e8fa894d77a5b +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/4bca8bd558619260cddf4e2f4593cbb2a0691b5ccc6d1dea6dfcc5a2b5f51d7d1a76c35e481244e211e2eacf32bd628df5ad0e6c75e5185bb1d9b569f6acbfd3 +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f898ceabcba052b7e6713a2b2c208a92 +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/92be1910f795390be5f15ba5b2c220a3209a5f7ac04fca3f5229486628bcf5d2f20cf6e4dda8b41d6beaaff42a68a9ddb95fdacc6eae33b9183b581e9a194895 +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/e366058cf69a4367945bdba9523f2a0b +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/45a786e8d0162bd5bd01c029691d2928d3744ef4a7a1efc2e39755dee2f9a9ae23ee725f0454ca601cb9c082a342209e9129df851314b5757c74767b13508fc4 +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/665a8502170729c86ea95a7ea2fcce0f +LLD.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/c1a2a85c9ce14af8c91bc9a599393c52c0b8a585057366b1ceeed34c5db44641ecd0c9b377bee80cb4951fc7102fbb4f21fd050126bfa5bb4e582ffefee17035 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b90b2130262f63f5189cc8e4a65e4433 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c1cbfd38c82d676c3fdbec486691334cf7bf4115d9ef2665012b71725c28545a49f34edf5689ea0352822c811c24c89cc152d1fccd1586b17ae8e6b2503641df +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/2d5360da4b2c9ffcea5d0a646a7c114b +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/73323e0937fe4423883480294c8df44744acde4f47380e35535cbe69c855c0e35e86a1eced3085ae0285f284f47af5ef237f4650bf2b6a8b9d5308efce88fa02 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/a9b9a65938a7701aaac6fa706481c867 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/fe8243aa131ad8be54f0fca5754c2e68ec39049004ec8feed499731c5228a7a46e303ba866b9f9a55e5318c73d8a46d964673e111f6c60e5ae1628c568d7d894 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/0d9592a287c9231ae2db65000be2cea2 +LLD.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/4ee192dd33f518d2735a829ac8f822b5672b39e8c2254987aea6e5f2f0056213bd85d84c4050d52ba9ac8c35762521c324fe2d6e18db0396e7142af9cb61a561 +LLD.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/d487598dec9969485dcf785fc0968bd4 +LLD.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d3117739919696b9b0c9ae398f1b1e9db8bd3e2e27839f62b3551c22ae2517f8abb69e57e23d125002bb466889b7352e69c1e9dfd9abf1c5433f274e928b341 +LLD.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/943434b08dffb54e8cf04ae7bee34923 +LLD.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/77b7bbc5d988cf36ecd10609e091cf24dea134cd32c7ee96dec7bfe1a4942553b6205653edc16c8454261f621966daeb267f42562172bab4cec9693ad733d867 +LLD.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cb9e371947ad415de048636ed78ca48f +LLD.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/c00b696fa146e8c29b37f15f78ab3325db9b3f5b3514e615f145b4eb7c9c8788662cfb6255b7dead596cad8c576b378f7459c2c85d462b597ba5e21adbac0536 +LLD.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/485f061ee8425f042e4dd3042388bf8a +LLD.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/845a47a36c61b305bb70b1249f6fb7c4e8f740acff90d3e850ab2e887f7d959ae263431a02305bf7587e4194463f9932769d500a19709bc479eb6e6168325421 +LLD.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/e4f97e8334e1f29ad9083d051a50eab9 +LLD.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/13ff037881da8a2333129bb702f515a0eb1afb3e4f27298c035c133ce5c512fa643b2a90df38d6f61b1dd5e86e32998b9061241358b61be794caba2b989efb70 +LLD.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/570f50ef6523cb8133b160af8fa2057e +LLD.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/69ec402469b2b2c85aabca1c8b36edd0c53b7e678e4c56fd96062b62a57b7ff1008f328d71e6aee36d4270a41a7bf84f62f934007398618b5426202d9614305d +LLD.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/0503dc3e4e69ca6fd7e2a5dac9c4ef3a +LLD.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9b6c851341c2642d5ed9169326b4de9eda50ea06b1270a721d2e85bce8ffe4c595cd491e0a218c3a418aed526f881737fbb44cb417cd5ba7db972bcbaa6ad0d1 +LLD.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/08b22e98c514d48ddb1039b44f64f480 +LLD.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/5e5b7c66d5fec3ff1a9cb7989d62887699cc3e70ab36a94e6f157cb0b9adbe8d63f5f1a74cfb6765cf46851087019b12ccf09ea848ed6456d17cdc796a5bf2e8 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7962fc6f08531f0dcfa44bd667f31582 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/2c936064685f12ed6764c187192023118e97dcbff6ca1656f0304a40772b4ecf55ee0296b3c2a00760f5bb437162e2b737635fdd59b889d35756d697fc7e6b72 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3eb4d78af670d446f696449a5e71e3ba +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/315dc76799f3e443fdb5ebbecf96a08070f8251930a26995de892b8e67bd35bbb365f2cc5fd93bc7cbcbc9edd08280ee8d2a36b28a704866dd3fdddae4969455 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/e73cadd0354897bd5bb611cc1c027858 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6f444a4ea22e7108ab75686ce9cd78c0db0a677e39e8434896fb1ec90b9dc013abf7de1024d329a9726dabf229a8a68c27a11f211092e676715d282efb7b8504 +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/aeb310f106f31126dbe53459e36d33bd +LLD.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/cd18c115415dd92bc7fbb5c29cacc5848b1f3851c3a526ff9c0813ad46824df0a4f13a66b1e6641ed11b44b5b937390619f01666fe6d5a047f1772f0ad03c941 +LLD.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/9493a58ed62367b45a055c8880de0924 +LLD.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/5a448c87ad627235d7d2c8f8f8866af0f6872c3f7775123edb09b23b772f165fa020fe0c592ad100f8c777213fe1346b642a556df66ed003771eb0e76345215a +LLD.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/d397b37abf0026ca69fa6657dd791e27 +LLD.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/9e9fc915389bfa09cbe8b977f22a3466ccda052f415b3b5fdfc97a15e089d4f887fba97d6bfe6e17104f09bebe48c859bad25e9f2cabc179000247292eafca1b +LLD.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/5dc96eef71dc28611bc998ef966371c6 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/781993c75bb07db96d02b5a7e779116864730a9bb941b64420a435956a7ecd501b5b2673f1854c09ece5f0c73559d5723c271d6352be57ddae6801a695629362 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/8a1fe0ccf7699ab7a7a514b620112a70 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/d002083045d3eb7c749f2e97527c1228cd317a8138ff254228e43594a6cabee47fa363785466ebc2874cc438457640ff08a836eec7334afac451506ea7bbed03 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/331be92bd3d76bb8e86991b7832ad41a +LLD.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7b1c6df53311a17a92a41cb67ec476f947949c4ca5d15a643badaf9f01e76a186abbb6e156f95ad1605d83250df4e081164986a6b7fcb3238076b0ec5a3bb565 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/97c7f5267ad6927f699a25ce44f55a70 +LLD.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/7b847c6026fd7daeb17a4459b852562ce6664b2f406664be672bcc384dd5a79b9505561fc61dd8fb78a903a2ed4978f322cccad19f5a3966bac856e877c11ef7 +LLD.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/c86da6a396fcdddbd26cfd71c0f70458 +LLD.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8d5b75b43167080b8ea456e516c9ace02ee6066ce715a56f0b42cb8045b965b1cf8d4ebc0786c23be4544693ff858816a6257b0638ec11e077df32ead62f7efb +LLD.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/d72e175272ed893688d18e868120c575 +LLD.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/9a46eeca8c7a8be65ed487a74227534e08a257e404814c44730f12a5bebc8cd160998cfd5ed30189aa606ddbe602e1b1788e465e4a210103c6726a7fd230abc3 +LLD.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0206fdaa9582ae3bddaed1b6fd7a8cb5 +LLD.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/584a67f603f656ca5d27aa0ef2e425ad385612aff06cdc1d534b5944939a09246c93954fc153b8a89acff721e657a8903af9a640abc252d4e452f348781bca98 +LLD.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/0dd14af342467eac2e13cad4acbc881f +LLD.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/918f2c66898f828414009fa6ee273da5bd654e4b787ebb4d703f0be27e388b46870d68bd58c4f45638d276c61c1bfe2f3c67fbf34dfb5578158d072f82d927de +LLD.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/d1862068a670d4c04887513b914e11a8 +LLD.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/c5a91657667394e468e71d9c07df0c71918d63d094d2598875f75cf3830d8502e70f59fba59b07a2d1e0551f58d0487521c856e68e4122fd6a6f7ebd1c7c0f58 +LLD.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/8dc0ec01029765dbfdd28d63bea8cfca +LLD.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/234e9db1177003a074c6ca7236c589424b4617d1a359f5f9e2ba6095a7f317d62ac731319b4b4513c523e80c15b82c99ff0fc9df5f76fad452955492e9935b1d +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/7beb510d766ac1e16017aa6924e88659 +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/bd18b733a6b2fbbeef7f8af2f13dade0330a525c83b4faed5a5d2507007be2f2f7be70f99d05524fa94ae1dca524be64adbb9dc87485477f62109f44cbae95fe +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/9ecca76cea81cd1d0fd3470778145371 +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/d1548402dfcb4aa0cf3c9e445a9810e5d8bc2411de9943b57e892ec82af29e214f6d93c58af9cd0de9b4fa5a0438e4c1fe0b9591a9582143d470e7a42e685f4a +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/b1de7acc21fe51c1486854cd46b71bae +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9f8457c12801584340b3fbf846920299756359016d151018562f8c14e0a03f657fdb6eb1d7418fdfbf586c59e670d866384e822de9bde15b2dbd031ce5e6af8d +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/373a7007eb8b526811604fb0161f73af +LLD.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/b586815621f698c7d6ff995c93e11ea1ec55e7e7c0e34ad874f64b942ecd73685cce150d51804bdd371ec42671e7814e364944276ec91282b9b8b8226a6d5786 diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 122aeb9a53337..1b375e6e72c5d 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -1,252 +1,260 @@ -LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/341e3f0b5c160100f5e12783b8f779c0 -LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/65b2c2091db1364adba4fe1e4ec6b9d6582432a0a0751cd0a3aa1c69798633b3aa5ff09d3de4e47d552d55d5ba81bc86662f1784cff2ed58e800452488cf9d50 -LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/249910dce0a9ee089711b71626972b26 -LLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/1eba4ecfefb56a00390e5c528c467f921d64e9ca40f5fdb4d7fe0d7ee995f03d253887f7fe40ee285f03b12fa7a194543d18cade6af8a24bf47e56b06a78d901 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7bc3125dd810bcc44ea2d454b6caa683 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/86742a4476481b14145855ead8a5acc6397782f6d3445f900ac2de0570f1fcf53563cf5e1f3cb59886282083ce63756604f1ca2434e9e427cdc1bd1f68373581 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/4eae06d9e6272aef23afc191501810fd -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fb75927982b1428b05b765bd5ac017b2c15d89990b7e6cb582b9e1a3ec04d09801d25d5cc6c037a12c205edb7c0f7a2d33832a2d1de7920711e9720dc3ca3655 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cd86e18a63cd6e84a1493acf0df4e267 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/1dfefc4600368467ab90ccb527a9fdb012b9b7f485d932a0db8c4b1b81985fad931b74494b76ef2162e46280447d39a055b5681b33a17c564c50094de29aeb13 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c7cf7daa7c11827ae4f9fb2e16f3cce3 -LLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/dabe2940606a671a8e3b4f28bb9e813d000650203c382372142457020f2ccd498534903aa99320afb7ff960a62d752ee6cb724e74745bc1bad1051e12cf78ab4 -LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/62e575b89fd92d9206abebc19b084abf -LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/7ac029567fd68fee82b7096e2fe278ee5cd2935494433b1faace036469c54bc471d614d0bb339750429dd88f3e723165d2dacaa627f73c3647c6f3b51a4a3034 -LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/5d39ef811bc78204ebfc7e98111469cf -LLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/10fc9a64d63351e168bc79fa63bcaa6fd49c8483e5ecc40a66216192588367e9b47ec3ea2c047e88f39ea8f1caf8052726f4bc8858223f7744606156b4133970 -LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/f072fe487e5d1b717aec49a6244adf05 -LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/42b03a2562728ac86e751abab2e8233d583baf006e69b107d002a9258844ad53f62e6332eab3790364940d478c7ebab6d3e0e2194220e8436f40e6b75063d1a2 -LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/eabf0239298f13ff4893011e75828bdf -LLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/34724d9c9a550c85d406021d7265e1848b002b8f212427eebff6e8f03ec6acc336efb0c2cd9d9e1c76329e7c84a84a9d852b8de5897550d957e0e9385129033d -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/1910b5daa31db6542f0c762901ab7d43 -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c43e8091e9946ba1d8849734a25b258df95b4759a79676565b624930d4a19805a78b66b1d193e528f95174d909d7895d4a4e49fe8ca298a24dc40d25c95900b1 -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/a5198b13dc75ad3454e05aa6cdaca48f -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/9ec8078a1a7246f1545fe074783d6b88ce9b50f62b0438ff5637f6dedf5bcac427cc252c350354b7063f79f4e31a19f699c168c15bc6547a207da497026c2827 -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/f569654ecdd8ec2a50986ccac8388c69 -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9b50e3be1577a753f0ce42704846bd126229d8dd9f28bfcbda58c4f18e4b9ca4ec6bb9b57de61b3b9af8157a2983aeffb8af782a073e5e19a8ccc261cbea9601 -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/496de8c9e2361f44ac6933480620d07f -LLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/02a8ecfb6e81e0fe07fb0d616a84a590e23e944588c18348c32265bf6bf19196beec189a0bc40514e379e97a9c8bef83557260839800fabe9f8e39e96689713d -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/05bc7406fd0a703edbc912bb3230eb37 -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/898dd4c19dd0f22dcd1bd44264daa8dc64340c890c3368fac7451da1ac872a687d55b5eb50ae4e156c2dc4ece226ec05775daebafe9d8b53eb83b72d2986ff92 -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/d6ca30fc3a2796ebda2451f80846883d -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/d7dc96e1bbca38272b1ca78b3ff995fc30434937a58815c63d0a9b4a017964cfb269a1f3203ad8374870257152229941d420f098644375b5f4d1b88fe39e0dff -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/6eb1a197150ad6c165b82c5e0e0db102 -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/a159598c2bf351ea79d01e8a454a82bbd9823c080399520af3182e57259957ad07834b03c336e6225857da365e8ec1aa9f65b0ddd0821883ae817cb81f8e6dab -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/116d849cb2fb4b1c8c517397b2b04192 -LLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/7b2596c76d2814fc30992ba78e5c8f93519442fa76004187de9830732b80bfc6c77f5d7aca042c20d8f868cd682bb6f71e3fa32940bc8c7b401753dc4ac2f331 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/27837dc854a173bd37a20f92383f6913 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1719205cba6de969e8724a99444bf958d5a7943ae90ee2dd11193f56ddfd4f0edf6d9af6da2e67787a64b91d994fee76bd8ffde36486c5229a980c2c4ef07e29 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f0016c21c045e205131ea22dc711acaf -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6d192b7e21c7ee3327d288b890f4c5dd03e5f53dcba6905a34cab96b7ad0ab6364f5271af88d95e60aab8f569a8840d17e16f27f6fcdafcaf537d5d4a651dca7 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/9a2bad4518966db29e37e7c88388e779 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b9a10af9dcbacf1f129d4e9b4cf562a6a4687252cc8a0fcd78f52d75c0c20be0ff32e67413a7902a628b04e7fac1091d35b64b145e33814899796009b6ed2853 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/77c4e24c1e44ce14bc6476954f294a15 -LLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/d9d90a4ac788dbbc1b532623a380d4cca8813ecdf8b7b4a8cfff769499e50a1433bac618234bd0765d8a4f50aafb3fa724d16ac71baf75ae5a2b4396fa2bd017 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b29e36dcf5a0aa05734f1d6a0afd6944 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/ab46a835f9843c5b3427101bcd0c5d2b8acf79693aa9b8d4282d499f25df4ca248a81fc94ddd96c75d69d3c6b3814b225eed81bec32fbe9199bffdd605f7fec8 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/a411269f925cc968a0438562262e6d97 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/04f275603134b0ea0f23da377e4983765885f2b1954d5c617134af9f103470a5e50dfda18bcddb836852db2382f1c134db40df00b36c8bd00e7a9e6ff1a9e684 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/841921e33407e15eeeaa76354aa2b737 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/e1fb8b75e141cc90916c5c81c31ee91336911983c525f38eab86682ba69679dfbe1f10c9b673323632fc75f38cacc2af47a3d5d5d1031ec9a2a60cebd68d501b -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/7342a1d7b1d2c0fed7f5edf1c331ffa8 -LLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/dae8ca11fa8d34f99ee19a95bcd108a65b9e6a6ddf2e5a9b126f2ba1b1cdff6b7ec21e9590d70b3785593435bb71e47703d9765811db814a90aa8a47940421ff -LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/10aac489dfa10a77427a82958f525da2 -LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/a87f721df4fc5f6e929a54d8e41e55fb366a051a610836923213bfa42a7f1593de880391131619653cc3571bb76a4c82e011852ee5a6005523957c9f0937e6ba -LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/7f231fd359f9297261c22f95d8f738c8 -LLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fdd6441011609ef341108ff2d108c6f320d415b621a69922aeacc555c3d1ae6090a0f600f24e229a609b88ba9c1868900791a6590033b7dad333ad11f8a6365b -LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/c4523a485082044553e1a89049dc4734 -LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/db365e63bbb5189f7f348e2fd51e627ddfebf838ca9dfc6c0f8a7bbf6b8a2a03d78ea3ccdf08b0c2674f4cf5a0979506efa643554091ba751f16051bdf42ca9f -LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bcd10e4f3e5a4b00d52441e0094de1c9 -LLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b17fae89a3dfaa9428cf48c9c0866477cc75edda6aa3800702227cc9e3d6ebaacbd60cccc96acb4ccde56a2de531dea5a436bac8e6c450a4674daae23b878037 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/2be8cf274b7667adf8d967a27abdede0 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/15f58c9a00aca5bf828708089912f128adfa3b719cc2fa8b9b4cd7ff7722d02375bc9a961b02d5c6a6c9ab637b626d78876741bd824353aab944e1c3b6719837 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/0dce4be3e8cead78cd3d12ca0796d560 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/cd60b39f2ccfca8ae0a497292819e9cc1893f6c3b2162fa9bb3136187351cfb1d6e4855141f1e9252bdee7e97ad61c0560566c2e9f73fe77a26b7f4ffadfdcdd -LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/f2548c8f4bf1edb488642245221829b2 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/1604986526156a40ea82f50ddd0465d06df9faf306835f1dbbdac7da7f97c60fe684cd6c64acd8833a9f8b1d16f80c123ceef94fc16f255f815b93f1d41251e4 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/1c268e3e93ab3a34b3c05322c2fb0dc9 -LLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/f111ca82e196ea9507bb089b9d10990de1acb1a94778c40012ba6bfc16cf362369fb1f9dcc869ce14545439df21f432589ec004816a1ba0323c5edecc2b84211 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/b39ce0b0f143c3bef4dade99251003bc -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/62148e1e0a31d6b28effda0a5016d9335005b27ffdc5be1d184efcbb13f13e29eca52eca19cc6800d1d0421c0e67a36027e05d5fdc967dae686b5bfd112fb2b6 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/9475748210eb5b1947fe3aa6673b6c29 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54320295e59e5903db558b6be0220442dbaf7ea78e1612d54a35cbe014541b354ea708679da00851b962140b6da77301e27b656fd478666d3f0f710382c13a85 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6a533054ccfc3d1b0920eabcfb45ee03 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/3871620aeea2ccaf6e4b17a675c5504624fc6d8ed57bf4e5b66e0372b7124e4f3d1e0f10baa1018d5a1ac5bc4bf0e9d2143e84827712fda1f512fed24829f1b9 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3fc6d1b7d59b98823d6016f97835b7c5 -LLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/745942235e40f2ab71a5eaef2768842823620d4a4dc7454a7512fb2bd95bc8a74323eec6a4b33edf1ef935151c18a20172f60fcca2fca1ff3a37b1e019ea4640 -LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/f069af39cbbb650e293093b5989324a8 -LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/34685eccd8c1cf7b72a52bf353de16bd0cac13959584217ce5d0995b52f506909955a7051ff7b29ab9d9c3f603af8f7db936f11e4bde83f5acf16415de62880b -LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/819a9695c365b9365b6cdba7cf9288b2 -LLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/4280768862b19918e11b6a7ed09f150270e71cf4560b18b224b3591c460c9375777e73e41eda375271d719f23b211daf3ed51b3c87bf4ee4429344d14f1ed7a5 -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/28ae362155ce224cef605cee53e36d0b -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d90f25e57f92a9da68245ceb15316e3868bf657d7e744f37cce5ccb4945777ec82fc5d470ba4fc104fe7aaabfff7b0dc260838a45331e4360b0fd14c59a55666 -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d10ec63510dc1a043ee0a4e37b49eacd -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54c393208d1f51661e631cba62a21c0685fb58827067d5ea7c42fb3d6dd8c8db99d8ee1b3c304abc25510bcb0265d86ca03e1ce19be4faa252d97cfc8a1b52cb -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2c1e000206c9e7c6c8e7515eb8115e3e -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/12c0ead798e43448a30699b5386b3d88aac49aaef9bae283ea6d089a1c66df7293f4f220a2b5c3d96e73e556e37e745f38d81f5c68e09a86a2b19a6695eff460 -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/21d6c5d5e422412b88ffce50862efb29 -LLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/5e8e17ba79134e9752c7fbd28b62e4616574a5e1dfcb0980160a3aad28a2f6cec4e48ed1acf73ca1f94d74397f7ee3eba53cb1280699e40c451295590ede3fe3 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/293fdc43431493f915a3e0a5b3c6d587 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/27e13a4334a3bfb3c91fd06abcc4eca7a347f4bffcbce40834302d153ef29756295121b42ac433c266668af1428ffa08ed12ce75f21fef44cd7ac1d8bdfd155a -LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2825dac8280d0563b7f521a9eb8c0563 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/7f4549ac7b63e58d8c149f6b22bd997545713477a1df3b32adf640f3951580df1645f08756d9ba80c479160cf5759e3f9372396655a35cdca14f4be4afc4ae22 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0c0da0eccec4a092fc0e9a915716ed6f -LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/e538e29c4d52d9aaf151670619702541fed8231ae4c7fb9431a425d10eea95433087034a37da8fe468bd27a1c882f6f8eb9549ef71964124db10e99f4b402ba5 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/6b4fd19277c978306441da3b58ab86a1 -LLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/6216b3e1dc6aea979d8b5abc4cc0faf510e4e64441b1d18b4b36c45d65e874e9046e14eea67efb88f3219449ef048d34fcb751b15c59f8a299aa822b426d50ae -LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/b7956d25e0e5ced19df637b4fadaa532 -LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/ad632493095a8fc3638ff48514c9902215378532c1455cb19d70da9f2ae46fdd91ad4a8b5a3151bedd38dda9f07c21f9a25d8e095ded7ba843f9bbeb005e1bd4 -LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/392f0f0f61fb672002c7473c64a63ccc -LLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/d620dcee0b20e3aa4b2fcb7ae835933b33b5e4c4b5d9102b885c70b1dcec535239eb5a3d6b56b51f7b049943a2c79950bcd4a4425610f7a1531f6c452eac03bb -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/0b41650067323bbe0c5edd5c060b517d -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/111a21a5b491a77c69ee724b37d15b0c7baea387bb6a36695a1c2dd5f6e2eedb0ed211513145d8a6ce4dd6329b2de67e9bfce1b03fbf911b906a33a39e573f9a -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/a9079da821bee8e4b5aebf47a46cd9f8 -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/7088945264d1ccead492e81636086390fad91b0e071e9f3a54ef903b619ac2a7bd38fa5e0e04ea1e299f3985e04838cd5b7a2dffd666b8e7dbbf3b419f74df88 -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/4ccb3d0eabf8253cbdc1192b04c78d4f -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/9d817068dcc2b60c77fa639aa7632cbf071746e7dba62fe524c095f86e88b9323c3ab82ed5af0dc8b1af9c3e6f0da18be53d92e7c05e2d056c84e5a4e974b6d8 -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/a88f7a9f42d2cb5567c84d7fa2a2732d -LLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/9b16cbf75e9971dd4950cd79aef85396a7d8522a572f1c8017af82725cb335674741af680e1dd10c731987a321d3afd5e3e85718d3c3fdd1c9de4803e72a66ac -LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/md5/b95ad4844e649bf46db43683b55b9f4f -LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/sha512/15e0996aebe6db91fe58121001aa7ea4b23685ead3c26b5d89afae34b535e34b4e801a971f4854d8e1a1fbc805cece06272470622eef863e225358113a127913 -LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/md5/6d8783dc9b86c9884e0877f0d8ac4167 -LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/sha512/d3b0c81498220d77e4f3cc684fb2cc0653792c381207390e695ac30bc74249f96a333a406b2cebdaca14e0b0a27b188cba6209bb5c1cbbb5c184d5626dbdc7a0 -LLVMLibUnwind.v12.0.1+0.aarch64-linux-musl.tar.gz/md5/052a35e879d52244e4b0804be875a38f -LLVMLibUnwind.v12.0.1+0.aarch64-linux-musl.tar.gz/sha512/d1b34fb97f9928e046d3131a050454710a93d38e60287b7e3c92f179f436586d3230cf90b0ca0eb8a3f9ef89fef7b1ffd7d52871645dfa233a8b07ca87ea2ee4 -LLVMLibUnwind.v12.0.1+0.armv6l-linux-gnueabihf.tar.gz/md5/1ad96a03a5dde506b5c05773b1849ec4 -LLVMLibUnwind.v12.0.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/82306fb7b920fa7c71bd53b23d6915e7f256e8da9679cc926a53bb0d879f1f4469f43efe556ca32c9ef59e27b435572c7b39859090652635db4eeefdec0d1685 -LLVMLibUnwind.v12.0.1+0.armv6l-linux-musleabihf.tar.gz/md5/6a24fcd3a4dc3b1a98bb7963b1bb4930 -LLVMLibUnwind.v12.0.1+0.armv6l-linux-musleabihf.tar.gz/sha512/9ba6b83ccec061a1e5260c807dc8afd6e18799431b25a7e65b97662cc4db02509d02ea07fe12025d80914cec7383624b1c8fc9add46511c668e184ede263ac52 -LLVMLibUnwind.v12.0.1+0.armv7l-linux-gnueabihf.tar.gz/md5/09f1bfcf58a4124561553ab5005f9538 -LLVMLibUnwind.v12.0.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/b0907cb857131183ffc338780c6c6dd1d48bf0ba61c3da1b8f20cf9a943373173b621cf9b2e8f1fbc657059a896b84aa025e6d4f0f1d1e8b623fac3e96541765 -LLVMLibUnwind.v12.0.1+0.armv7l-linux-musleabihf.tar.gz/md5/19158bcfae716b26f924d67c4e719342 -LLVMLibUnwind.v12.0.1+0.armv7l-linux-musleabihf.tar.gz/sha512/a90be57990b6699cb737ba96904e94e1f082601ca9d01e670f025b5500f526980741921c9cf672accab78cb5327714ab6ecdbb875174088f0773ebb627a98819 -LLVMLibUnwind.v12.0.1+0.i686-linux-gnu.tar.gz/md5/ba75556eb96b2bcdaf73ff68386d3bc3 -LLVMLibUnwind.v12.0.1+0.i686-linux-gnu.tar.gz/sha512/612fb765695b7aae11ef29608eedf8b959f60c021287a67b03a2a0f57a5814001ffa9b261c9d60d5f3d0582c06c2b41f75fd3afb66a045a248bd43d29e304c97 -LLVMLibUnwind.v12.0.1+0.i686-linux-musl.tar.gz/md5/2fcbceeb1bfde29be0cbca8bb6718bfe -LLVMLibUnwind.v12.0.1+0.i686-linux-musl.tar.gz/sha512/58f281cfc70b3f8a59cf4faa7732824637c811ddc5ea6a058f294f4c3ed4fa6c8ddab5c007567b439f2854635cf4fd146284059bfbc73e7006000ced9383f705 -LLVMLibUnwind.v12.0.1+0.i686-w64-mingw32.tar.gz/md5/153c028d97dceb6924414a7a9a137e1e -LLVMLibUnwind.v12.0.1+0.i686-w64-mingw32.tar.gz/sha512/7ae1f197600eabde9036ae58623de34a6d25636d7861777e324eb97902f65e26c6f3775e757178f8914b0cb6c2e925413f5ffc6abc9b6138470dc9e67a17f212 -LLVMLibUnwind.v12.0.1+0.powerpc64le-linux-gnu.tar.gz/md5/c08a6cf3e1baf156eb05003ed4e9ebe9 -LLVMLibUnwind.v12.0.1+0.powerpc64le-linux-gnu.tar.gz/sha512/f74e44986622329990842cb3ff549ff9254c81863d8bee468b0e58b7621067e7e7f7f18e4cbeafad6a05e0c107323de6828a78dc7afbcd7cd1892383ff417968 -LLVMLibUnwind.v12.0.1+0.x86_64-apple-darwin.tar.gz/md5/caf151150e56827be09acca6964d2b18 -LLVMLibUnwind.v12.0.1+0.x86_64-apple-darwin.tar.gz/sha512/cb3e7aa71367ec4a115bccc2e8ac6bd5d9f22b3935b3889eee1fbf7303c5f553d7d3108977bc1f6c9b6917a6ed9e10bff211fd56b8169233ceae287b112894c2 -LLVMLibUnwind.v12.0.1+0.x86_64-linux-gnu.tar.gz/md5/d95874cbf6f8b55bc314c3968a6a4563 -LLVMLibUnwind.v12.0.1+0.x86_64-linux-gnu.tar.gz/sha512/4986a8d9cc9d8761a99a4f02d017b424484233d4cbe2d4f49ccd371591384b1b8d1c4d31cb908505b86b00f2b164568e57751dd949d91af203ee4a582971798a -LLVMLibUnwind.v12.0.1+0.x86_64-linux-musl.tar.gz/md5/89077d871e15425b1f4c2451fb19a1b2 -LLVMLibUnwind.v12.0.1+0.x86_64-linux-musl.tar.gz/sha512/b65a218b05ade2e2d1582188897b036a4596d09cf65558f178c49c1a1a62b7d992b1d99fbe86a027dc83b614f178e6061f3dfb695b18a8e2b6bf76779b741d96 -LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/54ac594b4c8e7f261034a8829dad5e34 -LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/a43756afd92081e6dd7244d162862fc318b41ca110a5e8be6e4ee2d8fdfd8fb0f79961ae55e48913e055779791bd1c0ecd34fd59281fb66b3c4f24a1f44128f0 -LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/md5/83cf8fc2a085a73b8af4245a82b7d32f -LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/297a5c7b33bd3f57878871eccb3b9879ea5549639523a1b9db356b710cafb232906a74d668315340d60ba0c5087d3400f14ab92c3704e32e062e6b546abf7df6 -libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/2ea6046caf5a3d519ab1c3309a2eea31 -libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/079720b30c61ded8499eefdb314477d58bd121e9f326d98696ee39b2ed91f806d5f67e68b6fbef8613a992175fe34694e5efe83e87ef3bfbed67d6b7fc41ebf9 -libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/62c49bc7767d1ff114dc6b6a996449ae -libLLVM.v18.1.7+2.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/c708472b325cd73b94e10003bf3267b0ecbf3627072302fb22e78336974f2c7855c8597420efc954bca30aee17cec55277aa0c95a01cfff38d5d77df50c807f7 -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/766a2de98d275877bb676ff1f23e972f -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/3b353ea038fafefc13ccb4a81c7242d569c206362605be374fb312cb495f385796d052c3a7e08c7fe6ecaa3018e2a7e3dfa43d71a8c3a94987f7dc7aa378fd22 -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0684a6b210b799a8a0f45a286f3dfcc5 -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4221e2d74117bd7e89aba2945030c1507e51999b236814fd23036565364c328392e87032daf1b9fe274ed89fcf9a6dcd203f0f1c8602c2a08d3fcfa189a5fefe -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b460256e923637e5107d67859eb60ba -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7d3f2736afe4022842529b1355cf9914b7a1c7b1e261f814a4523ad30a0cf0189056d5117a06720bbb7a844a435bb632ddbda2daadbf7e01c0120452cd13e6a3 -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c2b13a6a296adbb4be91dd3bb5be0877 -libLLVM.v18.1.7+2.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9086937e718125afd535b0066ee08a3523161a94fa7ef3c9a3e86bfe760f251b6ea7b035888e61a0e7f192ed25c9bd0f4dc153df86e08569e7067a7a30ba48c5 -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/758d33fe0b2b3d0371708614365450e8 -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/79a662f72ba1b89b373d1d143ee880a12cb128211e79182e7befe8b3e50298b594de2ce489ca8bcdeadb17fceee811622f8bfcbc3e232cefdaf9927177469eec -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2dcbb811be8985bfed3c8b37733c0d40 -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/17f6fbd96ed5029f360c101cedad127881e14b42498d66f717448d99ca1909057ae79169d934e08157edcc7467db4b3941bdda26a2e9f42645963eec51f27e29 -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/bd3b904b5f9464aaaf87c41b899c8ca5 -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/fa99e8025419a18f548f658ea589771c2803480c3cb3a25cfb75e26ed0993b7b37bba204d7cba1475319a71159813b2b58a3b3327ba24d264cf80ef24263628d -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/b4f9038d5c3c13207111ee1a9a918cba -libLLVM.v18.1.7+2.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/e8b97bee30f597cc06d31175e12f0c2035aef0054e8abdb431f31b1e9d440d561bd9bc6637a403441aa7f3e1d2a46c600734e17e3b7ed0ae899c92df91758780 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/06d8e634b4a6914efc18b7962df52021 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/cf6aeed1eaf652e5830e34dd2ba88abc33668953281146106bbfdbc92f5f225645f00ff5b4a0eb902baf904362ab4eb32192fa50ee5b2672e8b031fe2550f9a8 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/53e83804b63e6ae4d0f1c97abcbbd1c8 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/45b3ee9b105ef2ef106fa8ac7b8e902cd1d6bf3c9bfb57edeca9e14f1654714d23fb086b369a9fd3cbb828c04fee4cfe80d2b2a2bfaa852d3ac65c0d213d8c62 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/91b6cf00564053d385e30b34e5b8778e -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9111f3f02b49bf78340c9b0c5c1325a1ca09b62c83aefece1121573dcc21dce095060351f18997971e5cfbaab346cb12c75cdc0fbe8fa92aca2e8a68b5f5f577 -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/f6c91b71dfd73c7301a4e3de48e072de -libLLVM.v18.1.7+2.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/581d7e1e4d85aeaf082fa31555074471705e391de0771bf66665807afb5192c79c481ca30e73a25f4e2d48d4d325f0198e39bcbfaed2c9bc7477ee917667f5ce -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/ce41ee46959e5e3a17b6c99293afedb7 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/73d8c5af750ea9deef822aec58d8697243ca154bc4435ac0b0ab8c90fc97750e91fa55f8de7b8283eb1ab19951cda3e3c4c60834bcf13730163e593126a8eb57 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/67ed5b654852dad400aef17fb542703f -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/07f70c57e27eea37f520f6f0a954b54d2506530d5eb5a74e5a8526ba8ef55a948073c49037544b602d03d0aa482704292eac943f0a83421386ccbfbf22ee8510 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8bd88d49ce21e5b63af6f77782eed4 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/cef1c561ae388b2baa08e39dc195989cb795d8a2747f5f11e0dc9d9e107b9e99dbba465335376beff2e1b326512f6afc962775e0b246f3edcfadf509235cabd8 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/5fbf26d20b2ce3f61edc9a9ca2eb5284 -libLLVM.v18.1.7+2.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/2c564c95d648458b9a0f0c963246cf5564c625107682f680390b6db5fde0e2b15a964fd3fd23734b5b2bb135db1fc698812d61b3f275710593f4defaee4a9c23 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c81bc29a75acf4f806f3eb13bf890604 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c8c922a0a4fefd549f1c2ba396a3cab9cf7738aa82e7ccf7ca29c090260e2d73ec45d6f2b07173d584f6074b10fa04052114deef6ecb6f53ea87f1924074137a -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/1fcb40ba1a427105b4e7d13a6c11dc78 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/392c9ee85ba7ab6697bb8979c7f443d1d25f7ac9178e96a886401cfc68d75a43ce98bf3038a7ba70a9a990f65e604d38e043472cec3badb25fbd1b38cfbb7162 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/427a19eaf69725d11bb33f48de9cb205 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/542e209b10c13d8dca867247a7414f84adb832f40051fcbdf0dcb09bc9664a77248e1b0ea1687805847dd9f5a05b86475dd76aba427c9a1bc83f8502444c60bd -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/ab34bfa2950014936edd13a7b5db8170 -libLLVM.v18.1.7+2.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/6376b25d0278e5c97581480fb4d54371b09a08be88f4cc39d2c7b3875f1189cef60c1be6bea5e12b0cf306cef8b394bc7d00f8b0fd95d749bd1b4eb318af7e15 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/cb6300fe87fd7cb9840f3bc44af26878 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/a7984cd90fef55559142fc05d91b0da1f37f77f25214e93ff7641b7c3958f08dc7c082611915dbfda4bbbaa392656ac8604d4f75369777dacfb78baee2f99b16 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/b8a4e8ef43340e9cbdf5e4479c6a5a56 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/fc249f2b666c8a8129e05ea08c773cbeb7af6d37791f271461eedd99adcfc5082e8609ed096d8a46edd1e73505352712a41e0ddc247a371f78227aab01fbe0f3 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5864689df3298be4b1b4df1ae0412d3a -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/8f32f73e366c3a6993fa8d6b8cd1a9391611b0644cd4a77a4f7a235c037fdb75308d99b5a23ada6e4a73ed5fbd8f929a981d6bf317d79d52396220c221619303 -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/6bf798476c4e94716cc47a95580104ad -libLLVM.v18.1.7+2.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/9dbd27a000dd3c3dda9047d366a667c4b179cc61582525adb0f8227e8055413ce46efcbc1530305400239656e2f1016fb8833fb7f4734714078e035d388f3531 -libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/66e2889f86ae6bc1977419e6d9be729e -libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d0cac798c4979b4d818d36596b173e523cba3f41ff7ab1e2111f6a75c3e819e563e207a547328f005c5a93c7f8f88c17bf43c1139b5c2690df4f1d719f82920a -libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0534b72d6d33c8573f79dce8a2a5a6e6 -libLLVM.v18.1.7+2.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/6beaf1b45eec8b46fbf92f692f53e6df40bf48e50589aeb5ef99240a5a3ec9089ffb350dda6df24530937d613bf6d2cc4da76e92921ea00def9d2d38ac5bbeba -libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2cf9a1ca20472179ce4a9eb3a949457b -libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/cebae06ccee12a14d20d3056ce0519b1e774e3c9d9200a783262fcc40aee6d7aabfb08714bf53b88e03d8b09a96d3cda248a70c16188f8c707b291642998262a -libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/4712f6a46e0ff407ece958a7701511b9 -libLLVM.v18.1.7+2.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9a0a2dfa2076b93027f766277a6890cf94d67c131697f74945e92cf13ae64e84c09d3dd744498986fb22ad5e5465300aa9c8ae6632fcf919a0932515edfcc1e6 -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b944ae477232ef10d213b4c7743280fb -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/25ff757620baaf6fbacb375b103dc0dd9af6a23c3d3bca567c182a6357a367ca125d7b6c66927d7db23816865b6ec783157352fba08532336de467be80efcb9c -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/52345a44b3ac74b3cdf93852bbc63710 -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/3e5b449b0f1bab302c45f9ee9f04d2cfbb01ce24e86096aa610fdf360ad65828f1b73734beb28b3d3c249ba8ef657d2663c5492940504f47c973038733b15248 -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/36e058b96771b4cf77e29b800227fa03 -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/98873cb2963c4469b0f69ad1d9d9e27056aabfb46a2642dfa3507b7fe2f0b0fc41c3991a2543125291783699e39fcbcac0bd6e92fa8f0df97609a85c340fd25b -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/3b3823fbafabea289a769958f633dcdb -libLLVM.v18.1.7+2.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/91a9c1ad6f37cb1186ba3392935fb55d49e0f8d6afc768cf881886f9b1d8b0a2b0ecf0c81a8e32e36d32cac04c065ac852bdb95ba5ff6780c00a763583a02973 -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/bbf060d61b294b86f7e3dde381b00b8a -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/632372d41f6e400a10fae27c6cd06a5a344cfb5902cad7928cb4133f14f36f0a3373e69e73ce9baf52f518340593c3a5a16173ef59a1878e6300e9975aeaa157 -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3d730b713e01cdb5a7a5a46028afd41b -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/052ab4fa7ac3b2c430601753ab078cdc9fd6db7f65ee0b76bb05473f4c5b99ec8919ad9d347425f1928cf619548e992c86ba97f9994218f50bca617e43d2f0d9 -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/bf9dcb92ba8c031ae62ed4434fd5447f -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/e53be14dd02a2cef8eccafb9301d29c51d652c635703529c1444947002993f6639083eb8bef13af21c9796717ce4b3129dcdcbe2751a1173d39e321db8f6e3c7 -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/b5cab0fc7c6643c6dd161f1e553ef1a0 -libLLVM.v18.1.7+2.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/4032634449e2669479761c4323096b152f8df4948e3a97eea10f0b400fbf2a00d1edda59b74a714b62c4e204b113d8ecda78d828c3344ebe8bd750d14b3c4c7d -libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/054e06d882173ede2886c510e8519c80 -libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/eb97ec25354badcac1b8a4a09fd9e04cfbb7d35493c54cff82af9ffa4c2dc5070c9232a86e900d6eb9acb03f1c572fcde8d2a865477bf6c9fbfc139763a9dd1c -libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/f1c23200365b659f0dc07cc6d0a32c60 -libLLVM.v18.1.7+2.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/fad13fef7e7584b3f756fce9125950e788e79608cf5d0c023cb8f8a4e79001afefa8060f7866875e4861a268b3020e50305e66bf472360c1d92fce12d7a81ba9 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/69564913bae176a167d24d3291ef7af7 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/b8eeb86b66d767218e59671bdd597623238eea72319913c2ac5e116faec3f4c13739a24f3b95338ed857ec29e714dc0308e4ddbfe359332b3c27ad5235052342 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/bc9d5637fe30f21d2231a98371e798e4 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4efbc2823322abe80d0134d35926767bd9cab717cde9308726a6a8891e5a707476138888c695ed399e3dddb57baf17abbc43a0a338cea2e5c0f472ab427c12e3 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/8492ff91e6dbd1a66edd8aaf0390a582 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6443bd2fa9c5beecc2b002c26595f2cf3a8e2ea5eb49aa4c00f7252a6623fe0f8c01824941ebe5475460641285c4e56a5203056c1b93a78250b7e48fb5ac9e00 -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/6918c9978fd8b5887c66eee76950478d -libLLVM.v18.1.7+2.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/d455a4f433bf3ea1b5100b9d45199bc785e4b6fbc7659bf06cbde6ada471134e7d4243d3a3a1f71d579126ef8371d70e59f174e124b3ff8d4842e9ee83e2dea4 -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/075f87d106dd95c8e9c6e7e157b5e9db -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8132379d8f44a21082c7a90f58a7dffb0c6ee725efd58a959d4023787411b080d72913bb1e89a35072f97aaf1ca512ab1d027b37eaed819e3c053d7a0cf64269 -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/4cfc2838a77f05883f82e50b3723dcfe -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/20079c81cd6a4020b087485be1ab4928b3bd3e1a53728cc98137a35b969484278093bc75a9e51ddfd8331556577c5fb3109d74dc2eccffa93b5390e0fabff2b1 -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8cbf00631bd4540b7335a86302a1fe -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/51ba9a4b74b740905cee4baf7f4e5f3620ed81e0746f49cd352d874ebedab95277c5031123f880c9239b7dbf505b10f6531f79c8a6b0482a652b8324f4137cf5 -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/11010cc2d58b1a8c6a6e7bc24df0c0db -libLLVM.v18.1.7+2.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/a6bdd9a2a2fa9a572e74ced69c3ce9d1b84cde18155ec9bc7dfbaba411ee6c43d229e6fb333eff66fb63b632b485b46b7cb1657c0c49d9d9bb849fa13f0bbc7b -libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/8afe26d16d9fdb0fe6c0248c51b4f053 -libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/32a92685f417c1887aef3cd8a9cadccc4de3e560ba8fc42e8db721f273a3451927b24dc4a2c2e83446e32a84d47f714fc3c22ce71989f2e97c5ca23a1783b8d6 -libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/59d8d911907127ff56f5eafcd8663300 -libLLVM.v18.1.7+2.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/9b0bf6f9d8d32ccbec349c249b79fd0fa3b4949c04b69c9d408f19dfa3b4f00e5cfa51b798234721f72f2793161d6af6491856e10e6a507976b0da6ed7a8065b -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/b0d9a7eca92d40ecbfa47461d52659e2 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/dc4a91e164d88ff51b4a642b556d5767156f28d1efafa533f5d7c619e05535e2000afb2ea47469a90f5a19f970e8f0522f35d59ec250e2f9b42ce22fadb9ffd3 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/92a60309ad33391415c6703edbbd5423 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/2fe90ac804d94bcf0d4058a8b8f0c274e405ffee7df0175f5e7ccd5014b29a813af48152870e1af0a79df8d3eec3118c233bc4f5b3f8439fd9792931140ee944 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/0964df17cb98d2d869a33468477f9901 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/2c062acd62175d32dda773e9116608ced814a64ab06ea73f89958437178e2603b268638e88162fb81c22e5947cf4cc925b1af10c6f9320be22c92b279b278992 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/7dfb8e61e972c66f1d754cb979bc0309 -libLLVM.v18.1.7+2.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/d462b6fe7aea75f6fee6c5c2f24576569b5deac8027fb88240e16c55a54d68b7dcb06b3ec4ab514616fb88549fc2f10fb1d587a641d6f29fa66273904bb9cfd8 +LLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/f8c2d285a6db7c3b89d295b32b78f07b +LLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/99d923fff09b70093962cb32d2a12a2d2355824c1c3404900d593cfd0e95a4b52744e7d3fcd22407651916adc2e1534637437630843762c3f2c0c650881aa0e6 +LLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/2ad6bf2ab91cb75bc3bb627b1859997b +LLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/bd06a3adcae64700f4051a18705e7937539b3cdfa61dda38260398a8896401a267b718594631d71afc68a3b273b0d05f6018927c3a08c070bd6c45d53b19c78a +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/7bc3125dd810bcc44ea2d454b6caa683 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/86742a4476481b14145855ead8a5acc6397782f6d3445f900ac2de0570f1fcf53563cf5e1f3cb59886282083ce63756604f1ca2434e9e427cdc1bd1f68373581 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/4eae06d9e6272aef23afc191501810fd +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fb75927982b1428b05b765bd5ac017b2c15d89990b7e6cb582b9e1a3ec04d09801d25d5cc6c037a12c205edb7c0f7a2d33832a2d1de7920711e9720dc3ca3655 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/cd86e18a63cd6e84a1493acf0df4e267 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/1dfefc4600368467ab90ccb527a9fdb012b9b7f485d932a0db8c4b1b81985fad931b74494b76ef2162e46280447d39a055b5681b33a17c564c50094de29aeb13 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c7cf7daa7c11827ae4f9fb2e16f3cce3 +LLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/dabe2940606a671a8e3b4f28bb9e813d000650203c382372142457020f2ccd498534903aa99320afb7ff960a62d752ee6cb724e74745bc1bad1051e12cf78ab4 +LLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/62e575b89fd92d9206abebc19b084abf +LLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/7ac029567fd68fee82b7096e2fe278ee5cd2935494433b1faace036469c54bc471d614d0bb339750429dd88f3e723165d2dacaa627f73c3647c6f3b51a4a3034 +LLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/5d39ef811bc78204ebfc7e98111469cf +LLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/10fc9a64d63351e168bc79fa63bcaa6fd49c8483e5ecc40a66216192588367e9b47ec3ea2c047e88f39ea8f1caf8052726f4bc8858223f7744606156b4133970 +LLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/f072fe487e5d1b717aec49a6244adf05 +LLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/42b03a2562728ac86e751abab2e8233d583baf006e69b107d002a9258844ad53f62e6332eab3790364940d478c7ebab6d3e0e2194220e8436f40e6b75063d1a2 +LLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/eabf0239298f13ff4893011e75828bdf +LLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/34724d9c9a550c85d406021d7265e1848b002b8f212427eebff6e8f03ec6acc336efb0c2cd9d9e1c76329e7c84a84a9d852b8de5897550d957e0e9385129033d +LLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/8b736710b2c749fccf0a782f3b887ec2 +LLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/d7458ead5a604781a117e54a03dc6f3fc47e932298c68af425a6725ef4767bb512c910316818081d5e27d9d08b4ce1792d684c0014271fd492eedaf47acc5eb3 +LLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/md5/ed0487ad3494352ffebfac51ef947168 +LLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/sha512/e13082056be94335b1f4253afe3c4a25555b6bd10c5d68052f01117415dab344a3f883a9f25ff4ac630262756dd15825e74395650d80181c85c0663d7028a9f5 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/1910b5daa31db6542f0c762901ab7d43 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c43e8091e9946ba1d8849734a25b258df95b4759a79676565b624930d4a19805a78b66b1d193e528f95174d909d7895d4a4e49fe8ca298a24dc40d25c95900b1 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/a5198b13dc75ad3454e05aa6cdaca48f +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/9ec8078a1a7246f1545fe074783d6b88ce9b50f62b0438ff5637f6dedf5bcac427cc252c350354b7063f79f4e31a19f699c168c15bc6547a207da497026c2827 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/f569654ecdd8ec2a50986ccac8388c69 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9b50e3be1577a753f0ce42704846bd126229d8dd9f28bfcbda58c4f18e4b9ca4ec6bb9b57de61b3b9af8157a2983aeffb8af782a073e5e19a8ccc261cbea9601 +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/496de8c9e2361f44ac6933480620d07f +LLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/02a8ecfb6e81e0fe07fb0d616a84a590e23e944588c18348c32265bf6bf19196beec189a0bc40514e379e97a9c8bef83557260839800fabe9f8e39e96689713d +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/05bc7406fd0a703edbc912bb3230eb37 +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/898dd4c19dd0f22dcd1bd44264daa8dc64340c890c3368fac7451da1ac872a687d55b5eb50ae4e156c2dc4ece226ec05775daebafe9d8b53eb83b72d2986ff92 +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/d6ca30fc3a2796ebda2451f80846883d +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/d7dc96e1bbca38272b1ca78b3ff995fc30434937a58815c63d0a9b4a017964cfb269a1f3203ad8374870257152229941d420f098644375b5f4d1b88fe39e0dff +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/6eb1a197150ad6c165b82c5e0e0db102 +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/a159598c2bf351ea79d01e8a454a82bbd9823c080399520af3182e57259957ad07834b03c336e6225857da365e8ec1aa9f65b0ddd0821883ae817cb81f8e6dab +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/116d849cb2fb4b1c8c517397b2b04192 +LLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/7b2596c76d2814fc30992ba78e5c8f93519442fa76004187de9830732b80bfc6c77f5d7aca042c20d8f868cd682bb6f71e3fa32940bc8c7b401753dc4ac2f331 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/27837dc854a173bd37a20f92383f6913 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/1719205cba6de969e8724a99444bf958d5a7943ae90ee2dd11193f56ddfd4f0edf6d9af6da2e67787a64b91d994fee76bd8ffde36486c5229a980c2c4ef07e29 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/f0016c21c045e205131ea22dc711acaf +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/6d192b7e21c7ee3327d288b890f4c5dd03e5f53dcba6905a34cab96b7ad0ab6364f5271af88d95e60aab8f569a8840d17e16f27f6fcdafcaf537d5d4a651dca7 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/9a2bad4518966db29e37e7c88388e779 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/b9a10af9dcbacf1f129d4e9b4cf562a6a4687252cc8a0fcd78f52d75c0c20be0ff32e67413a7902a628b04e7fac1091d35b64b145e33814899796009b6ed2853 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/77c4e24c1e44ce14bc6476954f294a15 +LLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/d9d90a4ac788dbbc1b532623a380d4cca8813ecdf8b7b4a8cfff769499e50a1433bac618234bd0765d8a4f50aafb3fa724d16ac71baf75ae5a2b4396fa2bd017 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/b29e36dcf5a0aa05734f1d6a0afd6944 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/ab46a835f9843c5b3427101bcd0c5d2b8acf79693aa9b8d4282d499f25df4ca248a81fc94ddd96c75d69d3c6b3814b225eed81bec32fbe9199bffdd605f7fec8 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/a411269f925cc968a0438562262e6d97 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/04f275603134b0ea0f23da377e4983765885f2b1954d5c617134af9f103470a5e50dfda18bcddb836852db2382f1c134db40df00b36c8bd00e7a9e6ff1a9e684 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/841921e33407e15eeeaa76354aa2b737 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/e1fb8b75e141cc90916c5c81c31ee91336911983c525f38eab86682ba69679dfbe1f10c9b673323632fc75f38cacc2af47a3d5d5d1031ec9a2a60cebd68d501b +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/7342a1d7b1d2c0fed7f5edf1c331ffa8 +LLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/dae8ca11fa8d34f99ee19a95bcd108a65b9e6a6ddf2e5a9b126f2ba1b1cdff6b7ec21e9590d70b3785593435bb71e47703d9765811db814a90aa8a47940421ff +LLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/10aac489dfa10a77427a82958f525da2 +LLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/a87f721df4fc5f6e929a54d8e41e55fb366a051a610836923213bfa42a7f1593de880391131619653cc3571bb76a4c82e011852ee5a6005523957c9f0937e6ba +LLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/7f231fd359f9297261c22f95d8f738c8 +LLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/fdd6441011609ef341108ff2d108c6f320d415b621a69922aeacc555c3d1ae6090a0f600f24e229a609b88ba9c1868900791a6590033b7dad333ad11f8a6365b +LLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/c4523a485082044553e1a89049dc4734 +LLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/db365e63bbb5189f7f348e2fd51e627ddfebf838ca9dfc6c0f8a7bbf6b8a2a03d78ea3ccdf08b0c2674f4cf5a0979506efa643554091ba751f16051bdf42ca9f +LLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/bcd10e4f3e5a4b00d52441e0094de1c9 +LLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/b17fae89a3dfaa9428cf48c9c0866477cc75edda6aa3800702227cc9e3d6ebaacbd60cccc96acb4ccde56a2de531dea5a436bac8e6c450a4674daae23b878037 +LLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/6bb986b1c9b66ca24c976e6534726b00 +LLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/1fd7cf1c80594561a8b83cf993192299e8a96046bd1e2f6eb330898c5e2dd0fc7c6ee0e3115d4e4049b83c71e724fab19a5d468e72fd141d8a2c4c02d831b71a +LLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/c44aad21aef3b92fa0b1543ab9e4b93a +LLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/1aed6fb716a576b132d13397c927b36f00d78a42e5273168f1eacd208e366c55328286c56bae0abaf2c7ee424e7f19f4e096cd53f7d7caf863a0d58de1a2386e +LLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/c3494f146906e178c5e5e32c10f6fec6 +LLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/a0fe26f88492ce8416257e76a5938a65b4911822c9c3e3bd0e3455adae1beaa952a769d616e8f8525c3bac64a6e3cd7f1dfd68800b5e7db94ad63320a2716e2b +LLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/4644616c2e8937169500c200fb56322a +LLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/1250c5c9541782dabb5f0063bb2a18ee15a5dcd0e8b675e78474fa7dce2d51dd97e1bc4eee0a526a73f7812c57e41faa85e021fea4de74d33c62ae67ca555d73 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/b39ce0b0f143c3bef4dade99251003bc +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/62148e1e0a31d6b28effda0a5016d9335005b27ffdc5be1d184efcbb13f13e29eca52eca19cc6800d1d0421c0e67a36027e05d5fdc967dae686b5bfd112fb2b6 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/9475748210eb5b1947fe3aa6673b6c29 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54320295e59e5903db558b6be0220442dbaf7ea78e1612d54a35cbe014541b354ea708679da00851b962140b6da77301e27b656fd478666d3f0f710382c13a85 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6a533054ccfc3d1b0920eabcfb45ee03 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/3871620aeea2ccaf6e4b17a675c5504624fc6d8ed57bf4e5b66e0372b7124e4f3d1e0f10baa1018d5a1ac5bc4bf0e9d2143e84827712fda1f512fed24829f1b9 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/3fc6d1b7d59b98823d6016f97835b7c5 +LLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/745942235e40f2ab71a5eaef2768842823620d4a4dc7454a7512fb2bd95bc8a74323eec6a4b33edf1ef935151c18a20172f60fcca2fca1ff3a37b1e019ea4640 +LLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/4bf72195bb2b3fafd98bd3f1966dfd0a +LLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/6554fd0374875428d0479e192ac3c70823a1143ac9acf0fafb3332f6c03e7fc8d14513512152bc995c186024bc36de77c5e7895ac1382f962b22b1089c3cf176 +LLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/5631a8736cab900c3fcfeb559abc54a2 +LLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/55d93ffcc0125720f7db379396c5a79e98408225aebebc72fdd05b38605e73481eef46c219f59088b3bdea6257a7a7e369e6e0110019164374ac35bb49897738 +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/28ae362155ce224cef605cee53e36d0b +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d90f25e57f92a9da68245ceb15316e3868bf657d7e744f37cce5ccb4945777ec82fc5d470ba4fc104fe7aaabfff7b0dc260838a45331e4360b0fd14c59a55666 +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/d10ec63510dc1a043ee0a4e37b49eacd +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/54c393208d1f51661e631cba62a21c0685fb58827067d5ea7c42fb3d6dd8c8db99d8ee1b3c304abc25510bcb0265d86ca03e1ce19be4faa252d97cfc8a1b52cb +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2c1e000206c9e7c6c8e7515eb8115e3e +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/12c0ead798e43448a30699b5386b3d88aac49aaef9bae283ea6d089a1c66df7293f4f220a2b5c3d96e73e556e37e745f38d81f5c68e09a86a2b19a6695eff460 +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/21d6c5d5e422412b88ffce50862efb29 +LLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/5e8e17ba79134e9752c7fbd28b62e4616574a5e1dfcb0980160a3aad28a2f6cec4e48ed1acf73ca1f94d74397f7ee3eba53cb1280699e40c451295590ede3fe3 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/293fdc43431493f915a3e0a5b3c6d587 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/27e13a4334a3bfb3c91fd06abcc4eca7a347f4bffcbce40834302d153ef29756295121b42ac433c266668af1428ffa08ed12ce75f21fef44cd7ac1d8bdfd155a +LLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2825dac8280d0563b7f521a9eb8c0563 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/7f4549ac7b63e58d8c149f6b22bd997545713477a1df3b32adf640f3951580df1645f08756d9ba80c479160cf5759e3f9372396655a35cdca14f4be4afc4ae22 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/0c0da0eccec4a092fc0e9a915716ed6f +LLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/e538e29c4d52d9aaf151670619702541fed8231ae4c7fb9431a425d10eea95433087034a37da8fe468bd27a1c882f6f8eb9549ef71964124db10e99f4b402ba5 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/6b4fd19277c978306441da3b58ab86a1 +LLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/6216b3e1dc6aea979d8b5abc4cc0faf510e4e64441b1d18b4b36c45d65e874e9046e14eea67efb88f3219449ef048d34fcb751b15c59f8a299aa822b426d50ae +LLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/689ce55ca1eb1be8090a7dad2e5f1a86 +LLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/a2ebd80e71375abafdaa45d4d104c1822d2205bd680b8c8541aa90dbc54d530e348a64a18acfba14cb66c078f0386d54375bf26cddef935a348e874b99609312 +LLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/dbb26e6bd19d71607248446d38ea0a42 +LLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/eecaafa95e1df14f57f93e44732a23b1fb734af73bb533c8b4662dd0ddcfe696271571b97e2a5346581c000336f9fa0b28bf1c92535490e5174649a7e01b6019 +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/51981c5aac875046101670896de92c2d +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/466da0868068d27dfa8284a3431925c9cfed9314f681bbadd0c331ae67a1acb975015a739abfea239e7f93a2fd7d439601f5d8421d7fa4fcceec5730649686a7 +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/65da06ac7ef16d3e3ea6137cb9a943f4 +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/6c70bcd54d1cbe502b7d9db50a59a62a8a10e4e90d7d607d61ed7737a70474aba2db5f5151b1dc03f965a84d8770d4be6f248ed1f4bc6c9e63298abecb936f1e +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/0a4cefbd15c37cb418cfaac56b789146 +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/7fd5c69bfde6264ae4e548ec9c399dd09b1a5fe4b9cced23d6bc4257f0f67874b838d53ee8d6eef7fc01ee9d086758e06f00bb0a0388b97de2eb85143a47192a +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/da2430483844823d31bcc5f302252ac2 +LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/19e9168b44d40acdc0d924e16f93c315237207a4441ae78997c511135872e557f654236bc859453069671145e81e961ac93c9dfa601d1b6631b9ccfa09b929b3 +LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/md5/d8584e0e3dc26ea7404d3719cea9e233 +LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/sha512/7a0396eaace91b9b4d013c209605d468a7ff9b99ede9fdd57602539a6fa6f3ea84a440f32840056a1234df3ef1896739ea0820fee72b4f208096c553fc54adb9 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/md5/d6edea561b61173d05aa79936e49f6b7 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/sha512/9fbe29ec6a33c719bc9a4dd19911ceded9622269c042192d339a6cf45aa8209ad64c424167c094ca01293438af5930f091acba0538b3fe640a746297f5cc8cb3 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/md5/3ec68c87e4bddd024ee0ca6adc2b3b96 +LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/sha512/be3cd9d5510c2693dee1494c36c479d32311ff83f5b2d31c08508a3dd370788961ce46e9025afe148a0febd05942fd294370a357dd717bee353d8a108617f6de +LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/md5/8ca5a926d69124225d485d679232a54f +LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/sha512/353f540b342bc54877e7a41fe65c9eeac525fd91bf4cddbe1b3ec2ed93c3751beaf8316a4d31530502b067100b160301262e10cbe4407db3abf1ceb5d9a74eb2 +LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/md5/4e5b576958f2a2e708eb5918ceef0de0 +LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/sha512/2e98c472d3ee25c2e062efa4eb21ac9cfc49b26ea9d99ad4a8e7660c4c09f121d31193bd161f54ea332ce94785d601897311e9e6668adb1e25e2b666e0d5bb3f +LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/md5/1c81a886e799663ce8d04400c5b516a9 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/sha512/236b78b9a17eaae74ab07349ac8dde16c3abbd48e0d075abd1c195d60efff48e2fbf799554df114ea3d3dba937e0369430a2788bde2a1201126e026ef6cdac42 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/md5/0371f43ebcb571d0a635739252b88986 +LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/sha512/605318ae3737e26ff89d6291311a7db3bc3ec7c8d1f2e72ae40fd3d9df0754ee2ebfb77687122605f26d76d62effb85157bc39982814920d5af46c124e71a5ff +LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/md5/cd3f1cdf404b6102754ced4bd3a890f6 +LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/sha512/65fe2c5b1e04da1e1d8111a0b0083fa0fa9447eaea7af7a018c09fe6d5506566c491bbad296a7be8c488ca3495016ae16a6879d69f057f8866d94910147dee03 +LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/md5/abac9b416d2ba5abcf5ce849f43ffa96 +LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/sha512/fed677ed6f103c56eb9dd4578fa37a56ed2a4bc803aa1997c5af19762a623d2f82db1f72f429448d66fcef3b37af2104e6cb782f023aaabef086a921a862b042 +LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/md5/4c71ffd7c8cabb1c0ed6290b193883c5 +LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/sha512/6b1421a3268170467225112167cdb33fec962181993a2dad5594d4ee0623ac88ee0588cdc7d0656dc1cb9129ef96f621a97a224731cd161134d7d63c8fd32c16 +LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/md5/06faf505f0dc354afcd01113cfc57af2 +LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/sha512/1f9dfbd403e2ce121e126c217baede178cb1323012bb5e3cd1f778ff51e4216aed9dd69036e2baffbd60a6f5ae438ddaba6c13809459e94bb00be3f7bfc8c30e +LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/md5/516a11d99306e3f214968a7951b07a06 +LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/sha512/885738599bbd96f20083f9b9368ce3f243bd5868d3ac9a45189de6cb40b6664a6dcdaece159989e504670231db8c2addfa8d544003eb0cdabba960e4ab6a4470 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/md5/d851b90ea3f9664774316169fc494e21 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/sha512/a1f529454f0881baaa508481ba97ecffb040fa92141b4cbc72278adcf8b84f0766fa918aea7fb99ce690c4fd80c36fec365987625db42f4e7bb36ad24ce177d0 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/md5/dc4e86eb2effe1f6cb0d0ceda635f226 +LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/sha512/c52de384853890f9df81aa9e422c1ba3fde12b2ae9c7b60b9ecdc6d0c88eab495dd336af2b6cd2c31d6eddcd0a213954eadbc7884bc39ce2039cec672eac32fe +LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/md5/8477e3624c73a820d8ab82a53e1e10fa +LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/sha512/32ce031245a5b59a779cd77fa3c9bf05ee59e48c913b75d4964bea49f37da232c59a42ad993f7b5edc88322148c1d7394984349682bfce3b69d33a51756ac8e3 +LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/md5/7be93eccbdb0aff427c43af651073d66 +LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/sha512/89a61a81ec664c72107ac09e717200b00434350bf77064267180bc0c101a59e0ee8c8af4dd6fe75eacdeb14e82743c138b2fc558ca08550d8796b8db93f89da4 +libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/f7ce9539d0802dd4b5e5e673d36d1a99 +libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/7a54be16ccc327731c802380d29f2c9ee5e635cd6af0b7eb6b69e9d3b0b4fecb74147359af182def3b016ec4445891bdb91eb0d541b783e451e8263968c25161 +libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/cd946ab46745ce71ad7438cf0f30cfd0 +libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/sha512/15f8bcdf6f66e654d5d6e950392ced62586e2bf7c2b0845db78282669c5440c2140432950c7726fcc8910c7113685cc29ac880de565f85b77536d63dbab0a8b5 +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/766a2de98d275877bb676ff1f23e972f +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/3b353ea038fafefc13ccb4a81c7242d569c206362605be374fb312cb495f385796d052c3a7e08c7fe6ecaa3018e2a7e3dfa43d71a8c3a94987f7dc7aa378fd22 +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0684a6b210b799a8a0f45a286f3dfcc5 +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4221e2d74117bd7e89aba2945030c1507e51999b236814fd23036565364c328392e87032daf1b9fe274ed89fcf9a6dcd203f0f1c8602c2a08d3fcfa189a5fefe +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/6b460256e923637e5107d67859eb60ba +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/7d3f2736afe4022842529b1355cf9914b7a1c7b1e261f814a4523ad30a0cf0189056d5117a06720bbb7a844a435bb632ddbda2daadbf7e01c0120452cd13e6a3 +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/c2b13a6a296adbb4be91dd3bb5be0877 +libLLVM.v18.1.7+3.aarch64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9086937e718125afd535b0066ee08a3523161a94fa7ef3c9a3e86bfe760f251b6ea7b035888e61a0e7f192ed25c9bd0f4dc153df86e08569e7067a7a30ba48c5 +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/758d33fe0b2b3d0371708614365450e8 +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/79a662f72ba1b89b373d1d143ee880a12cb128211e79182e7befe8b3e50298b594de2ce489ca8bcdeadb17fceee811622f8bfcbc3e232cefdaf9927177469eec +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/2dcbb811be8985bfed3c8b37733c0d40 +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/17f6fbd96ed5029f360c101cedad127881e14b42498d66f717448d99ca1909057ae79169d934e08157edcc7467db4b3941bdda26a2e9f42645963eec51f27e29 +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/bd3b904b5f9464aaaf87c41b899c8ca5 +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/fa99e8025419a18f548f658ea589771c2803480c3cb3a25cfb75e26ed0993b7b37bba204d7cba1475319a71159813b2b58a3b3327ba24d264cf80ef24263628d +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/b4f9038d5c3c13207111ee1a9a918cba +libLLVM.v18.1.7+3.aarch64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/e8b97bee30f597cc06d31175e12f0c2035aef0054e8abdb431f31b1e9d440d561bd9bc6637a403441aa7f3e1d2a46c600734e17e3b7ed0ae899c92df91758780 +libLLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/1f59987d027a3bc930fca6bef917f739 +libLLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/7bd0532e11abf1c4979e59d513257d53ea940f15c08d2fa30dc16e59e11d1899dcd2abe4a35dd3c7719aa49aacfa1b0e49049df3548336e5ec64355319129b30 +libLLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/md5/e4ff6f08094846700acc4e55d5b79e93 +libLLVM.v18.1.7+3.aarch64-unknown-freebsd-llvm_version+18.tar.gz/sha512/8a575e9640e5ff9b75ef4e970f203139e51afbcbf1b82c774fbe4a0176c22c51029533c188fb89068c1714eb3c8b1b232804f276a68c0c40aa0a6611ae72d1ce +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/06d8e634b4a6914efc18b7962df52021 +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/cf6aeed1eaf652e5830e34dd2ba88abc33668953281146106bbfdbc92f5f225645f00ff5b4a0eb902baf904362ab4eb32192fa50ee5b2672e8b031fe2550f9a8 +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/53e83804b63e6ae4d0f1c97abcbbd1c8 +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/45b3ee9b105ef2ef106fa8ac7b8e902cd1d6bf3c9bfb57edeca9e14f1654714d23fb086b369a9fd3cbb828c04fee4cfe80d2b2a2bfaa852d3ac65c0d213d8c62 +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/91b6cf00564053d385e30b34e5b8778e +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/9111f3f02b49bf78340c9b0c5c1325a1ca09b62c83aefece1121573dcc21dce095060351f18997971e5cfbaab346cb12c75cdc0fbe8fa92aca2e8a68b5f5f577 +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/f6c91b71dfd73c7301a4e3de48e072de +libLLVM.v18.1.7+3.armv6l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/581d7e1e4d85aeaf082fa31555074471705e391de0771bf66665807afb5192c79c481ca30e73a25f4e2d48d4d325f0198e39bcbfaed2c9bc7477ee917667f5ce +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/ce41ee46959e5e3a17b6c99293afedb7 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/73d8c5af750ea9deef822aec58d8697243ca154bc4435ac0b0ab8c90fc97750e91fa55f8de7b8283eb1ab19951cda3e3c4c60834bcf13730163e593126a8eb57 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/67ed5b654852dad400aef17fb542703f +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/07f70c57e27eea37f520f6f0a954b54d2506530d5eb5a74e5a8526ba8ef55a948073c49037544b602d03d0aa482704292eac943f0a83421386ccbfbf22ee8510 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8bd88d49ce21e5b63af6f77782eed4 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/cef1c561ae388b2baa08e39dc195989cb795d8a2747f5f11e0dc9d9e107b9e99dbba465335376beff2e1b326512f6afc962775e0b246f3edcfadf509235cabd8 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/5fbf26d20b2ce3f61edc9a9ca2eb5284 +libLLVM.v18.1.7+3.armv6l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/2c564c95d648458b9a0f0c963246cf5564c625107682f680390b6db5fde0e2b15a964fd3fd23734b5b2bb135db1fc698812d61b3f275710593f4defaee4a9c23 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/c81bc29a75acf4f806f3eb13bf890604 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/c8c922a0a4fefd549f1c2ba396a3cab9cf7738aa82e7ccf7ca29c090260e2d73ec45d6f2b07173d584f6074b10fa04052114deef6ecb6f53ea87f1924074137a +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/md5/1fcb40ba1a427105b4e7d13a6c11dc78 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx03-llvm_version+18.tar.gz/sha512/392c9ee85ba7ab6697bb8979c7f443d1d25f7ac9178e96a886401cfc68d75a43ce98bf3038a7ba70a9a990f65e604d38e043472cec3badb25fbd1b38cfbb7162 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/427a19eaf69725d11bb33f48de9cb205 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/542e209b10c13d8dca867247a7414f84adb832f40051fcbdf0dcb09bc9664a77248e1b0ea1687805847dd9f5a05b86475dd76aba427c9a1bc83f8502444c60bd +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/md5/ab34bfa2950014936edd13a7b5db8170 +libLLVM.v18.1.7+3.armv7l-linux-gnueabihf-cxx11-llvm_version+18.tar.gz/sha512/6376b25d0278e5c97581480fb4d54371b09a08be88f4cc39d2c7b3875f1189cef60c1be6bea5e12b0cf306cef8b394bc7d00f8b0fd95d749bd1b4eb318af7e15 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/md5/cb6300fe87fd7cb9840f3bc44af26878 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.asserts.tar.gz/sha512/a7984cd90fef55559142fc05d91b0da1f37f77f25214e93ff7641b7c3958f08dc7c082611915dbfda4bbbaa392656ac8604d4f75369777dacfb78baee2f99b16 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/md5/b8a4e8ef43340e9cbdf5e4479c6a5a56 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx03-llvm_version+18.tar.gz/sha512/fc249f2b666c8a8129e05ea08c773cbeb7af6d37791f271461eedd99adcfc5082e8609ed096d8a46edd1e73505352712a41e0ddc247a371f78227aab01fbe0f3 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/md5/5864689df3298be4b1b4df1ae0412d3a +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.asserts.tar.gz/sha512/8f32f73e366c3a6993fa8d6b8cd1a9391611b0644cd4a77a4f7a235c037fdb75308d99b5a23ada6e4a73ed5fbd8f929a981d6bf317d79d52396220c221619303 +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/md5/6bf798476c4e94716cc47a95580104ad +libLLVM.v18.1.7+3.armv7l-linux-musleabihf-cxx11-llvm_version+18.tar.gz/sha512/9dbd27a000dd3c3dda9047d366a667c4b179cc61582525adb0f8227e8055413ce46efcbc1530305400239656e2f1016fb8833fb7f4734714078e035d388f3531 +libLLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/66e2889f86ae6bc1977419e6d9be729e +libLLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/d0cac798c4979b4d818d36596b173e523cba3f41ff7ab1e2111f6a75c3e819e563e207a547328f005c5a93c7f8f88c17bf43c1139b5c2690df4f1d719f82920a +libLLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/0534b72d6d33c8573f79dce8a2a5a6e6 +libLLVM.v18.1.7+3.i686-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/6beaf1b45eec8b46fbf92f692f53e6df40bf48e50589aeb5ef99240a5a3ec9089ffb350dda6df24530937d613bf6d2cc4da76e92921ea00def9d2d38ac5bbeba +libLLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/2cf9a1ca20472179ce4a9eb3a949457b +libLLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/cebae06ccee12a14d20d3056ce0519b1e774e3c9d9200a783262fcc40aee6d7aabfb08714bf53b88e03d8b09a96d3cda248a70c16188f8c707b291642998262a +libLLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/4712f6a46e0ff407ece958a7701511b9 +libLLVM.v18.1.7+3.i686-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/9a0a2dfa2076b93027f766277a6890cf94d67c131697f74945e92cf13ae64e84c09d3dd744498986fb22ad5e5465300aa9c8ae6632fcf919a0932515edfcc1e6 +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/274c51cc4dc133d7470ef82987b78df6 +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/24944b1fec24bd21f2f773480c7783975b2cce5ef9909f285c959d954669b98ae18a174126440c03de28d1fa9b055f4bd092104dcb29d8c0c07400dd8e4cb493 +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/8b36d976399e4b603a1c4f8bce1510fc +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/4f5a1169cd566898357c98f86786bf86f6f1d9282327f8026c7d04359fa7148f4026ef2de765debfb45d4013368cbf420e78802289ceea253a9ed2f58e89db8a +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/121a0c243591d8295fd3063821569e01 +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/e55fbf36802e7d8547e1aa0f60c650b29cc3dbeaff67e6b6a095e0647d6a8c6f55bc7cf72daaeb6f3d2e87e831b3cb275d8c3b4beea2413de8a1cfbac4771ec0 +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/7af4fdf2475dcf896750e046edc9fd2c +libLLVM.v18.1.7+3.i686-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/e8294e855565109e70d0596402dd8b7886174034242cbc6deb55f481a306c85ed9840732b3cb346c2ed5ce10a3d42647f2d1a97d2e998805089533880a326197 +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/bbf060d61b294b86f7e3dde381b00b8a +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/632372d41f6e400a10fae27c6cd06a5a344cfb5902cad7928cb4133f14f36f0a3373e69e73ce9baf52f518340593c3a5a16173ef59a1878e6300e9975aeaa157 +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/3d730b713e01cdb5a7a5a46028afd41b +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/052ab4fa7ac3b2c430601753ab078cdc9fd6db7f65ee0b76bb05473f4c5b99ec8919ad9d347425f1928cf619548e992c86ba97f9994218f50bca617e43d2f0d9 +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/bf9dcb92ba8c031ae62ed4434fd5447f +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/e53be14dd02a2cef8eccafb9301d29c51d652c635703529c1444947002993f6639083eb8bef13af21c9796717ce4b3129dcdcbe2751a1173d39e321db8f6e3c7 +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/b5cab0fc7c6643c6dd161f1e553ef1a0 +libLLVM.v18.1.7+3.powerpc64le-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/4032634449e2669479761c4323096b152f8df4948e3a97eea10f0b400fbf2a00d1edda59b74a714b62c4e204b113d8ecda78d828c3344ebe8bd750d14b3c4c7d +libLLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/9f31ae627df95fb4818d8bb96e17c941 +libLLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/da67146a80ba3615e5e46455144c5f4a25919e391aadd3d63c9c645b639d68f8883a61e947b767f4583f666e653721c53d5d4098c8af2abd81691f941fdde686 +libLLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/md5/55fc5ae75087cb1ff1f08a1ef65f8b94 +libLLVM.v18.1.7+3.x86_64-apple-darwin-llvm_version+18.tar.gz/sha512/a000c0e349722f6b0196cc9a10aff8040dbe6a679bd79787c96c1de76968df636ab79dc24a31e4da960502858514fd74c3586c37411381d7ca68c5474576f7e0 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/md5/69564913bae176a167d24d3291ef7af7 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.asserts.tar.gz/sha512/b8eeb86b66d767218e59671bdd597623238eea72319913c2ac5e116faec3f4c13739a24f3b95338ed857ec29e714dc0308e4ddbfe359332b3c27ad5235052342 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/md5/bc9d5637fe30f21d2231a98371e798e4 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx03-llvm_version+18.tar.gz/sha512/4efbc2823322abe80d0134d35926767bd9cab717cde9308726a6a8891e5a707476138888c695ed399e3dddb57baf17abbc43a0a338cea2e5c0f472ab427c12e3 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/md5/8492ff91e6dbd1a66edd8aaf0390a582 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.asserts.tar.gz/sha512/6443bd2fa9c5beecc2b002c26595f2cf3a8e2ea5eb49aa4c00f7252a6623fe0f8c01824941ebe5475460641285c4e56a5203056c1b93a78250b7e48fb5ac9e00 +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/md5/6918c9978fd8b5887c66eee76950478d +libLLVM.v18.1.7+3.x86_64-linux-gnu-cxx11-llvm_version+18.tar.gz/sha512/d455a4f433bf3ea1b5100b9d45199bc785e4b6fbc7659bf06cbde6ada471134e7d4243d3a3a1f71d579126ef8371d70e59f174e124b3ff8d4842e9ee83e2dea4 +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/md5/075f87d106dd95c8e9c6e7e157b5e9db +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.asserts.tar.gz/sha512/8132379d8f44a21082c7a90f58a7dffb0c6ee725efd58a959d4023787411b080d72913bb1e89a35072f97aaf1ca512ab1d027b37eaed819e3c053d7a0cf64269 +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/md5/4cfc2838a77f05883f82e50b3723dcfe +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx03-llvm_version+18.tar.gz/sha512/20079c81cd6a4020b087485be1ab4928b3bd3e1a53728cc98137a35b969484278093bc75a9e51ddfd8331556577c5fb3109d74dc2eccffa93b5390e0fabff2b1 +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/md5/5b8cbf00631bd4540b7335a86302a1fe +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.asserts.tar.gz/sha512/51ba9a4b74b740905cee4baf7f4e5f3620ed81e0746f49cd352d874ebedab95277c5031123f880c9239b7dbf505b10f6531f79c8a6b0482a652b8324f4137cf5 +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/md5/11010cc2d58b1a8c6a6e7bc24df0c0db +libLLVM.v18.1.7+3.x86_64-linux-musl-cxx11-llvm_version+18.tar.gz/sha512/a6bdd9a2a2fa9a572e74ced69c3ce9d1b84cde18155ec9bc7dfbaba411ee6c43d229e6fb333eff66fb63b632b485b46b7cb1657c0c49d9d9bb849fa13f0bbc7b +libLLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/md5/566390f0f0fa92c4a9a400e25e7086d0 +libLLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.asserts.tar.gz/sha512/31981cc3be65117d8dfcb0254dcdecd79b0f141a61864db4e50b81fbe7a1db431b71f9ef43bbeb320e4ae33bb00f2db42d83f849ce6ca5044445cd5de9572566 +libLLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/md5/b753aba58a0704da416bb06cd97acdd7 +libLLVM.v18.1.7+3.x86_64-unknown-freebsd-llvm_version+18.tar.gz/sha512/99358ace0ef20138284c3f8b28b46dd431b460d1c92034fc918233a266c9be398eba63d1758a388fb39935123c65f72969e01231e54b27cff771cdabef9171c2 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/md5/52cee10b0dd37d9a4487d3762e1902c3 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.asserts.tar.gz/sha512/c44d305ffcb2939779a071a5a78ca9469654e36c5e4cf3e0e78603c85ec30eae3c8ab2594df19812d51dba7cea565c16a70f514faf30bc43b8f37592f57aa059 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/md5/eef5f1bc5a0026bf96f33e2254b93711 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx03-llvm_version+18.tar.gz/sha512/df39558259dd59f7b602581e7afdf67e77c854c1192b53b24a5c2d133a4a74b3f44e74682f9f02745ef97a969de92566a7633c46816a031b14cb04006af845de +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/bbe95b31b958f187d49692d4856d84af +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/3035b3b8b1cd1349c893aa47f066a1b8b7610f69ff0c4f2f3325a377818fd8bb12ad5485730be354bc2a9982db405b5954dbda39bc7cff38dc22966a6d86c5d5 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/0e21a6d22dd45d125d0e98fe8f72e8c7 +libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/efbbad538c6f8b773d7ef1019a9b754e1ce7da59ea5f00f452fa7f7cc93c40f248762eb7f708e3d2fa7f9bdbc0b680d6e6502a07bbca0d4e701b51b0565d625e llvm-julia-18.1.7-2.tar.gz/md5/5c0ae4abc4ce31a86d5d6d4ecabc2683 llvm-julia-18.1.7-2.tar.gz/sha512/b4d1dde929a8670eec1a9b25abe23fbc926a922e61b60ed99b52b440cd07cb026e7f746878292db4cd0cb422d9b87ecc4ee4b2b141f8e9411855d18da51facb9 -llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b -llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 +llvm-project-14.0.6.tar.xz/md5/0b3373eded268dc27e2e874872fed4eb +llvm-project-14.0.6.tar.xz/sha512/6fc6eeb60fac698702d1aac495fc0161eb7216a1f8db2020af8fccec5837831f7cc20dc2a169bf4f0b5f520748280b4a86621f3697d622aa58faaa45dbfaad13 diff --git a/deps/checksums/llvmunwind b/deps/checksums/llvmunwind index a90d28717dd85..e69de29bb2d1d 100644 --- a/deps/checksums/llvmunwind +++ b/deps/checksums/llvmunwind @@ -1,32 +0,0 @@ -LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/md5/d8584e0e3dc26ea7404d3719cea9e233 -LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/sha512/7a0396eaace91b9b4d013c209605d468a7ff9b99ede9fdd57602539a6fa6f3ea84a440f32840056a1234df3ef1896739ea0820fee72b4f208096c553fc54adb9 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/md5/d6edea561b61173d05aa79936e49f6b7 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/sha512/9fbe29ec6a33c719bc9a4dd19911ceded9622269c042192d339a6cf45aa8209ad64c424167c094ca01293438af5930f091acba0538b3fe640a746297f5cc8cb3 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/md5/3ec68c87e4bddd024ee0ca6adc2b3b96 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/sha512/be3cd9d5510c2693dee1494c36c479d32311ff83f5b2d31c08508a3dd370788961ce46e9025afe148a0febd05942fd294370a357dd717bee353d8a108617f6de -LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/md5/8ca5a926d69124225d485d679232a54f -LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/sha512/353f540b342bc54877e7a41fe65c9eeac525fd91bf4cddbe1b3ec2ed93c3751beaf8316a4d31530502b067100b160301262e10cbe4407db3abf1ceb5d9a74eb2 -LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/md5/4e5b576958f2a2e708eb5918ceef0de0 -LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/sha512/2e98c472d3ee25c2e062efa4eb21ac9cfc49b26ea9d99ad4a8e7660c4c09f121d31193bd161f54ea332ce94785d601897311e9e6668adb1e25e2b666e0d5bb3f -LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/md5/1c81a886e799663ce8d04400c5b516a9 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/sha512/236b78b9a17eaae74ab07349ac8dde16c3abbd48e0d075abd1c195d60efff48e2fbf799554df114ea3d3dba937e0369430a2788bde2a1201126e026ef6cdac42 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/md5/0371f43ebcb571d0a635739252b88986 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/sha512/605318ae3737e26ff89d6291311a7db3bc3ec7c8d1f2e72ae40fd3d9df0754ee2ebfb77687122605f26d76d62effb85157bc39982814920d5af46c124e71a5ff -LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/md5/cd3f1cdf404b6102754ced4bd3a890f6 -LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/sha512/65fe2c5b1e04da1e1d8111a0b0083fa0fa9447eaea7af7a018c09fe6d5506566c491bbad296a7be8c488ca3495016ae16a6879d69f057f8866d94910147dee03 -LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/md5/abac9b416d2ba5abcf5ce849f43ffa96 -LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/sha512/fed677ed6f103c56eb9dd4578fa37a56ed2a4bc803aa1997c5af19762a623d2f82db1f72f429448d66fcef3b37af2104e6cb782f023aaabef086a921a862b042 -LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/md5/4c71ffd7c8cabb1c0ed6290b193883c5 -LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/sha512/6b1421a3268170467225112167cdb33fec962181993a2dad5594d4ee0623ac88ee0588cdc7d0656dc1cb9129ef96f621a97a224731cd161134d7d63c8fd32c16 -LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/md5/06faf505f0dc354afcd01113cfc57af2 -LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/sha512/1f9dfbd403e2ce121e126c217baede178cb1323012bb5e3cd1f778ff51e4216aed9dd69036e2baffbd60a6f5ae438ddaba6c13809459e94bb00be3f7bfc8c30e -LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/md5/516a11d99306e3f214968a7951b07a06 -LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/sha512/885738599bbd96f20083f9b9368ce3f243bd5868d3ac9a45189de6cb40b6664a6dcdaece159989e504670231db8c2addfa8d544003eb0cdabba960e4ab6a4470 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/md5/d851b90ea3f9664774316169fc494e21 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/sha512/a1f529454f0881baaa508481ba97ecffb040fa92141b4cbc72278adcf8b84f0766fa918aea7fb99ce690c4fd80c36fec365987625db42f4e7bb36ad24ce177d0 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/md5/dc4e86eb2effe1f6cb0d0ceda635f226 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/sha512/c52de384853890f9df81aa9e422c1ba3fde12b2ae9c7b60b9ecdc6d0c88eab495dd336af2b6cd2c31d6eddcd0a213954eadbc7884bc39ce2039cec672eac32fe -LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/md5/8477e3624c73a820d8ab82a53e1e10fa -LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/sha512/32ce031245a5b59a779cd77fa3c9bf05ee59e48c913b75d4964bea49f37da232c59a42ad993f7b5edc88322148c1d7394984349682bfce3b69d33a51756ac8e3 -LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/md5/7be93eccbdb0aff427c43af651073d66 -LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/sha512/89a61a81ec664c72107ac09e717200b00434350bf77064267180bc0c101a59e0ee8c8af4dd6fe75eacdeb14e82743c138b2fc558ca08550d8796b8db93f89da4 diff --git a/deps/checksums/mbedtls b/deps/checksums/mbedtls index 2db4d7fed384f..e52066b6f4bac 100644 --- a/deps/checksums/mbedtls +++ b/deps/checksums/mbedtls @@ -1,34 +1,36 @@ -MbedTLS.v2.28.6+0.aarch64-apple-darwin.tar.gz/md5/c97705b08c6bf695fa7a11a42167df94 -MbedTLS.v2.28.6+0.aarch64-apple-darwin.tar.gz/sha512/91825c3a495045ca74ceb5a23e3d7e9387701e401911b147d905a49892b1a5a9f22662a4f16a7f4468c5a807f2980b66e3409ea1ff7e04c6fdac0b105472e200 -MbedTLS.v2.28.6+0.aarch64-linux-gnu.tar.gz/md5/8ebaaeefd75c805227229086c262d0e7 -MbedTLS.v2.28.6+0.aarch64-linux-gnu.tar.gz/sha512/89983c1f9f9d7b901619522afcd12c6bc1996757edeb9f3012954992f82f3b36ae50f49dcf7731623fca197946e4281eecffdc29a5819f04e7f6203afd4eb93a -MbedTLS.v2.28.6+0.aarch64-linux-musl.tar.gz/md5/b40b2ba247f4ff755e15daad13c5a255 -MbedTLS.v2.28.6+0.aarch64-linux-musl.tar.gz/sha512/4cb4f2213b631dda0caa8baafa8effc9c8592c72a6a5b826fce060cd81f8f77c188c9ddc76595b47078db3c35b3043d9bf0cb891d822a940df87982de56dec44 -MbedTLS.v2.28.6+0.armv6l-linux-gnueabihf.tar.gz/md5/c6dd1cb1aba1075c73c41719a03c5ab5 -MbedTLS.v2.28.6+0.armv6l-linux-gnueabihf.tar.gz/sha512/981a8925dd90418150625e9467cc791e4a9d5223e7df6ead113ec41a279a5dd7e8ebcecb5b87611ef451fc6483fd6eb5bf984cf528037ad742e68b4be94e5c07 -MbedTLS.v2.28.6+0.armv6l-linux-musleabihf.tar.gz/md5/c30ed777bd74d269656f7e9bc8163765 -MbedTLS.v2.28.6+0.armv6l-linux-musleabihf.tar.gz/sha512/f04014181082561195caa4d3b178480bb5cce7f459d76aca8cdaa2f615d105b24871656ce4cbf8d9ec33f0424de35a16f12d4964a1f0fab9a416e5d18a468c94 -MbedTLS.v2.28.6+0.armv7l-linux-gnueabihf.tar.gz/md5/256f8327773ea2d0d6b4649541c34e84 -MbedTLS.v2.28.6+0.armv7l-linux-gnueabihf.tar.gz/sha512/ab4c9e82752386a0fd642a709bc90b712d6aaff78309968f1fdbf1121a790a9c0227ddd8e79373359cea9c75b21e762f600abea42036609571ba999531b50852 -MbedTLS.v2.28.6+0.armv7l-linux-musleabihf.tar.gz/md5/249ada3e9a7ad4eba08270e575ae68ec -MbedTLS.v2.28.6+0.armv7l-linux-musleabihf.tar.gz/sha512/0682e65f4257c3d237ba8cfc643be4430341888ec4cd17c2dc3018350aa7ff176e834a69ebc9d240b383a7aed439b34e45c237310ad66043956700b782323793 -MbedTLS.v2.28.6+0.i686-linux-gnu.tar.gz/md5/d0a176d2843ac780884395c90971bf68 -MbedTLS.v2.28.6+0.i686-linux-gnu.tar.gz/sha512/c2f96f314d0e5d9bffe46dc7d0adceb038db81e8c9d9a3c0fb0a237849d0d568d249e2df6c275d27a74a9122d0a53b38e5d8521807669a9c82bd67befbea169c -MbedTLS.v2.28.6+0.i686-linux-musl.tar.gz/md5/9c7501c6b04df53f8d56cd59dd42ae4c -MbedTLS.v2.28.6+0.i686-linux-musl.tar.gz/sha512/6fd35f9c2e1c5822920bc1d9315dc68b10694ee5507cc512868615c3d35dc389fa67038b9ab79fa86ea7ff6bf5f6f1eed053fafcc519080559057dcaff813ec5 -MbedTLS.v2.28.6+0.i686-w64-mingw32.tar.gz/md5/1eef46b3c89a81973778817a8856673c -MbedTLS.v2.28.6+0.i686-w64-mingw32.tar.gz/sha512/f202595cf971825601d5e12263eef0dd101e9be971d15592a12187f1d170fafaab358f02db89458f495ddc8922f66fbd662123b0d6df527fffa514e9f410784a -MbedTLS.v2.28.6+0.powerpc64le-linux-gnu.tar.gz/md5/fec1779ff02d71d5e94b3f1455453fc0 -MbedTLS.v2.28.6+0.powerpc64le-linux-gnu.tar.gz/sha512/e97ae38c555f6b45e33c023c7e07c982d36501f6c2dc36121bb73f2fb08db3fa3ab7f4ab0d9ecb622d25bfe1816eab3a6190d2034a05a66b7425c36a637623e0 -MbedTLS.v2.28.6+0.x86_64-apple-darwin.tar.gz/md5/6d44a0c126affaedad544460da9415ab -MbedTLS.v2.28.6+0.x86_64-apple-darwin.tar.gz/sha512/bf074429f32f51d954bc0c242fb4455ec6ead0e8337a3e5ab9e5b0df47d8a195947a488169f743db63d70b245be80084cd0d78f2211b6cd4b9524010b2c893cc -MbedTLS.v2.28.6+0.x86_64-linux-gnu.tar.gz/md5/95641af7a92c8c83d82264dd2275692c -MbedTLS.v2.28.6+0.x86_64-linux-gnu.tar.gz/sha512/3606ecd5a566e643cc03959a3eac9a45cb4c644006ee5820b852dfc22d40b85d75f5c018c46776954d92001986ecb49238058ca3d99340f9a689875b690aa6e7 -MbedTLS.v2.28.6+0.x86_64-linux-musl.tar.gz/md5/aee58ac107ca0d9e1eb5d7de8146ec8d -MbedTLS.v2.28.6+0.x86_64-linux-musl.tar.gz/sha512/86219aa5ba3280da39e91beded7455160c1ebc274c3158b9f0703a2c034756a9a9e51e5354d22ce983fcd026157d81f471446e6ee2743cae2663384e3e796176 -MbedTLS.v2.28.6+0.x86_64-unknown-freebsd.tar.gz/md5/67857ac031b10fb6a0620b453477653b -MbedTLS.v2.28.6+0.x86_64-unknown-freebsd.tar.gz/sha512/118f3c662580c88d092610be08b60236939c7fd7feab4cd524c7c1e2e2e1b557bddbd603902b697142695889ea6c0a8087982020cd5e7267c9c7c82b49622460 -MbedTLS.v2.28.6+0.x86_64-w64-mingw32.tar.gz/md5/15ebd14ae435b64b2a0006ee7bc21bd4 -MbedTLS.v2.28.6+0.x86_64-w64-mingw32.tar.gz/sha512/7b327ecd405417a3be6ad4ba746656c9b25b70f09985e3e53b07416ab6f271f630eee638c98be938d5cb827c92b5bf656c02865685306389efba2275a1b2113f +MbedTLS.v2.28.6+1.aarch64-apple-darwin.tar.gz/md5/c97705b08c6bf695fa7a11a42167df94 +MbedTLS.v2.28.6+1.aarch64-apple-darwin.tar.gz/sha512/91825c3a495045ca74ceb5a23e3d7e9387701e401911b147d905a49892b1a5a9f22662a4f16a7f4468c5a807f2980b66e3409ea1ff7e04c6fdac0b105472e200 +MbedTLS.v2.28.6+1.aarch64-linux-gnu.tar.gz/md5/8ebaaeefd75c805227229086c262d0e7 +MbedTLS.v2.28.6+1.aarch64-linux-gnu.tar.gz/sha512/89983c1f9f9d7b901619522afcd12c6bc1996757edeb9f3012954992f82f3b36ae50f49dcf7731623fca197946e4281eecffdc29a5819f04e7f6203afd4eb93a +MbedTLS.v2.28.6+1.aarch64-linux-musl.tar.gz/md5/b40b2ba247f4ff755e15daad13c5a255 +MbedTLS.v2.28.6+1.aarch64-linux-musl.tar.gz/sha512/4cb4f2213b631dda0caa8baafa8effc9c8592c72a6a5b826fce060cd81f8f77c188c9ddc76595b47078db3c35b3043d9bf0cb891d822a940df87982de56dec44 +MbedTLS.v2.28.6+1.aarch64-unknown-freebsd.tar.gz/md5/51774d7907dc1a72d7c6e1b6cff02347 +MbedTLS.v2.28.6+1.aarch64-unknown-freebsd.tar.gz/sha512/b85292a75d4ba6fc3996ed497f0951f0dc0a3846e1df83f36b7d3ed3fc30687efdc1742848f6fb5a06e204fa9eb66837c8fbef16e6329f50763086bafef14fb7 +MbedTLS.v2.28.6+1.armv6l-linux-gnueabihf.tar.gz/md5/c6dd1cb1aba1075c73c41719a03c5ab5 +MbedTLS.v2.28.6+1.armv6l-linux-gnueabihf.tar.gz/sha512/981a8925dd90418150625e9467cc791e4a9d5223e7df6ead113ec41a279a5dd7e8ebcecb5b87611ef451fc6483fd6eb5bf984cf528037ad742e68b4be94e5c07 +MbedTLS.v2.28.6+1.armv6l-linux-musleabihf.tar.gz/md5/c30ed777bd74d269656f7e9bc8163765 +MbedTLS.v2.28.6+1.armv6l-linux-musleabihf.tar.gz/sha512/f04014181082561195caa4d3b178480bb5cce7f459d76aca8cdaa2f615d105b24871656ce4cbf8d9ec33f0424de35a16f12d4964a1f0fab9a416e5d18a468c94 +MbedTLS.v2.28.6+1.armv7l-linux-gnueabihf.tar.gz/md5/256f8327773ea2d0d6b4649541c34e84 +MbedTLS.v2.28.6+1.armv7l-linux-gnueabihf.tar.gz/sha512/ab4c9e82752386a0fd642a709bc90b712d6aaff78309968f1fdbf1121a790a9c0227ddd8e79373359cea9c75b21e762f600abea42036609571ba999531b50852 +MbedTLS.v2.28.6+1.armv7l-linux-musleabihf.tar.gz/md5/249ada3e9a7ad4eba08270e575ae68ec +MbedTLS.v2.28.6+1.armv7l-linux-musleabihf.tar.gz/sha512/0682e65f4257c3d237ba8cfc643be4430341888ec4cd17c2dc3018350aa7ff176e834a69ebc9d240b383a7aed439b34e45c237310ad66043956700b782323793 +MbedTLS.v2.28.6+1.i686-linux-gnu.tar.gz/md5/d0a176d2843ac780884395c90971bf68 +MbedTLS.v2.28.6+1.i686-linux-gnu.tar.gz/sha512/c2f96f314d0e5d9bffe46dc7d0adceb038db81e8c9d9a3c0fb0a237849d0d568d249e2df6c275d27a74a9122d0a53b38e5d8521807669a9c82bd67befbea169c +MbedTLS.v2.28.6+1.i686-linux-musl.tar.gz/md5/9c7501c6b04df53f8d56cd59dd42ae4c +MbedTLS.v2.28.6+1.i686-linux-musl.tar.gz/sha512/6fd35f9c2e1c5822920bc1d9315dc68b10694ee5507cc512868615c3d35dc389fa67038b9ab79fa86ea7ff6bf5f6f1eed053fafcc519080559057dcaff813ec5 +MbedTLS.v2.28.6+1.i686-w64-mingw32.tar.gz/md5/1eef46b3c89a81973778817a8856673c +MbedTLS.v2.28.6+1.i686-w64-mingw32.tar.gz/sha512/f202595cf971825601d5e12263eef0dd101e9be971d15592a12187f1d170fafaab358f02db89458f495ddc8922f66fbd662123b0d6df527fffa514e9f410784a +MbedTLS.v2.28.6+1.powerpc64le-linux-gnu.tar.gz/md5/fec1779ff02d71d5e94b3f1455453fc0 +MbedTLS.v2.28.6+1.powerpc64le-linux-gnu.tar.gz/sha512/e97ae38c555f6b45e33c023c7e07c982d36501f6c2dc36121bb73f2fb08db3fa3ab7f4ab0d9ecb622d25bfe1816eab3a6190d2034a05a66b7425c36a637623e0 +MbedTLS.v2.28.6+1.x86_64-apple-darwin.tar.gz/md5/6d44a0c126affaedad544460da9415ab +MbedTLS.v2.28.6+1.x86_64-apple-darwin.tar.gz/sha512/bf074429f32f51d954bc0c242fb4455ec6ead0e8337a3e5ab9e5b0df47d8a195947a488169f743db63d70b245be80084cd0d78f2211b6cd4b9524010b2c893cc +MbedTLS.v2.28.6+1.x86_64-linux-gnu.tar.gz/md5/95641af7a92c8c83d82264dd2275692c +MbedTLS.v2.28.6+1.x86_64-linux-gnu.tar.gz/sha512/3606ecd5a566e643cc03959a3eac9a45cb4c644006ee5820b852dfc22d40b85d75f5c018c46776954d92001986ecb49238058ca3d99340f9a689875b690aa6e7 +MbedTLS.v2.28.6+1.x86_64-linux-musl.tar.gz/md5/aee58ac107ca0d9e1eb5d7de8146ec8d +MbedTLS.v2.28.6+1.x86_64-linux-musl.tar.gz/sha512/86219aa5ba3280da39e91beded7455160c1ebc274c3158b9f0703a2c034756a9a9e51e5354d22ce983fcd026157d81f471446e6ee2743cae2663384e3e796176 +MbedTLS.v2.28.6+1.x86_64-unknown-freebsd.tar.gz/md5/67857ac031b10fb6a0620b453477653b +MbedTLS.v2.28.6+1.x86_64-unknown-freebsd.tar.gz/sha512/118f3c662580c88d092610be08b60236939c7fd7feab4cd524c7c1e2e2e1b557bddbd603902b697142695889ea6c0a8087982020cd5e7267c9c7c82b49622460 +MbedTLS.v2.28.6+1.x86_64-w64-mingw32.tar.gz/md5/1ca2c982712620941c4b0d731251dfff +MbedTLS.v2.28.6+1.x86_64-w64-mingw32.tar.gz/sha512/cef70c00c79e421ce92424bbfda259b4e233d7be3489db1b8cbac7e926d9429be6c88fb806664db60210427748810ea08117066480e8e17c60cb61485b639669 mbedtls-2.28.6.tar.gz/md5/768932cee6c42f7f4751362091ac56d4 mbedtls-2.28.6.tar.gz/sha512/a5c876489bf89908f34626c879f68e8f962d84b50756df17b6b75dfb93e08fe163ed3f32bf70e89bce9080d15257a4cbd2679b743bf8f2e2d7a04606c5811c05 diff --git a/deps/checksums/mpfr b/deps/checksums/mpfr index 050e9cbd8d5a8..4d029986663c9 100644 --- a/deps/checksums/mpfr +++ b/deps/checksums/mpfr @@ -1,34 +1,36 @@ -MPFR.v4.2.1+0.aarch64-apple-darwin.tar.gz/md5/816f9ff59070f21f1df2f310e2606c06 -MPFR.v4.2.1+0.aarch64-apple-darwin.tar.gz/sha512/dad9adba7a8867d1ce26d77efb5c33b602b920a2cdbec84ea58a054cfab3ab7df54d2bda101de72b71604e7844993f1e216b002ba092e69277d0764040216c81 -MPFR.v4.2.1+0.aarch64-linux-gnu.tar.gz/md5/c1e3c9619af6454d8adae9bcbd911dba -MPFR.v4.2.1+0.aarch64-linux-gnu.tar.gz/sha512/5d916492aa73d11e022a7ca3f31940ceb8f8667bdf878ba29d6256736a380a2f6a11ac90cd8de3f1d3454a79165db240a1b971b9794fd21692ed64502ec34b9a -MPFR.v4.2.1+0.aarch64-linux-musl.tar.gz/md5/8ada267e2d23eb0c65ab2d2df02362d5 -MPFR.v4.2.1+0.aarch64-linux-musl.tar.gz/sha512/0c7f18e6d0f3e2052541e3279dfa9a74eb34067ac4fea0b17ab805cd73010cc83f8d7cb4eda8f4a904da398268d1c0d638c35521a9f339f8c7c3b5f159f27277 -MPFR.v4.2.1+0.armv6l-linux-gnueabihf.tar.gz/md5/42bdb78eee83f496d7da699ad9603913 -MPFR.v4.2.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/edaa9ece1404a606d6b635406ad5e721c8d094ffa1c73ce19222afc2b4ea7b3b9e23e7c5589ae10fd9f4c4aefa265773bcfce6c510efbca57782115d43daeb13 -MPFR.v4.2.1+0.armv6l-linux-musleabihf.tar.gz/md5/2213207772b8a50de4768816fdc20e2f -MPFR.v4.2.1+0.armv6l-linux-musleabihf.tar.gz/sha512/d24debc38b8135ac5c10c4ea19de0c69126b6881940b4e182118e12cc2c7cf0aca2db065620f0cca636742da32eddec5bda3b4f449a035274f05120c977ed449 -MPFR.v4.2.1+0.armv7l-linux-gnueabihf.tar.gz/md5/a0d9fe20c9ff0027b6816ee0102b1f9a -MPFR.v4.2.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/97ce02898dc0d29a616048fd7ecee3100a710f7a30a21f2276c01675749034a5241be88bd46dff3dbf9ea0adca98a4357bd16e43fa9520e7a02477494c2d072e -MPFR.v4.2.1+0.armv7l-linux-musleabihf.tar.gz/md5/7898b9047c914b290b5928af5df63030 -MPFR.v4.2.1+0.armv7l-linux-musleabihf.tar.gz/sha512/cbefa9588752c65751630832417c1c42e4819d49ff9a505f61c2567ef4271097e585542fa898efd61409a43e439d827bb79f693a0937d0a3a427b39535979588 -MPFR.v4.2.1+0.i686-linux-gnu.tar.gz/md5/15fa598e5c1c723ff6cd2ad3ea51e437 -MPFR.v4.2.1+0.i686-linux-gnu.tar.gz/sha512/2ec4cf0c88363bc9fb39522bbcd6a9c2311c38efb166f604aab118fed39712beea68367ff5c4cabb2b7b3f5a53469414b8552fd22a70a637cbbfc936f0c4851b -MPFR.v4.2.1+0.i686-linux-musl.tar.gz/md5/6dc6a00d3ea22e2c60374d49926598d6 -MPFR.v4.2.1+0.i686-linux-musl.tar.gz/sha512/4a90356091b53d7238dda59f6e9c5c420614f16460dc67310e581611ad46a2dd3324d6164cfecf1bcd660b8f2e473f0afe137aac954c608b11be3acbda648e14 -MPFR.v4.2.1+0.i686-w64-mingw32.tar.gz/md5/bda99a916573607716c61473153a1927 -MPFR.v4.2.1+0.i686-w64-mingw32.tar.gz/sha512/ed3f45ff5ac8f4588584dd80036d9f3623651c87276a9b624955c831009dc33f8804c2845bd187ba750515725c29d65ac5d70c71db1b953c618cd771d2b066d0 -MPFR.v4.2.1+0.powerpc64le-linux-gnu.tar.gz/md5/ac70f716bddd5323b4add663b473b52d -MPFR.v4.2.1+0.powerpc64le-linux-gnu.tar.gz/sha512/ebb0f5ea76c892b7a4e4636706e71f476aaea58bb88e1734a7966c44495fda8c81318e0e8629e208185f0fc8d0c73b6f3463034cd831dfb5fbbd493a0689bc06 -MPFR.v4.2.1+0.x86_64-apple-darwin.tar.gz/md5/ff13e865e3be717b0fffc16296cb2f56 -MPFR.v4.2.1+0.x86_64-apple-darwin.tar.gz/sha512/98479210910945714da0285a40803674242581894a731ba4709c70dc1341849e736a88aa4914df0ff536c15f8848c417e712ff6abeb25047d300f8b215fd131f -MPFR.v4.2.1+0.x86_64-linux-gnu.tar.gz/md5/48194b9f92ad01b168e8b9612f4c9559 -MPFR.v4.2.1+0.x86_64-linux-gnu.tar.gz/sha512/638eb40d23fd492972809cdc3326ad4c2c99d3eae1ca5f7c0da6e0e335bb596de2899da5b3e65153225654b2cd9a805298e7241a21395e07d0b333eb1f101b5d -MPFR.v4.2.1+0.x86_64-linux-musl.tar.gz/md5/0babbb823964ccebf63b42fd07f08936 -MPFR.v4.2.1+0.x86_64-linux-musl.tar.gz/sha512/880b685d9b456fa2bf78e707273783423f9ff00791b529eba00c5e1b94ff96f4ba01e680152a4d6b45b695e3c1169d07f793db42c5a4120861813d5458dfc828 -MPFR.v4.2.1+0.x86_64-unknown-freebsd.tar.gz/md5/f11d634e5a19177fe36b2b2f6f5727ca -MPFR.v4.2.1+0.x86_64-unknown-freebsd.tar.gz/sha512/291245c06edf31b2e39b6774359ebd4f95b924f19d2a7e8581822a5bf908426d00f0452c061a027da0d7d4bb2fa1bb7ef8ab6d8e49bc848d6d7450a8d5c8a9c4 -MPFR.v4.2.1+0.x86_64-w64-mingw32.tar.gz/md5/e6d1347d5da312f7301d578ce9d7c4d9 -MPFR.v4.2.1+0.x86_64-w64-mingw32.tar.gz/sha512/3ea4b944172be250677ef271f1e10c2b95861755f203795a50b8d0f76f72498897059271e44e038625c3b73cccbd0165685d60afa994180d42e912bffbe86729 +MPFR.v4.2.1+1.aarch64-apple-darwin.tar.gz/md5/816f9ff59070f21f1df2f310e2606c06 +MPFR.v4.2.1+1.aarch64-apple-darwin.tar.gz/sha512/dad9adba7a8867d1ce26d77efb5c33b602b920a2cdbec84ea58a054cfab3ab7df54d2bda101de72b71604e7844993f1e216b002ba092e69277d0764040216c81 +MPFR.v4.2.1+1.aarch64-linux-gnu.tar.gz/md5/c1e3c9619af6454d8adae9bcbd911dba +MPFR.v4.2.1+1.aarch64-linux-gnu.tar.gz/sha512/5d916492aa73d11e022a7ca3f31940ceb8f8667bdf878ba29d6256736a380a2f6a11ac90cd8de3f1d3454a79165db240a1b971b9794fd21692ed64502ec34b9a +MPFR.v4.2.1+1.aarch64-linux-musl.tar.gz/md5/8ada267e2d23eb0c65ab2d2df02362d5 +MPFR.v4.2.1+1.aarch64-linux-musl.tar.gz/sha512/0c7f18e6d0f3e2052541e3279dfa9a74eb34067ac4fea0b17ab805cd73010cc83f8d7cb4eda8f4a904da398268d1c0d638c35521a9f339f8c7c3b5f159f27277 +MPFR.v4.2.1+1.aarch64-unknown-freebsd.tar.gz/md5/8aa99bf9c6157b8bb2833d8987ce0806 +MPFR.v4.2.1+1.aarch64-unknown-freebsd.tar.gz/sha512/6e4f547596eb8dd8ee2e1d3aefd7c73eed744add401c1f93d9951a9187c96fa9fc39be14683723dcb43cdf6891ea0021dc3416e43a0e2ec2038b0d1cd7c8434e +MPFR.v4.2.1+1.armv6l-linux-gnueabihf.tar.gz/md5/42bdb78eee83f496d7da699ad9603913 +MPFR.v4.2.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/edaa9ece1404a606d6b635406ad5e721c8d094ffa1c73ce19222afc2b4ea7b3b9e23e7c5589ae10fd9f4c4aefa265773bcfce6c510efbca57782115d43daeb13 +MPFR.v4.2.1+1.armv6l-linux-musleabihf.tar.gz/md5/2213207772b8a50de4768816fdc20e2f +MPFR.v4.2.1+1.armv6l-linux-musleabihf.tar.gz/sha512/d24debc38b8135ac5c10c4ea19de0c69126b6881940b4e182118e12cc2c7cf0aca2db065620f0cca636742da32eddec5bda3b4f449a035274f05120c977ed449 +MPFR.v4.2.1+1.armv7l-linux-gnueabihf.tar.gz/md5/a0d9fe20c9ff0027b6816ee0102b1f9a +MPFR.v4.2.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/97ce02898dc0d29a616048fd7ecee3100a710f7a30a21f2276c01675749034a5241be88bd46dff3dbf9ea0adca98a4357bd16e43fa9520e7a02477494c2d072e +MPFR.v4.2.1+1.armv7l-linux-musleabihf.tar.gz/md5/7898b9047c914b290b5928af5df63030 +MPFR.v4.2.1+1.armv7l-linux-musleabihf.tar.gz/sha512/cbefa9588752c65751630832417c1c42e4819d49ff9a505f61c2567ef4271097e585542fa898efd61409a43e439d827bb79f693a0937d0a3a427b39535979588 +MPFR.v4.2.1+1.i686-linux-gnu.tar.gz/md5/ac5a9db4bef94e7062dac463b5f87346 +MPFR.v4.2.1+1.i686-linux-gnu.tar.gz/sha512/2b5f3656e25065bfd83c81ee75999e6162c6e5436fcb0e3e3a767e2d941a556b4ebd3bebab78c63e8165105f81576959d8ad6e6d9cef1052751e39849e85df73 +MPFR.v4.2.1+1.i686-linux-musl.tar.gz/md5/6dc6a00d3ea22e2c60374d49926598d6 +MPFR.v4.2.1+1.i686-linux-musl.tar.gz/sha512/4a90356091b53d7238dda59f6e9c5c420614f16460dc67310e581611ad46a2dd3324d6164cfecf1bcd660b8f2e473f0afe137aac954c608b11be3acbda648e14 +MPFR.v4.2.1+1.i686-w64-mingw32.tar.gz/md5/7f7158a28ce8f262b897b38218f57958 +MPFR.v4.2.1+1.i686-w64-mingw32.tar.gz/sha512/8fbae0f1dd36534d4b9c63192c6e5cb1e531732d8eb1ab36783a6c71182f24ef80245b31a03460fd2f412fd0acaf1c4b9c8b574725271391217a3977b9ae4c79 +MPFR.v4.2.1+1.powerpc64le-linux-gnu.tar.gz/md5/ac70f716bddd5323b4add663b473b52d +MPFR.v4.2.1+1.powerpc64le-linux-gnu.tar.gz/sha512/ebb0f5ea76c892b7a4e4636706e71f476aaea58bb88e1734a7966c44495fda8c81318e0e8629e208185f0fc8d0c73b6f3463034cd831dfb5fbbd493a0689bc06 +MPFR.v4.2.1+1.x86_64-apple-darwin.tar.gz/md5/ff13e865e3be717b0fffc16296cb2f56 +MPFR.v4.2.1+1.x86_64-apple-darwin.tar.gz/sha512/98479210910945714da0285a40803674242581894a731ba4709c70dc1341849e736a88aa4914df0ff536c15f8848c417e712ff6abeb25047d300f8b215fd131f +MPFR.v4.2.1+1.x86_64-linux-gnu.tar.gz/md5/ca582be47601b8e6edb9d39f2881f44a +MPFR.v4.2.1+1.x86_64-linux-gnu.tar.gz/sha512/44a2e6158fde9fa8eaa6fac513dd5a8cae25a4b8879e5bb752a3f6af53d750c3a8e79be669ad87925b10c559cf9518fae431a607a342c48c00a390555e7e7b1f +MPFR.v4.2.1+1.x86_64-linux-musl.tar.gz/md5/0babbb823964ccebf63b42fd07f08936 +MPFR.v4.2.1+1.x86_64-linux-musl.tar.gz/sha512/880b685d9b456fa2bf78e707273783423f9ff00791b529eba00c5e1b94ff96f4ba01e680152a4d6b45b695e3c1169d07f793db42c5a4120861813d5458dfc828 +MPFR.v4.2.1+1.x86_64-unknown-freebsd.tar.gz/md5/f11d634e5a19177fe36b2b2f6f5727ca +MPFR.v4.2.1+1.x86_64-unknown-freebsd.tar.gz/sha512/291245c06edf31b2e39b6774359ebd4f95b924f19d2a7e8581822a5bf908426d00f0452c061a027da0d7d4bb2fa1bb7ef8ab6d8e49bc848d6d7450a8d5c8a9c4 +MPFR.v4.2.1+1.x86_64-w64-mingw32.tar.gz/md5/dcfad84470f15484443734feccbf8bf6 +MPFR.v4.2.1+1.x86_64-w64-mingw32.tar.gz/sha512/ceba1814fa671c2ba3e1ffeb6c736776981052e14111112fe963b5c11fd070136f8f022c5c21895f1f4f5084a5612fa673dddbb6b9622d7cade9b62eefcc8a14 mpfr-4.2.1.tar.bz2/md5/7765afa036e4ce7fb0e02bce0fef894b mpfr-4.2.1.tar.bz2/sha512/c81842532ecc663348deb7400d911ad71933d3b525a2f9e5adcd04265c9c0fdd1f22eca229f482703ac7f222ef209fc9e339dd1fa47d72ae57f7f70b2336a76f diff --git a/deps/checksums/nghttp2 b/deps/checksums/nghttp2 index f8226d4f68b3d..e552dfe9329b0 100644 --- a/deps/checksums/nghttp2 +++ b/deps/checksums/nghttp2 @@ -1,34 +1,36 @@ -nghttp2-1.60.0.tar.bz2/md5/ec20d9a6df7cc006894f72f81f9f2b42 -nghttp2-1.60.0.tar.bz2/sha512/95b76dd492dad490640469c4b806dd1a446f11143bc990220ff106fe4bfb76cdc4dfb112e0297c543b2d828f2870aa09ba820d88e3e9dedb29c8f3d3c9741af8 -nghttp2.v1.60.0+0.aarch64-apple-darwin.tar.gz/md5/dbf9f8161a124dc88ba44b54094b96e4 -nghttp2.v1.60.0+0.aarch64-apple-darwin.tar.gz/sha512/1997f473ea802afb09d7e13feb4eec9c11ad1d161cf83659ef6059a7c81639e00f8a3461c3538c81ea025e359b0927c3a362cef4a57e6544ad27588683142203 -nghttp2.v1.60.0+0.aarch64-linux-gnu.tar.gz/md5/d2e821a693d7d0720f0158b3e19ef7fa -nghttp2.v1.60.0+0.aarch64-linux-gnu.tar.gz/sha512/4165a1282d125b461d670d7d953c8a06b6508d1b97383a4126bc2fa9641454a9e0be749dbbaf772f2c2e6ea8cc3e64eb980cb0e09ac3d2fe5533eb3e6f7fa9e8 -nghttp2.v1.60.0+0.aarch64-linux-musl.tar.gz/md5/61ecc91336fcddb0f58af6af167e9a81 -nghttp2.v1.60.0+0.aarch64-linux-musl.tar.gz/sha512/802c7455e8f1ddfea74d3de3ceb937d1d10312f51594257cd406aedd67c181ada6ee5115bca00f8ee340a1471e2903bbe0159a0c08b80c556188647345e2c85b -nghttp2.v1.60.0+0.armv6l-linux-gnueabihf.tar.gz/md5/2998ae8d24d1bd540a29e0c6054bfcc8 -nghttp2.v1.60.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/5b2235a0c8bded57adcbab11dbe97b85a7d6d8a083c155bd74b0ac5546aa861730e88b615f1cbfa1071fcc2eb252aae8508e926ad3d5a1ddf0374536c260217e -nghttp2.v1.60.0+0.armv6l-linux-musleabihf.tar.gz/md5/7ebec92e3b340e25b952ccc4e714aa2e -nghttp2.v1.60.0+0.armv6l-linux-musleabihf.tar.gz/sha512/eb0e5c584527182816203ce9bfc35688a969803104ffd17dd4ac3720c27a4fcde3b3b471bf66fda8ac83ec8a56aa82d6d40f492ce06cbf6af39fafc60f35574d -nghttp2.v1.60.0+0.armv7l-linux-gnueabihf.tar.gz/md5/8c124c0daf59c622aedc7b9f1423d522 -nghttp2.v1.60.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/6e03246d1bfef7f184da68ac0eacc975dcb99172f2f352ce4ea5f5ae77536812163874d7ffc4fcb2df65dc51079880fdb83afc8988b73edb241cb641dc72f1fb -nghttp2.v1.60.0+0.armv7l-linux-musleabihf.tar.gz/md5/79968e1cb68c3b0518db528933251b0e -nghttp2.v1.60.0+0.armv7l-linux-musleabihf.tar.gz/sha512/f40790aa9a86fa2f44072c36a33416a7a8b4778881233989f8ed64ccb84f59ccdf3632b7a9d48d3e434e26cbd48c020e5d208da8fcb96e4e4ad41757e050213a -nghttp2.v1.60.0+0.i686-linux-gnu.tar.gz/md5/1580bf21084fa62ec26020f5c89430a1 -nghttp2.v1.60.0+0.i686-linux-gnu.tar.gz/sha512/cf83afe1bb796c57e220c0ba32a6990830df50cd91f82c781f2722d7b0ca5e5fbd8b708a0204be65bb8838c85b548f867c97e85941d124b81c67e01738f1db1a -nghttp2.v1.60.0+0.i686-linux-musl.tar.gz/md5/605eb6cd67b6fe3a1ba2d95413635831 -nghttp2.v1.60.0+0.i686-linux-musl.tar.gz/sha512/2c626b76898b4d782038661601fe34580c3cd560a519a46c4f6bc62d94ab987c7f2984350fc65933c00850cd2fe0b942fc64fcb23d2fb7db29bfed5866291b1a -nghttp2.v1.60.0+0.i686-w64-mingw32.tar.gz/md5/5b5694f36addbc503bc1e78e57159e5a -nghttp2.v1.60.0+0.i686-w64-mingw32.tar.gz/sha512/e70069b1dde2cf4dd041c4cc1c1ff40f67c20a8954b88d997fd7bf03d925b08148bc55293380dffce8c3b550a0e5768c94066e1a3b881ce4109ee94076c9a8b8 -nghttp2.v1.60.0+0.powerpc64le-linux-gnu.tar.gz/md5/1d073bba8e90c970bf1325a3d150d129 -nghttp2.v1.60.0+0.powerpc64le-linux-gnu.tar.gz/sha512/7e6f3895316d47a701944e8ee192b56f66aa05bf212c41164d25a0507f0e54c4c58c856e1c464fe3ec3eae78e0fe09ba8cf8b595c246faa3300a797750677180 -nghttp2.v1.60.0+0.x86_64-apple-darwin.tar.gz/md5/27d405bf53d4d438f74f91176d638741 -nghttp2.v1.60.0+0.x86_64-apple-darwin.tar.gz/sha512/59c4b4cca09e9a99e2e9ccc765068870824b907577c385313568ea29cd395caa3352bda230238888412b625e4a428a24c9ae0e59d122730cbd8025c6edbf0196 -nghttp2.v1.60.0+0.x86_64-linux-gnu.tar.gz/md5/ed1fe996e4c3e51d9ea8f724883dd3bc -nghttp2.v1.60.0+0.x86_64-linux-gnu.tar.gz/sha512/0b20db04ef7b2cc470b9abaab05d0e1e7ea3d674c1ed47c63e1cda00b98a6f10ce19ceb77ebd5ece28f6e4a2cf46227f5858f767ff0f04feed867c57941793ee -nghttp2.v1.60.0+0.x86_64-linux-musl.tar.gz/md5/cf3fcdb5720633700e4f9a9d8cd0cfc0 -nghttp2.v1.60.0+0.x86_64-linux-musl.tar.gz/sha512/d8f87b354de0f47be21b8e3aab6c2b05ee2af377e4bcc7df692fc4dd361ee5b731a190a0d9b4d4fdedf9c3a6a8a300f43338b38ac096da39ec13d4b79b544144 -nghttp2.v1.60.0+0.x86_64-unknown-freebsd.tar.gz/md5/b83aba7b3bd97ed7de770a597d6ec374 -nghttp2.v1.60.0+0.x86_64-unknown-freebsd.tar.gz/sha512/e40e47835bb0d5d548fbcfb28a64124323422bcdab411bcee7d4288cea765c6c82d7f4586980ee28b6ff310c6c7313aa4185ede192cd94839fbe708ab1ed14a7 -nghttp2.v1.60.0+0.x86_64-w64-mingw32.tar.gz/md5/37ba862c196b4d7c063cddc87722f7ff -nghttp2.v1.60.0+0.x86_64-w64-mingw32.tar.gz/sha512/ce0b70b4ad5cb30b83e672c3875fac7bcc8fc039506f05fef552a9d9cb53f053187dd02da4550dd7e5ef9aaaf8d587cee331eace0335f663d4190bacbc4ff9a2 +nghttp2-1.63.0.tar.bz2/md5/c29228929c3c323ecd0eae172f1eb9d5 +nghttp2-1.63.0.tar.bz2/sha512/a328b4642c6ca4395adfcaaf4e6eb6dbd39fa7bd86f872a76260af59a5a830e0ff5ad015865d6bc00e0baa8e4d0d9a67b4b97e9d78e5e05d1c53522364e5e235 +nghttp2.v1.63.0+1.aarch64-apple-darwin.tar.gz/md5/d11717d2fa4d04a374c55b86c790366a +nghttp2.v1.63.0+1.aarch64-apple-darwin.tar.gz/sha512/10dd24435818b66c92399941d3ea3280451f039e59112a866f973cdfe5602136e366f2bdb4638a1264f48fde737be4e50a49e44934971b4c69284377f4d9cf53 +nghttp2.v1.63.0+1.aarch64-linux-gnu.tar.gz/md5/5ebe2395d0d31b6481a4218468ec82a4 +nghttp2.v1.63.0+1.aarch64-linux-gnu.tar.gz/sha512/e7aff0eaa99be3b5e8539f160e1e5cf53bf1efa6b5f07d625796353a36ef12473946562731cedd570205229d263f81fd692b222a01297b09c89ce0e49edeff7a +nghttp2.v1.63.0+1.aarch64-linux-musl.tar.gz/md5/95c492eeca62b92baf8f8fa11a1da41f +nghttp2.v1.63.0+1.aarch64-linux-musl.tar.gz/sha512/10c0b3f560b1ff7ca9fe5cbc3340ec7789ecdeddf5d857d5c1245d1519074bd105f68c29714f36c8c9b688c5bf42133b30cbabed450410b79bb4f1f1d1474ef6 +nghttp2.v1.63.0+1.aarch64-unknown-freebsd.tar.gz/md5/c6fe5cea3d1386532f75f512e3641a7c +nghttp2.v1.63.0+1.aarch64-unknown-freebsd.tar.gz/sha512/20437e066e13b770f014e30b57830237bf38c3ecc56e716208c5103a7c242fec6cedcf78e641004891afa40ce945bfe1319d11aab4810a76ceeb6ff10373984c +nghttp2.v1.63.0+1.armv6l-linux-gnueabihf.tar.gz/md5/995dbd522109b882eaf7bedec84e8372 +nghttp2.v1.63.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/faf7ca13cd1d3050a5c693c61a5e92b560e1c0c2e30833162cc7aeefcd31c018b2015dbdbf543f38ddec2aefe78927af5f30f3938dc6a67b3b84fc399513c8cf +nghttp2.v1.63.0+1.armv6l-linux-musleabihf.tar.gz/md5/7bf7063ee64eb9b41212a6c39111de4f +nghttp2.v1.63.0+1.armv6l-linux-musleabihf.tar.gz/sha512/3091a4373508e1913e69d76688fd31d57d8e01e15c39c8859b025ac19ff23a0d396dc771fb0a2860ee1913866c0b0dbc4432fbcd0283b5cbecfb02235c738762 +nghttp2.v1.63.0+1.armv7l-linux-gnueabihf.tar.gz/md5/645de11170289308ce00ed089853271c +nghttp2.v1.63.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/bd974e629cd2626fc67a20b175e82e3a0dc742ee0c1b82b505395ebb7ce282def7d123b9bd8c4f7e3386db6c2c3d38d94475a00d96efb504a06fc2371db5a9e2 +nghttp2.v1.63.0+1.armv7l-linux-musleabihf.tar.gz/md5/8b5776769ec5577fde617814ccf57a7c +nghttp2.v1.63.0+1.armv7l-linux-musleabihf.tar.gz/sha512/7d463687abdfb600fcc11fd62b4973cdabdcfc076c5ace554af126ba6b0d925b9ff3eb3a9f730af6e4ef39d6ca1930084833a159d205a4230c88bbc82da7a3b6 +nghttp2.v1.63.0+1.i686-linux-gnu.tar.gz/md5/beb06b6d634fce5c15453c216c50d6b7 +nghttp2.v1.63.0+1.i686-linux-gnu.tar.gz/sha512/0616ffb2a98aa4cfdfbcaa408a6b33815a8e52af60417081854afb7b500e433f9fd63dff633df40507829217e96058f8b27184e50f442c175480e975f2387e13 +nghttp2.v1.63.0+1.i686-linux-musl.tar.gz/md5/ab15ac7ffaaeb7e69f7cad24ed3a6c4e +nghttp2.v1.63.0+1.i686-linux-musl.tar.gz/sha512/c9c18a6e255f4e3f81a9ee27b8ac58149507bce3dc9e79acbba8573ed66908c6cf3dcd29d9b989e47788977e6dec474c291f764fc1cfd718f20557001ca363c7 +nghttp2.v1.63.0+1.i686-w64-mingw32.tar.gz/md5/415858a5c10d7f2473174224e44ef3f6 +nghttp2.v1.63.0+1.i686-w64-mingw32.tar.gz/sha512/575c425df04c084980e8b9149579b787bf7ff38eecd958f38164db0bb0b4c331d4f9d6534f2c2cf1a105fc922ecba1d654a6a48a9a390f53bbcb4e8c2edbb0c7 +nghttp2.v1.63.0+1.powerpc64le-linux-gnu.tar.gz/md5/b2b8216a50aa7dd670e14ad47de31c3f +nghttp2.v1.63.0+1.powerpc64le-linux-gnu.tar.gz/sha512/c41570c8aa245fc2dce2a824cd73c4132ae2a65f9347d097462624917d637da34652c7f693e595a1f1ff1376171be4e2d48633d00dd6cafb00cd6cc974d9fa9e +nghttp2.v1.63.0+1.x86_64-apple-darwin.tar.gz/md5/878b3344f3656d663e33d7afc2fefb21 +nghttp2.v1.63.0+1.x86_64-apple-darwin.tar.gz/sha512/3f9cd4b20b149f8166f8411dc922c80a483113722f9a54c6274b3d4527dfd54bd5723fbd60535d8e90196f1d4e6e5c415f839b34cc4dc8c98eceee5466666471 +nghttp2.v1.63.0+1.x86_64-linux-gnu.tar.gz/md5/96fbea72cc42fa050fb0c9cb065588c8 +nghttp2.v1.63.0+1.x86_64-linux-gnu.tar.gz/sha512/47dd191ec2eb91b34ac9acd1802515778b2cea930c7c5876db3812566d3959e8a0e47bcee9601866180e40c9fd382d1599ca205a6229aaebdfa47e689f6ebd23 +nghttp2.v1.63.0+1.x86_64-linux-musl.tar.gz/md5/d68d6aac8629d95b2cd1b218152cc529 +nghttp2.v1.63.0+1.x86_64-linux-musl.tar.gz/sha512/fdac9dacd1392322cdc308997d7205b7194f897e21ff15fe4eda7c9c8a3b0b2b777cbc54723d884c341ef46467fc4fa056054ebff0f849090464ced031ff98f7 +nghttp2.v1.63.0+1.x86_64-unknown-freebsd.tar.gz/md5/61ecaf1d84e6b203ca7e601a91dc68e3 +nghttp2.v1.63.0+1.x86_64-unknown-freebsd.tar.gz/sha512/c25a017148ff7f01299a9a7cead87251f1a31fd404f4b1f5413fe9b09823ae471173f8d0828b096bb0ac7411e23d354f2c9e2596668d38d9a509831c6b4f5624 +nghttp2.v1.63.0+1.x86_64-w64-mingw32.tar.gz/md5/60ceb3ff3f5287b2782dfd98db4f1816 +nghttp2.v1.63.0+1.x86_64-w64-mingw32.tar.gz/sha512/971fd9e24c393fc594ae813caa7b14052320d924309bea0bd77c847ce0e86803cb803be051812ea029baf632deff6f9f8200dbc41f9bc143eba098adc294e757 diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 08bd98646c24b..74cdaa26c30b2 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,98 @@ -OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/md5/312aa603d089d680205dad7d5da58195 -OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/ffb0069561f52f8ac2f8affe937a00592e0c5d75c6d64bb0d5c93d1c925c93a46b763638031c88818b9dcef4a7b149ee3f15792a812e87f57a8ad086604164c4 -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/md5/7c43d9e9ac07820130a3d5faefdef882 -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/3ade0f098796148c37b118f9c052bad4e40431b4792f001043f040f8b1e4b7c3bae512f56ea21e6c0111246b2200e7720fe720a56a19dd11d1fba789344f29e3 -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/md5/cd2fe87dac703c8bfa25406aa732b88a -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/2aea68bd8f1db2ac920951c8d9a47ce8c071f3736ee8aad8d185a09be25234a0ffd11b9f9640015b82770ba3b3fad9aa511cc43501c1bb5a3a44f1fb7ccd5692 -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/md5/e3db2bf2f1f38aeee8530c78f3ec049a -OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/a0ccb92e818650ac3cbc292d5af1a000ee9b123953cc3eb16e2479e926af3f2be0ed9858e3c0c1075b1b9dd70ec1e51b9dce2c9d45b999d296aa050d257a3cb1 -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/md5/5bb605738930037259e773ebdb4a7041 -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/sha512/967e0f33be7b743d9617627a947a802286962a46c7c3b2418aaa1504cffc5f311b01e1702b35ded18ae3686b1914c6085213b03fa8a51e0a7ca16dc4cfee8504 -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/md5/ce175e82b9c6597c546552e79a43f934 -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/sha512/8ff5dff293d9786fc4f541b209b35afcbe325c13ddd0f9c8f9bfca8ba5c318c7890152260a5441b9e9088751ce03b1ff8f0f5d6fd4f142fae34bdb7390d1952c -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/md5/cae6aabbdccf31fb78b234785b52d48a -OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/sha512/ac842023e5db243fcfada22adca051bd2ffa04fca496454539931eede159e5d0490d444c338684c2d178c3367b23b8f3d76c544e30f1897bbed181f56237619f -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a -OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a -OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a -OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a -OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/md5/53087cc770708c57d2654fd0095b64df -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/sha512/90961448ae40b0445bf881d0815aec54d2096ad235dc8e3db8d698a72961ef9a97e7fcd08f79c83cd1f7c5a341464f52a90351d927d5f1c3e9c8ee32b17970db -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/md5/ee910e19faa961bde11fdf90c211df9d -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/sha512/f5cfecfe965991cfd7843eff71efa71d6842058565bb63657e909b2942e58a8c7506aa66335308961e59f392da16e1177d79542ae509795566a14122f67a1782 -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/md5/fe52ba7ca8e16f37aa04b79248e0471d -OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/sha512/79b5108886d60f12424709a841e359dc1cf23cef21bb0ee6d1a48043ac48a35dac1637e43c8ebf3f2e10dd34721993a7a12c5776f2975dd5bd7b6e29e1a9adc3 -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/md5/88d8ff421d29456f1d7670ceaf8867ca -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/sha512/91c1bd8142845d11fecba87a719315a14218e3863955ddd2ed82cecd4a2c177a48c660b6aac374ee9a11008245c0ced1bae70eaf5a1a6e3114db02e09a96396f -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/md5/3035066a53032b551e49f56b323e941d -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/sha512/f218e152a1c92bd374599814612add8010aedc78113cbe06465e8a1ee7f66892bb654cad687aa55555e74f3a65d74608692d41c9f0ce6c0bc63475ef62ab55b7 -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/md5/f7cf36ac9a0cbb535952ec73f2e6c9ea -OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/sha512/00ab052d9fa4a72a640545782019f24ed6017b36aa89c5e659ce73b1e821817f560c09f71b26c027c0a05bd13567c71a6d7f5995d1c39ab233bec56cd3a7fd9e -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/md5/b65414bb15539e5aa2f5f1c7984edb94 -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/sha512/847ada020bb92fe6ea81dfffaf855707a529c9c0f7e246e802b9521e5c7d4aa36104d04279c09a905a797184cdf05a6fabf84711b7661ecb14e9ac2fba251f61 -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/md5/0b626ebb8b3fc49b946723a9a2a21a91 -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/sha512/b5bba23878399fc1ff20abc2e2eb4acb9691ce982f290e33384732452774a0b447bd0fb01ee696d10ad8b03d99eec905662af92bd3b499d9fe6db419e05d2573 -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 -OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 -OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/md5/27c24775af446a44a72a28ffd197696d -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/2af8caa33bee88efff84653f3932b04e8fd4aabb1bf16d49fa73657b0ec13c9457fde7ab3f953fc9b01da5c2841c3c9b588e3b0f559b89df0e6268468d1f7cc8 -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/md5/414e701d918d5fba08a12de6979db4b5 -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/949886d388b80e19b944d102852f2bb58ffa03c42e624986dd9dc076797c996634d4a8fc0f04544451d6848c2079969816979e1f68a999b2747e9dd5472be7a6 -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/md5/29fcf62c0280cc10f91d22189a2e8de8 -OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/02e75d4ecf9cd922157a72c0ca2e713cf336b125df3982cd5f7cc4f2a04367ad4c2b1190ca2a0a9df8b639c7ebcfc9783066e99dd0b13acde7b02038391e8567 -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/md5/147d5e8eb2ec78fc8a31bdb091fab001 -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/2319eda568800c0b1f2d96a8a36c59b1bbd792c06de1d740aea3f1e49798242426ea8d10c100c42c3c281702e2b4f5b673b6ab5252b276d48542e875bcaa3094 -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/md5/448857d9c4b2e95afc12a14c75b24055 -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/3e7c8cd55e0b15a30992b1e0b48a6e2ae36fd9babf689fa5595c7de94aec401de1d7821d45a22bf14cd5c45c708bc8fa3511d34d732dadd4daaca3f49e200bdb -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/md5/3aaf417685b44e0e505208f7b31b981a -OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/f7b1d123e48ede93fe624a79d9535a8915bfa3441d7a6f9c6643467027414c9f2538e299858ea98bbb49d4e6d385a6a491063cb1878ac3b0b3d6a8f7ff0a48df -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/md5/5723136deaaf4b2e5960fb0774943288 -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/sha512/127ea8b2b0d8d4586a23a2b8ecbf148d512efe68626e89b0688c3c9e29ed9420b45ae86755c1467313c565f9f3835762051d7086a815b813dbe6e9eb05fb4be1 -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/md5/80b1b9cf5346916edda653174a987aa2 -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/sha512/77e1387ec969bbed4945d2a598a1cd04d258265c4b2d5c43af92118eb32e0c69e40619a20ea1835f277febcfea068b241343d44932afef832bdcfd2e9f618f0a -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/md5/44dcedf01c938d1a1c67dd3bc90ab61d -OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/sha512/e490d49b8d41d73ab3e71aca8c691ca58704f0fc6930cbfcc203f97b8db8d83144bad597a2c53ff0c0c4f7c40316d975a1b589a3603873d508f6beeb75970c5b -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/0e8a7e88b54cb836292c289d1c456fa9 -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/0e9b3af6839b9c41c950bb4d8b739f0243a890af7092ef9f3a00e4931f2acc3820afb78e40c7bfef716dcd3230c1d0acc7b0b37f30eb47441b476bd7540745e6 -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/5fc47ad55780c99ef9cab7ef1b26d9c0 -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/c531201e4abddd652efeb5801658f5c1e4891578f181e99d6e41fc0d3bc6347b82e5e928ff8a717ee1e75bb0a6a765260bf7c99fce44aa24c21f1c5a5e3c1e3b -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/dc127f3ab984b5d47b325d5701ab73cd -OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/50850911703320894a2e1e996c5de4613b5f9e3012f5cbf591f3677799599c45d9cc4c42cf310bdc6ba91ef550e52f6424bbbabdf47f96748d4669d94e6b46a4 -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 -OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff +OpenBLAS.v0.3.28+3.aarch64-apple-darwin-libgfortran5.tar.gz/md5/312aa603d089d680205dad7d5da58195 +OpenBLAS.v0.3.28+3.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/ffb0069561f52f8ac2f8affe937a00592e0c5d75c6d64bb0d5c93d1c925c93a46b763638031c88818b9dcef4a7b149ee3f15792a812e87f57a8ad086604164c4 +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran3.tar.gz/md5/7c43d9e9ac07820130a3d5faefdef882 +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/3ade0f098796148c37b118f9c052bad4e40431b4792f001043f040f8b1e4b7c3bae512f56ea21e6c0111246b2200e7720fe720a56a19dd11d1fba789344f29e3 +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran4.tar.gz/md5/cd2fe87dac703c8bfa25406aa732b88a +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/2aea68bd8f1db2ac920951c8d9a47ce8c071f3736ee8aad8d185a09be25234a0ffd11b9f9640015b82770ba3b3fad9aa511cc43501c1bb5a3a44f1fb7ccd5692 +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran5.tar.gz/md5/e3db2bf2f1f38aeee8530c78f3ec049a +OpenBLAS.v0.3.28+3.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/a0ccb92e818650ac3cbc292d5af1a000ee9b123953cc3eb16e2479e926af3f2be0ed9858e3c0c1075b1b9dd70ec1e51b9dce2c9d45b999d296aa050d257a3cb1 +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran3.tar.gz/md5/5bb605738930037259e773ebdb4a7041 +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran3.tar.gz/sha512/967e0f33be7b743d9617627a947a802286962a46c7c3b2418aaa1504cffc5f311b01e1702b35ded18ae3686b1914c6085213b03fa8a51e0a7ca16dc4cfee8504 +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran4.tar.gz/md5/ce175e82b9c6597c546552e79a43f934 +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran4.tar.gz/sha512/8ff5dff293d9786fc4f541b209b35afcbe325c13ddd0f9c8f9bfca8ba5c318c7890152260a5441b9e9088751ce03b1ff8f0f5d6fd4f142fae34bdb7390d1952c +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran5.tar.gz/md5/cae6aabbdccf31fb78b234785b52d48a +OpenBLAS.v0.3.28+3.aarch64-linux-musl-libgfortran5.tar.gz/sha512/ac842023e5db243fcfada22adca051bd2ffa04fca496454539931eede159e5d0490d444c338684c2d178c3367b23b8f3d76c544e30f1897bbed181f56237619f +OpenBLAS.v0.3.28+3.aarch64-unknown-freebsd-libgfortran4.tar.gz/md5/875223f1a3867d1d77ca911da1f12e7d +OpenBLAS.v0.3.28+3.aarch64-unknown-freebsd-libgfortran4.tar.gz/sha512/a53eced30cd5d85bf13f17959f0d43127a1d967dfa3fc18fd931b8a0670d8f4fa7fa4e5360937ec301a195e8c4757d2454c8d1d189e6429b97fe3b322559c970 +OpenBLAS.v0.3.28+3.aarch64-unknown-freebsd-libgfortran5.tar.gz/md5/efc5b9b88bbb515b88b4cd84d280d6f2 +OpenBLAS.v0.3.28+3.aarch64-unknown-freebsd-libgfortran5.tar.gz/sha512/16581e2b61500c939f3be0d1e1aab3c103c2cdf56b9e5880368ff87bd2ecec89e6ee6ed00f2db90208ca26132c0b92f318084b0b2644ed93e72ca3c9706f951c +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+3.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+3.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+3.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+3.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran3.tar.gz/md5/53087cc770708c57d2654fd0095b64df +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran3.tar.gz/sha512/90961448ae40b0445bf881d0815aec54d2096ad235dc8e3db8d698a72961ef9a97e7fcd08f79c83cd1f7c5a341464f52a90351d927d5f1c3e9c8ee32b17970db +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran4.tar.gz/md5/ee910e19faa961bde11fdf90c211df9d +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran4.tar.gz/sha512/f5cfecfe965991cfd7843eff71efa71d6842058565bb63657e909b2942e58a8c7506aa66335308961e59f392da16e1177d79542ae509795566a14122f67a1782 +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran5.tar.gz/md5/fe52ba7ca8e16f37aa04b79248e0471d +OpenBLAS.v0.3.28+3.i686-linux-gnu-libgfortran5.tar.gz/sha512/79b5108886d60f12424709a841e359dc1cf23cef21bb0ee6d1a48043ac48a35dac1637e43c8ebf3f2e10dd34721993a7a12c5776f2975dd5bd7b6e29e1a9adc3 +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran3.tar.gz/md5/88d8ff421d29456f1d7670ceaf8867ca +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran3.tar.gz/sha512/91c1bd8142845d11fecba87a719315a14218e3863955ddd2ed82cecd4a2c177a48c660b6aac374ee9a11008245c0ced1bae70eaf5a1a6e3114db02e09a96396f +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran4.tar.gz/md5/3035066a53032b551e49f56b323e941d +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran4.tar.gz/sha512/f218e152a1c92bd374599814612add8010aedc78113cbe06465e8a1ee7f66892bb654cad687aa55555e74f3a65d74608692d41c9f0ce6c0bc63475ef62ab55b7 +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran5.tar.gz/md5/f7cf36ac9a0cbb535952ec73f2e6c9ea +OpenBLAS.v0.3.28+3.i686-linux-musl-libgfortran5.tar.gz/sha512/00ab052d9fa4a72a640545782019f24ed6017b36aa89c5e659ce73b1e821817f560c09f71b26c027c0a05bd13567c71a6d7f5995d1c39ab233bec56cd3a7fd9e +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran3.tar.gz/md5/b19d09297372e071805ba033afb55def +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran3.tar.gz/sha512/eb1138578033167ececfe428db17fe28fad70631da3c25532edb4204fe733821156d6c619b6541fd47d53d335d6ab11b3d1ac1effb56031a2f782a5e8d863a89 +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran4.tar.gz/md5/98ed2a8f2d3249438b913d5f35715a53 +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran4.tar.gz/sha512/fbc32d81a4189ac170b18a029419bc98bb0655ee4d485f4bd165a394d223b80ba77f848d94a9ad96d926291de3db4a7602abd81c44fec55e4591dfe0aa91e29e +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 +OpenBLAS.v0.3.28+3.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 +OpenBLAS.v0.3.28+3.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran3.tar.gz/md5/27c24775af446a44a72a28ffd197696d +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/2af8caa33bee88efff84653f3932b04e8fd4aabb1bf16d49fa73657b0ec13c9457fde7ab3f953fc9b01da5c2841c3c9b588e3b0f559b89df0e6268468d1f7cc8 +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran4.tar.gz/md5/414e701d918d5fba08a12de6979db4b5 +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/949886d388b80e19b944d102852f2bb58ffa03c42e624986dd9dc076797c996634d4a8fc0f04544451d6848c2079969816979e1f68a999b2747e9dd5472be7a6 +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran5.tar.gz/md5/29fcf62c0280cc10f91d22189a2e8de8 +OpenBLAS.v0.3.28+3.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/02e75d4ecf9cd922157a72c0ca2e713cf336b125df3982cd5f7cc4f2a04367ad4c2b1190ca2a0a9df8b639c7ebcfc9783066e99dd0b13acde7b02038391e8567 +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran3.tar.gz/md5/147d5e8eb2ec78fc8a31bdb091fab001 +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/2319eda568800c0b1f2d96a8a36c59b1bbd792c06de1d740aea3f1e49798242426ea8d10c100c42c3c281702e2b4f5b673b6ab5252b276d48542e875bcaa3094 +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran4.tar.gz/md5/448857d9c4b2e95afc12a14c75b24055 +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/3e7c8cd55e0b15a30992b1e0b48a6e2ae36fd9babf689fa5595c7de94aec401de1d7821d45a22bf14cd5c45c708bc8fa3511d34d732dadd4daaca3f49e200bdb +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran5.tar.gz/md5/3aaf417685b44e0e505208f7b31b981a +OpenBLAS.v0.3.28+3.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/f7b1d123e48ede93fe624a79d9535a8915bfa3441d7a6f9c6643467027414c9f2538e299858ea98bbb49d4e6d385a6a491063cb1878ac3b0b3d6a8f7ff0a48df +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran3.tar.gz/md5/5723136deaaf4b2e5960fb0774943288 +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran3.tar.gz/sha512/127ea8b2b0d8d4586a23a2b8ecbf148d512efe68626e89b0688c3c9e29ed9420b45ae86755c1467313c565f9f3835762051d7086a815b813dbe6e9eb05fb4be1 +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran4.tar.gz/md5/80b1b9cf5346916edda653174a987aa2 +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran4.tar.gz/sha512/77e1387ec969bbed4945d2a598a1cd04d258265c4b2d5c43af92118eb32e0c69e40619a20ea1835f277febcfea068b241343d44932afef832bdcfd2e9f618f0a +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran5.tar.gz/md5/44dcedf01c938d1a1c67dd3bc90ab61d +OpenBLAS.v0.3.28+3.x86_64-linux-musl-libgfortran5.tar.gz/sha512/e490d49b8d41d73ab3e71aca8c691ca58704f0fc6930cbfcc203f97b8db8d83144bad597a2c53ff0c0c4f7c40316d975a1b589a3603873d508f6beeb75970c5b +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/0e8a7e88b54cb836292c289d1c456fa9 +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/0e9b3af6839b9c41c950bb4d8b739f0243a890af7092ef9f3a00e4931f2acc3820afb78e40c7bfef716dcd3230c1d0acc7b0b37f30eb47441b476bd7540745e6 +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/5fc47ad55780c99ef9cab7ef1b26d9c0 +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/c531201e4abddd652efeb5801658f5c1e4891578f181e99d6e41fc0d3bc6347b82e5e928ff8a717ee1e75bb0a6a765260bf7c99fce44aa24c21f1c5a5e3c1e3b +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/dc127f3ab984b5d47b325d5701ab73cd +OpenBLAS.v0.3.28+3.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/50850911703320894a2e1e996c5de4613b5f9e3012f5cbf591f3677799599c45d9cc4c42cf310bdc6ba91ef550e52f6424bbbabdf47f96748d4669d94e6b46a4 +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 +OpenBLAS.v0.3.28+3.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/md5/f7a1fe86cefbf7d4f2608843c7833ca7 openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/sha512/5f6020e958967a12a3c5b18bde13331f9c0602bd073563f35cd7cec848c92b45f30ca362819b12cd16989c0e4641ee3e63db8322d1092f61b31ba2e4068dd7a7 diff --git a/deps/checksums/openlibm b/deps/checksums/openlibm index 452abb133c671..e8c17e1efd26e 100644 --- a/deps/checksums/openlibm +++ b/deps/checksums/openlibm @@ -1,34 +1,36 @@ -OpenLibm.v0.8.1+2.aarch64-apple-darwin.tar.gz/md5/9ce53048e8944f6edff44f75b731229c -OpenLibm.v0.8.1+2.aarch64-apple-darwin.tar.gz/sha512/3a14e28db0656b47a473e19ca0afae1f8b72dd01e108d6b6cb52dc24fc03e4a43db867616b375369e82177bb274fbcfeb8f24b488ee68871e8da8463e9090adf -OpenLibm.v0.8.1+2.aarch64-linux-gnu.tar.gz/md5/8b284fe2905c3e5315291f5e5f27ca8b -OpenLibm.v0.8.1+2.aarch64-linux-gnu.tar.gz/sha512/d326181349ee7f74b73611cd71f933e93c38c11d6db9a1cd4fee49d1ac06c7f244f4cfc6ab373dd52909064117405b3d4fa39e5c626464c066ab53f1cd26dc4a -OpenLibm.v0.8.1+2.aarch64-linux-musl.tar.gz/md5/dc40ad1f2e53a3b914dcca364b6ead77 -OpenLibm.v0.8.1+2.aarch64-linux-musl.tar.gz/sha512/3779d8cd23c5987a15666e2160e40f5a6fc5e7d350c9e3c86d8af8c99515a8cb1f3b5e8438dae0f3cf0b5e1cb2c0cb74c5dd5a06c65e0c2a2382d86dacfaf9fb -OpenLibm.v0.8.1+2.armv6l-linux-gnueabihf.tar.gz/md5/7c9e56f6124b85e7dee74601f8c16abd -OpenLibm.v0.8.1+2.armv6l-linux-gnueabihf.tar.gz/sha512/a78e15177992025462d334a9d5b10b9c7f6710d77ac36056fe7a1cc3bc3fada87f16696366578cfa5f325d5f746639c41c5d80b4885814014d29556d63bd4c7c -OpenLibm.v0.8.1+2.armv6l-linux-musleabihf.tar.gz/md5/78d9e3178fdf93a35f7d2b0b00753dc6 -OpenLibm.v0.8.1+2.armv6l-linux-musleabihf.tar.gz/sha512/ff7b78786f7035eaa08770ddf7d4eb2984595a318c3ac4dfbe4091ca398e00638df2e77bc2ab5fd159defd0927d4fe46b7e824cf055fbae4860bfa12347e8c5b -OpenLibm.v0.8.1+2.armv7l-linux-gnueabihf.tar.gz/md5/7c9e56f6124b85e7dee74601f8c16abd -OpenLibm.v0.8.1+2.armv7l-linux-gnueabihf.tar.gz/sha512/a78e15177992025462d334a9d5b10b9c7f6710d77ac36056fe7a1cc3bc3fada87f16696366578cfa5f325d5f746639c41c5d80b4885814014d29556d63bd4c7c -OpenLibm.v0.8.1+2.armv7l-linux-musleabihf.tar.gz/md5/78d9e3178fdf93a35f7d2b0b00753dc6 -OpenLibm.v0.8.1+2.armv7l-linux-musleabihf.tar.gz/sha512/ff7b78786f7035eaa08770ddf7d4eb2984595a318c3ac4dfbe4091ca398e00638df2e77bc2ab5fd159defd0927d4fe46b7e824cf055fbae4860bfa12347e8c5b -OpenLibm.v0.8.1+2.i686-linux-gnu.tar.gz/md5/e9942dca99f024ae27876ea5ab1592a9 -OpenLibm.v0.8.1+2.i686-linux-gnu.tar.gz/sha512/406e39894a643bf99c493585fa631800bbbcd6c36aaa9e677de772f7ceaed93b462fdf797235174e22baf2f34c26527f400e282061954b34f05b389acaba1e29 -OpenLibm.v0.8.1+2.i686-linux-musl.tar.gz/md5/0037f2e2113282d49967eba72f215c4b -OpenLibm.v0.8.1+2.i686-linux-musl.tar.gz/sha512/96666332a814232084340791384505acf964064dba4f7b62db51a7ae4416237decb40318dc07b9a041547fd4ff77f204f42bc5c7f029e590af1ee1dd6196d843 -OpenLibm.v0.8.1+2.i686-w64-mingw32.tar.gz/md5/73193f2e5149d07008902adfbf1b74b2 -OpenLibm.v0.8.1+2.i686-w64-mingw32.tar.gz/sha512/e8202b59b8f922bcc908b8b8e6687a674faa701689f5c6175d83fea0bcc5d73f74bed37660e60406f37873dab1d8489e0fd1506294791adfa61a069555eababf -OpenLibm.v0.8.1+2.powerpc64le-linux-gnu.tar.gz/md5/01997fb48464f94f59f4708bd26eabc3 -OpenLibm.v0.8.1+2.powerpc64le-linux-gnu.tar.gz/sha512/1e1d8901fd3aab0948be5c387b8d5bd0db12766fe00bf800ee3100aa0d5973c7aa03ef9c9b4e34942e5e2b46b64035d7f8d7b070113db031d4611f2a7dd02ca3 -OpenLibm.v0.8.1+2.x86_64-apple-darwin.tar.gz/md5/6cb5a472d6c1446acfca11bb8f7283d6 -OpenLibm.v0.8.1+2.x86_64-apple-darwin.tar.gz/sha512/e52f399002544d94536c3bda742d3cc5b0995929d656eeb0e808954fb800fd8e5cfc0ab57279fbccab44fc33a1207ab345d78e685d519ff7f02cca8f554b9c06 -OpenLibm.v0.8.1+2.x86_64-linux-gnu.tar.gz/md5/e1c7dc61e98d5b8aa68de3462a2620a4 -OpenLibm.v0.8.1+2.x86_64-linux-gnu.tar.gz/sha512/fe6d74a2522d75374b87ac9746d444d75a768e069f24f3fbfc6a140aa9d073fa54e8899861f839e647b9261e660c5f2b5555f52fab39ef84a74685b632e89df9 -OpenLibm.v0.8.1+2.x86_64-linux-musl.tar.gz/md5/5fe8eb59d21732a80f432720419324b3 -OpenLibm.v0.8.1+2.x86_64-linux-musl.tar.gz/sha512/0d1b22ca01eda89caa1832b63b1d7ddafe0fedf5906680e817100e2176cbbae95f576409706a9ea1834bc692b72009f4fd244586df30228d18e626bf25fc040a -OpenLibm.v0.8.1+2.x86_64-unknown-freebsd.tar.gz/md5/2bcdf32fdef91433763e32be029814d9 -OpenLibm.v0.8.1+2.x86_64-unknown-freebsd.tar.gz/sha512/97854736fc8c797abd5a5c331e5795dfa9124ac108a76fc2bcac518f5750a08884717d611bb98222b13387bcd27e1c3f4ec841547859e87fafbbe8c7dcd7381a -OpenLibm.v0.8.1+2.x86_64-w64-mingw32.tar.gz/md5/e22079c6e610c9543cca0fb88495d989 -OpenLibm.v0.8.1+2.x86_64-w64-mingw32.tar.gz/sha512/67081bcf360a62eee3928bd1b9d5302ed29b4a176245721723692d5ef938a828379617847308f26a2c7bc0cb2d0dce129d4b8c65c0446c611126894c0aaa5ea8 +OpenLibm.v0.8.1+3.aarch64-apple-darwin.tar.gz/md5/9ce53048e8944f6edff44f75b731229c +OpenLibm.v0.8.1+3.aarch64-apple-darwin.tar.gz/sha512/3a14e28db0656b47a473e19ca0afae1f8b72dd01e108d6b6cb52dc24fc03e4a43db867616b375369e82177bb274fbcfeb8f24b488ee68871e8da8463e9090adf +OpenLibm.v0.8.1+3.aarch64-linux-gnu.tar.gz/md5/8b284fe2905c3e5315291f5e5f27ca8b +OpenLibm.v0.8.1+3.aarch64-linux-gnu.tar.gz/sha512/d326181349ee7f74b73611cd71f933e93c38c11d6db9a1cd4fee49d1ac06c7f244f4cfc6ab373dd52909064117405b3d4fa39e5c626464c066ab53f1cd26dc4a +OpenLibm.v0.8.1+3.aarch64-linux-musl.tar.gz/md5/dc40ad1f2e53a3b914dcca364b6ead77 +OpenLibm.v0.8.1+3.aarch64-linux-musl.tar.gz/sha512/3779d8cd23c5987a15666e2160e40f5a6fc5e7d350c9e3c86d8af8c99515a8cb1f3b5e8438dae0f3cf0b5e1cb2c0cb74c5dd5a06c65e0c2a2382d86dacfaf9fb +OpenLibm.v0.8.1+3.aarch64-unknown-freebsd.tar.gz/md5/f5e9441d81626b958396e585083e0bdb +OpenLibm.v0.8.1+3.aarch64-unknown-freebsd.tar.gz/sha512/1078823b0f5f48cd9f6dc753213b6b3f8112476c9df70192b042fd9bbb597fff34da009f376b6e67034681fcb07810a1a22b0dc83112fbbbaa60dac189164a41 +OpenLibm.v0.8.1+3.armv6l-linux-gnueabihf.tar.gz/md5/7c9e56f6124b85e7dee74601f8c16abd +OpenLibm.v0.8.1+3.armv6l-linux-gnueabihf.tar.gz/sha512/a78e15177992025462d334a9d5b10b9c7f6710d77ac36056fe7a1cc3bc3fada87f16696366578cfa5f325d5f746639c41c5d80b4885814014d29556d63bd4c7c +OpenLibm.v0.8.1+3.armv6l-linux-musleabihf.tar.gz/md5/78d9e3178fdf93a35f7d2b0b00753dc6 +OpenLibm.v0.8.1+3.armv6l-linux-musleabihf.tar.gz/sha512/ff7b78786f7035eaa08770ddf7d4eb2984595a318c3ac4dfbe4091ca398e00638df2e77bc2ab5fd159defd0927d4fe46b7e824cf055fbae4860bfa12347e8c5b +OpenLibm.v0.8.1+3.armv7l-linux-gnueabihf.tar.gz/md5/7c9e56f6124b85e7dee74601f8c16abd +OpenLibm.v0.8.1+3.armv7l-linux-gnueabihf.tar.gz/sha512/a78e15177992025462d334a9d5b10b9c7f6710d77ac36056fe7a1cc3bc3fada87f16696366578cfa5f325d5f746639c41c5d80b4885814014d29556d63bd4c7c +OpenLibm.v0.8.1+3.armv7l-linux-musleabihf.tar.gz/md5/78d9e3178fdf93a35f7d2b0b00753dc6 +OpenLibm.v0.8.1+3.armv7l-linux-musleabihf.tar.gz/sha512/ff7b78786f7035eaa08770ddf7d4eb2984595a318c3ac4dfbe4091ca398e00638df2e77bc2ab5fd159defd0927d4fe46b7e824cf055fbae4860bfa12347e8c5b +OpenLibm.v0.8.1+3.i686-linux-gnu.tar.gz/md5/69b0c561e8f70e12f78a47bbcc28d43f +OpenLibm.v0.8.1+3.i686-linux-gnu.tar.gz/sha512/916bedde7b75aaa10a7517aa6a24da924e896aa46159447722010aa60a8c0974da8c2aa847d0a5853d391e7f3b792371304aa18a6d72d998f38f2a00b7179c30 +OpenLibm.v0.8.1+3.i686-linux-musl.tar.gz/md5/0037f2e2113282d49967eba72f215c4b +OpenLibm.v0.8.1+3.i686-linux-musl.tar.gz/sha512/96666332a814232084340791384505acf964064dba4f7b62db51a7ae4416237decb40318dc07b9a041547fd4ff77f204f42bc5c7f029e590af1ee1dd6196d843 +OpenLibm.v0.8.1+3.i686-w64-mingw32.tar.gz/md5/a2a5ba90531660f1e758df91bb11c2f9 +OpenLibm.v0.8.1+3.i686-w64-mingw32.tar.gz/sha512/b177c124dbe2dd491b49bf01b58b639629e2039c60dbd8ef1acf42985a7bd5ac1c5950a803b19e3ed5436ebd0a83f1e7af505d5f90b270467600ecab3e8a5cda +OpenLibm.v0.8.1+3.powerpc64le-linux-gnu.tar.gz/md5/01997fb48464f94f59f4708bd26eabc3 +OpenLibm.v0.8.1+3.powerpc64le-linux-gnu.tar.gz/sha512/1e1d8901fd3aab0948be5c387b8d5bd0db12766fe00bf800ee3100aa0d5973c7aa03ef9c9b4e34942e5e2b46b64035d7f8d7b070113db031d4611f2a7dd02ca3 +OpenLibm.v0.8.1+3.x86_64-apple-darwin.tar.gz/md5/6cb5a472d6c1446acfca11bb8f7283d6 +OpenLibm.v0.8.1+3.x86_64-apple-darwin.tar.gz/sha512/e52f399002544d94536c3bda742d3cc5b0995929d656eeb0e808954fb800fd8e5cfc0ab57279fbccab44fc33a1207ab345d78e685d519ff7f02cca8f554b9c06 +OpenLibm.v0.8.1+3.x86_64-linux-gnu.tar.gz/md5/e1c7dc61e98d5b8aa68de3462a2620a4 +OpenLibm.v0.8.1+3.x86_64-linux-gnu.tar.gz/sha512/fe6d74a2522d75374b87ac9746d444d75a768e069f24f3fbfc6a140aa9d073fa54e8899861f839e647b9261e660c5f2b5555f52fab39ef84a74685b632e89df9 +OpenLibm.v0.8.1+3.x86_64-linux-musl.tar.gz/md5/5fe8eb59d21732a80f432720419324b3 +OpenLibm.v0.8.1+3.x86_64-linux-musl.tar.gz/sha512/0d1b22ca01eda89caa1832b63b1d7ddafe0fedf5906680e817100e2176cbbae95f576409706a9ea1834bc692b72009f4fd244586df30228d18e626bf25fc040a +OpenLibm.v0.8.1+3.x86_64-unknown-freebsd.tar.gz/md5/2bcdf32fdef91433763e32be029814d9 +OpenLibm.v0.8.1+3.x86_64-unknown-freebsd.tar.gz/sha512/97854736fc8c797abd5a5c331e5795dfa9124ac108a76fc2bcac518f5750a08884717d611bb98222b13387bcd27e1c3f4ec841547859e87fafbbe8c7dcd7381a +OpenLibm.v0.8.1+3.x86_64-w64-mingw32.tar.gz/md5/31a75f828f782130bf6a463521a11f04 +OpenLibm.v0.8.1+3.x86_64-w64-mingw32.tar.gz/sha512/d54f688940229a5fc3db958460556d362c81e2e0a7bac010537123e5ff102b17d84123ee2e164151d51fb8ee7524e0888531e14d2c5ebfb3d6847b03af0086ad openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/md5/19408d70bf042a109e1c267a53740089 openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/sha512/9597fdcbc4af8369e6eecc3f8e86f251661cc64d236578f3ee8a6b39e77a47951446e1a0fe1151513da153e7ed17bf39aa5a36c32153d0d0400232bed2839e22 diff --git a/deps/checksums/p7zip b/deps/checksums/p7zip index 272f1d768161f..2fe4fb874bec4 100644 --- a/deps/checksums/p7zip +++ b/deps/checksums/p7zip @@ -1,34 +1,36 @@ p7zip-17.05.tar.gz/md5/de921a08f37242a8eed8e4a758fbcb58 p7zip-17.05.tar.gz/sha512/97a7cfd15287998eb049c320548477be496c4ddf6b45c833c42adca4ab88719b07a442ae2e71cf2dc3b30a0777a3acab0a1a30f01fd85bacffa3fa9bd22c3f7d -p7zip.v17.5.0+0.aarch64-apple-darwin.tar.gz/md5/2a254e251901b3d1ddfd7aff23a6e5eb -p7zip.v17.5.0+0.aarch64-apple-darwin.tar.gz/sha512/8efb9a2c9bcab388e523adba3dc0b876e8ae34e2440c3eee01fd780eb87c8619c7a7bbdc46d703ccefff6aa6ad64c4e4b45b723136ab1f6fd6de4f52e75ebbbf -p7zip.v17.5.0+0.aarch64-linux-gnu.tar.gz/md5/bb1f3773fd409dbb91a10f7d9d2e99b5 -p7zip.v17.5.0+0.aarch64-linux-gnu.tar.gz/sha512/e95ccc342be644570d218d25403b91a7db9ee983fbf8cce3deff453355d68d426f9301eaac865a98691025b596b8cd77ebebf6184c0eaf8b2f294bc6763b9a4b -p7zip.v17.5.0+0.aarch64-linux-musl.tar.gz/md5/3fac518a6a70412294d71ca510958cf2 -p7zip.v17.5.0+0.aarch64-linux-musl.tar.gz/sha512/fc127790739bf8a8b918b2e83753d86f5e79ee8706bde4cc79d74d9f7d846aae99a109da4b2b3cc92ccedc1eef4d52a555a65a95f588e173e0fecc11f2ca21e6 -p7zip.v17.5.0+0.armv6l-linux-gnueabihf.tar.gz/md5/355410848192de3b02d12fd663867f4b -p7zip.v17.5.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/8f103b41e755d157d70dacca89a0ef4610bea109686b4005e8edd5f79ed2e6419c00c2625d0ab90e6e33fa389e670490d8de263c0bdae952cc34cbbf440e275f -p7zip.v17.5.0+0.armv6l-linux-musleabihf.tar.gz/md5/34363b227306fce34a728af54b71064f -p7zip.v17.5.0+0.armv6l-linux-musleabihf.tar.gz/sha512/8dd7b37ce6223c9fedcaa999eb806eb6dec8c4a3133d3c07e2456cb8543b8e4f5b881c1bff2d2e25f19b1312b18673e9013aeff87d6a274eec6c451b1ba0d6b9 -p7zip.v17.5.0+0.armv7l-linux-gnueabihf.tar.gz/md5/dbb1fc0cf3bea674442ff8cc932a94cd -p7zip.v17.5.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/c4d71d905fa420391417786ed206a0c334475dd0df8baa1fc3f6560ce548db11805003d0d0b35bb622fe818c761f2b0abe0796d1cbfce2a922da69e697f056a2 -p7zip.v17.5.0+0.armv7l-linux-musleabihf.tar.gz/md5/d188b5dd453faedb616ba9c48fdeab6b -p7zip.v17.5.0+0.armv7l-linux-musleabihf.tar.gz/sha512/ea30a775370502ca9e271b87cbda528d0c51d63ce0df41883d4dbc1527a32f251d797f3692fcf9b883b5fbaaad80515b971a8f8fe09ba102978b19a0ecb58528 -p7zip.v17.5.0+0.i686-linux-gnu.tar.gz/md5/dc02bdde045a0b6b22cf14d6960e63ed -p7zip.v17.5.0+0.i686-linux-gnu.tar.gz/sha512/d2d0dd14a5fc1163fea2276e0925bfa8d075d5dba1d8018e4e3160977d3b09642b2e521d8e57d049abaf0e2ea391a846f0b0136b3c59e8b476c8c52ac5210447 -p7zip.v17.5.0+0.i686-linux-musl.tar.gz/md5/0b8658147938a8ec109ee2b3b0a0665f -p7zip.v17.5.0+0.i686-linux-musl.tar.gz/sha512/411b2950f5928c537b87ba0651c09c08e57afed765db9fee89eda8b12939ef0da94c8ba38c0a24ba46b4513a0e4cca798eb09f2b20a011099ed3cf14455dd19e -p7zip.v17.5.0+0.i686-w64-mingw32.tar.gz/md5/98bdd8767c77a35f71303ff490a3d363 -p7zip.v17.5.0+0.i686-w64-mingw32.tar.gz/sha512/14f08071af74297df8bfe1d9f7efa3c0212e62ace573848f17b729e4c36dc3861110f3c5cc9315364c318e5b040736443a24492e86d76161993653a309996eb3 -p7zip.v17.5.0+0.powerpc64le-linux-gnu.tar.gz/md5/b18c917b9852898a9b9d6d24bcc6863e -p7zip.v17.5.0+0.powerpc64le-linux-gnu.tar.gz/sha512/0148dc8a0bc9c95212d7f8e2f92ee24e968eb7290fe72c7ae02e286bf5c05dd6b1f10b32350a7ff37777ed5a8cc21f3303f464620f3394c7a4690ae98bf77299 -p7zip.v17.5.0+0.x86_64-apple-darwin.tar.gz/md5/da31752a2556644d39e48649bb0111de -p7zip.v17.5.0+0.x86_64-apple-darwin.tar.gz/sha512/0695ad111263d2fadfdf9a46ce7ee80def0bf60db7d1c2585ed2af6fc945fb169311a9f1ffc6f95fb43b0b03694d2d1be9136d3d78ba2ef2b19228987883a385 -p7zip.v17.5.0+0.x86_64-linux-gnu.tar.gz/md5/2fb55d86e4eaccb0488bd637d088b996 -p7zip.v17.5.0+0.x86_64-linux-gnu.tar.gz/sha512/38ac355157d59c09f308fc29964d0e9c1466c9633efd8d3c6ff3c738abce2af45ebc6b92a29f56d5e7baa4871f9f39b14ecfcbedd4e2f4ca7c0fe6627c6b13e7 -p7zip.v17.5.0+0.x86_64-linux-musl.tar.gz/md5/f0bd567a851d2dd9d306552ffafbca3a -p7zip.v17.5.0+0.x86_64-linux-musl.tar.gz/sha512/e60047a6e7e3496cb6658f87c8c88676f399cd9f3d0d7daa880b6be09cd5525f7f22776896f1375722b47555514ff8c018f02ce800ec3fd0ed922e16e8a6d657 -p7zip.v17.5.0+0.x86_64-unknown-freebsd.tar.gz/md5/d37bd26e39a3ec84f262636f70624341 -p7zip.v17.5.0+0.x86_64-unknown-freebsd.tar.gz/sha512/0604a880c19f9d72d5828edd75be641625c29f230b3a5e7d70ec3812c014c96b76ee7b45e0e80f49be63f109a48700e75d1e5be01b5ae7b46d42dafda9885e8c -p7zip.v17.5.0+0.x86_64-w64-mingw32.tar.gz/md5/f02c7b2481dee880b096340a8735350f -p7zip.v17.5.0+0.x86_64-w64-mingw32.tar.gz/sha512/08b717c1b072d1309f6af8973eb09b1a482abb7ae7d01fba79873d4310a7c11292e2e8779029f99cc60627ed0d064224bc87782e587c520f970b840b7b838052 +p7zip.v17.5.0+1.aarch64-apple-darwin.tar.gz/md5/2a254e251901b3d1ddfd7aff23a6e5eb +p7zip.v17.5.0+1.aarch64-apple-darwin.tar.gz/sha512/8efb9a2c9bcab388e523adba3dc0b876e8ae34e2440c3eee01fd780eb87c8619c7a7bbdc46d703ccefff6aa6ad64c4e4b45b723136ab1f6fd6de4f52e75ebbbf +p7zip.v17.5.0+1.aarch64-linux-gnu.tar.gz/md5/bb1f3773fd409dbb91a10f7d9d2e99b5 +p7zip.v17.5.0+1.aarch64-linux-gnu.tar.gz/sha512/e95ccc342be644570d218d25403b91a7db9ee983fbf8cce3deff453355d68d426f9301eaac865a98691025b596b8cd77ebebf6184c0eaf8b2f294bc6763b9a4b +p7zip.v17.5.0+1.aarch64-linux-musl.tar.gz/md5/3fac518a6a70412294d71ca510958cf2 +p7zip.v17.5.0+1.aarch64-linux-musl.tar.gz/sha512/fc127790739bf8a8b918b2e83753d86f5e79ee8706bde4cc79d74d9f7d846aae99a109da4b2b3cc92ccedc1eef4d52a555a65a95f588e173e0fecc11f2ca21e6 +p7zip.v17.5.0+1.aarch64-unknown-freebsd.tar.gz/md5/4190f8d7d42572b3fdab0fa382417d43 +p7zip.v17.5.0+1.aarch64-unknown-freebsd.tar.gz/sha512/5b0cb08374b8561873f76cb2b8bcbb8de1ff4c91bde23222cc1b650c6ea2fff265e48b6190551ed136324a47d25e1d357a754295b674e74b4628b20223ad067d +p7zip.v17.5.0+1.armv6l-linux-gnueabihf.tar.gz/md5/355410848192de3b02d12fd663867f4b +p7zip.v17.5.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/8f103b41e755d157d70dacca89a0ef4610bea109686b4005e8edd5f79ed2e6419c00c2625d0ab90e6e33fa389e670490d8de263c0bdae952cc34cbbf440e275f +p7zip.v17.5.0+1.armv6l-linux-musleabihf.tar.gz/md5/34363b227306fce34a728af54b71064f +p7zip.v17.5.0+1.armv6l-linux-musleabihf.tar.gz/sha512/8dd7b37ce6223c9fedcaa999eb806eb6dec8c4a3133d3c07e2456cb8543b8e4f5b881c1bff2d2e25f19b1312b18673e9013aeff87d6a274eec6c451b1ba0d6b9 +p7zip.v17.5.0+1.armv7l-linux-gnueabihf.tar.gz/md5/dbb1fc0cf3bea674442ff8cc932a94cd +p7zip.v17.5.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/c4d71d905fa420391417786ed206a0c334475dd0df8baa1fc3f6560ce548db11805003d0d0b35bb622fe818c761f2b0abe0796d1cbfce2a922da69e697f056a2 +p7zip.v17.5.0+1.armv7l-linux-musleabihf.tar.gz/md5/d188b5dd453faedb616ba9c48fdeab6b +p7zip.v17.5.0+1.armv7l-linux-musleabihf.tar.gz/sha512/ea30a775370502ca9e271b87cbda528d0c51d63ce0df41883d4dbc1527a32f251d797f3692fcf9b883b5fbaaad80515b971a8f8fe09ba102978b19a0ecb58528 +p7zip.v17.5.0+1.i686-linux-gnu.tar.gz/md5/dc02bdde045a0b6b22cf14d6960e63ed +p7zip.v17.5.0+1.i686-linux-gnu.tar.gz/sha512/d2d0dd14a5fc1163fea2276e0925bfa8d075d5dba1d8018e4e3160977d3b09642b2e521d8e57d049abaf0e2ea391a846f0b0136b3c59e8b476c8c52ac5210447 +p7zip.v17.5.0+1.i686-linux-musl.tar.gz/md5/0b8658147938a8ec109ee2b3b0a0665f +p7zip.v17.5.0+1.i686-linux-musl.tar.gz/sha512/411b2950f5928c537b87ba0651c09c08e57afed765db9fee89eda8b12939ef0da94c8ba38c0a24ba46b4513a0e4cca798eb09f2b20a011099ed3cf14455dd19e +p7zip.v17.5.0+1.i686-w64-mingw32.tar.gz/md5/98bdd8767c77a35f71303ff490a3d363 +p7zip.v17.5.0+1.i686-w64-mingw32.tar.gz/sha512/14f08071af74297df8bfe1d9f7efa3c0212e62ace573848f17b729e4c36dc3861110f3c5cc9315364c318e5b040736443a24492e86d76161993653a309996eb3 +p7zip.v17.5.0+1.powerpc64le-linux-gnu.tar.gz/md5/b18c917b9852898a9b9d6d24bcc6863e +p7zip.v17.5.0+1.powerpc64le-linux-gnu.tar.gz/sha512/0148dc8a0bc9c95212d7f8e2f92ee24e968eb7290fe72c7ae02e286bf5c05dd6b1f10b32350a7ff37777ed5a8cc21f3303f464620f3394c7a4690ae98bf77299 +p7zip.v17.5.0+1.x86_64-apple-darwin.tar.gz/md5/da31752a2556644d39e48649bb0111de +p7zip.v17.5.0+1.x86_64-apple-darwin.tar.gz/sha512/0695ad111263d2fadfdf9a46ce7ee80def0bf60db7d1c2585ed2af6fc945fb169311a9f1ffc6f95fb43b0b03694d2d1be9136d3d78ba2ef2b19228987883a385 +p7zip.v17.5.0+1.x86_64-linux-gnu.tar.gz/md5/2fb55d86e4eaccb0488bd637d088b996 +p7zip.v17.5.0+1.x86_64-linux-gnu.tar.gz/sha512/38ac355157d59c09f308fc29964d0e9c1466c9633efd8d3c6ff3c738abce2af45ebc6b92a29f56d5e7baa4871f9f39b14ecfcbedd4e2f4ca7c0fe6627c6b13e7 +p7zip.v17.5.0+1.x86_64-linux-musl.tar.gz/md5/f0bd567a851d2dd9d306552ffafbca3a +p7zip.v17.5.0+1.x86_64-linux-musl.tar.gz/sha512/e60047a6e7e3496cb6658f87c8c88676f399cd9f3d0d7daa880b6be09cd5525f7f22776896f1375722b47555514ff8c018f02ce800ec3fd0ed922e16e8a6d657 +p7zip.v17.5.0+1.x86_64-unknown-freebsd.tar.gz/md5/d37bd26e39a3ec84f262636f70624341 +p7zip.v17.5.0+1.x86_64-unknown-freebsd.tar.gz/sha512/0604a880c19f9d72d5828edd75be641625c29f230b3a5e7d70ec3812c014c96b76ee7b45e0e80f49be63f109a48700e75d1e5be01b5ae7b46d42dafda9885e8c +p7zip.v17.5.0+1.x86_64-w64-mingw32.tar.gz/md5/f02c7b2481dee880b096340a8735350f +p7zip.v17.5.0+1.x86_64-w64-mingw32.tar.gz/sha512/08b717c1b072d1309f6af8973eb09b1a482abb7ae7d01fba79873d4310a7c11292e2e8779029f99cc60627ed0d064224bc87782e587c520f970b840b7b838052 diff --git a/deps/checksums/pcre b/deps/checksums/pcre index 744d16540d6c8..018ffd5201653 100644 --- a/deps/checksums/pcre +++ b/deps/checksums/pcre @@ -1,34 +1,36 @@ -PCRE2.v10.43.0+0.aarch64-apple-darwin.tar.gz/md5/f1bee27b8d9465c14eaf9362701fb795 -PCRE2.v10.43.0+0.aarch64-apple-darwin.tar.gz/sha512/33b8f6e3703f0a52cd2d57897c28e35fb3c63af459296a2fef4e414dc99239617833b2ab176068d6aab690122a34a9ab9b6042dfff54b5a30ad60429a809818d -PCRE2.v10.43.0+0.aarch64-linux-gnu.tar.gz/md5/c55a569260e302f315f4a1bd185346ab -PCRE2.v10.43.0+0.aarch64-linux-gnu.tar.gz/sha512/be4d2883e69d562898a157424b2baa146fe79545a8c10935cf25b54e498ca2c14fae026fa0d958d175895fe2cb695d0f96ef7f09fecbf54e1cee4a55b81a382b -PCRE2.v10.43.0+0.aarch64-linux-musl.tar.gz/md5/fb041ccace415ccc26263968c6435a47 -PCRE2.v10.43.0+0.aarch64-linux-musl.tar.gz/sha512/06672ebe18e0f6bfa1dd2d5c02e10d9fd67236a73fd38ee2e8f4496d98f297f7866760f0be3b9cebeca348a5d748a3719e416b84cec96a90c71eac55afbbd905 -PCRE2.v10.43.0+0.armv6l-linux-gnueabihf.tar.gz/md5/4f303a4cbf26abb7bf4ffb8bfe3d636d -PCRE2.v10.43.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/dddb3b227ee48d8329f6c65c5d0fce9f460eccaec98594a05bf28d1d9af01397cf7ef86c96e88b0e96030a7f6d8406461f78dd5fa558db8fc8f7bfb3b522ed54 -PCRE2.v10.43.0+0.armv6l-linux-musleabihf.tar.gz/md5/eade1fff90404bf3584fd15b62be0cfa -PCRE2.v10.43.0+0.armv6l-linux-musleabihf.tar.gz/sha512/351f6fa11c39b90fcc4086bd00b1b1126ed92272595f0b745757ca4e7e360c84d244446a871029245c3bcf838b23f42d908f858e44fae7deb9002a36cb76753c -PCRE2.v10.43.0+0.armv7l-linux-gnueabihf.tar.gz/md5/daa0a34b2cf0b71a6f8e1f9456cd4b06 -PCRE2.v10.43.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/ae72956ae7a9a5f315bfc816fdbb500937a170dfea306a28289ec9eac57d883cf2fa5a467ce9406eea80546b632a272c63bbb48b89ebe6d9f69d30366fd84180 -PCRE2.v10.43.0+0.armv7l-linux-musleabihf.tar.gz/md5/90bfb9e4efd7c92a2bb6a1a48fd88ecb -PCRE2.v10.43.0+0.armv7l-linux-musleabihf.tar.gz/sha512/147ac98d82fec4695de0c43c87d3d9242b9c024bc6df7ad7504d17ef6a12a029ed703c4deade0e2b24faf5283d66309f880d62f8c4834f27b2cc8889587d7abe -PCRE2.v10.43.0+0.i686-linux-gnu.tar.gz/md5/6fde649bf449c4122438fff32c0706ab -PCRE2.v10.43.0+0.i686-linux-gnu.tar.gz/sha512/edfaa15490497723c095eaa5df26194637b0606e9dce7b89b400024ef8ac42e21f010bb31c2cee5c735ce82fc8de0c42bf2b35b095a1e70a9a111d3bfba6da64 -PCRE2.v10.43.0+0.i686-linux-musl.tar.gz/md5/73aa8d13cc48338a5071e30b3a899109 -PCRE2.v10.43.0+0.i686-linux-musl.tar.gz/sha512/200e2d3ffd68f49b76c70a5be80cb0ae9703049214674485a2ab24abaaea7aefd6dec2042a14bd48cc52b04379f57322ec1e1788dc8c00896e1074921725d9cc -PCRE2.v10.43.0+0.i686-w64-mingw32.tar.gz/md5/4ddf0f31c97463e5216ed71afc4fb014 -PCRE2.v10.43.0+0.i686-w64-mingw32.tar.gz/sha512/75903d81668a66a5c4d830e31657391d507883943d86245998f224655406dcc6a95ba4f5fad20dcf608a98d6ccf49abe50107993448669b03c42a878d8466611 -PCRE2.v10.43.0+0.powerpc64le-linux-gnu.tar.gz/md5/64cb71080da1c97eba3a440ff53d298c -PCRE2.v10.43.0+0.powerpc64le-linux-gnu.tar.gz/sha512/16348b96a45c7a7d86775cb1d082b4d1c060e5a8acfb37554885d8da0db87430d8a40f834f008a90f4a7b1c07b8329df96836ba0430ecec506a143b7347bb101 -PCRE2.v10.43.0+0.x86_64-apple-darwin.tar.gz/md5/31bbb2485f5e06c3616fb061ffb2f022 -PCRE2.v10.43.0+0.x86_64-apple-darwin.tar.gz/sha512/3284ee63ed1e5631267efacb354a1d90bd1b7db0bc81d7233c9580eee4a9af06093c1c4f240786c34299df89a36a17ed92598fc302074f5a200c56cc96081bf1 -PCRE2.v10.43.0+0.x86_64-linux-gnu.tar.gz/md5/2fb7e0e9bbc32dddf543f4d395b50d3f -PCRE2.v10.43.0+0.x86_64-linux-gnu.tar.gz/sha512/5a533a3a01f817689077377835dc88edf914459ed0df7323f8f4dba602a47fd6af700075feb1f448221366b1cf7e2d717c615a5c506eb4ca2db9c600fd290fb0 -PCRE2.v10.43.0+0.x86_64-linux-musl.tar.gz/md5/b432063c93aa477dd0883428191041f8 -PCRE2.v10.43.0+0.x86_64-linux-musl.tar.gz/sha512/36475e90e29d7324046fe1da669fb37f667245a680df23f3978394964e14eb9bda3fd56703ad62cd56e27a5af77d8b6b9612516457ae803cef0627bd919e4628 -PCRE2.v10.43.0+0.x86_64-unknown-freebsd.tar.gz/md5/6124870a991e70c2ed8a64d8f3258760 -PCRE2.v10.43.0+0.x86_64-unknown-freebsd.tar.gz/sha512/4645a2d05af149467f2e4ce5e48853b57c585d6a5950c70726d04bc71a5d82f50809af141ad98e99671e764ac74965651ecad1c49a849caa8fd077c7f4911c7c -PCRE2.v10.43.0+0.x86_64-w64-mingw32.tar.gz/md5/cc4e9f45471f538c1fefa657ab99b878 -PCRE2.v10.43.0+0.x86_64-w64-mingw32.tar.gz/sha512/eed45e621263cb307b6e8ab42e2c12cf9e1d61ad523760fd721a85765c359b74d580752ca7c3d222e0cba26a74e872a6d43dbf2dbf08e4733a3e709417e48651 +PCRE2.v10.43.0+1.aarch64-apple-darwin.tar.gz/md5/f1bee27b8d9465c14eaf9362701fb795 +PCRE2.v10.43.0+1.aarch64-apple-darwin.tar.gz/sha512/33b8f6e3703f0a52cd2d57897c28e35fb3c63af459296a2fef4e414dc99239617833b2ab176068d6aab690122a34a9ab9b6042dfff54b5a30ad60429a809818d +PCRE2.v10.43.0+1.aarch64-linux-gnu.tar.gz/md5/c55a569260e302f315f4a1bd185346ab +PCRE2.v10.43.0+1.aarch64-linux-gnu.tar.gz/sha512/be4d2883e69d562898a157424b2baa146fe79545a8c10935cf25b54e498ca2c14fae026fa0d958d175895fe2cb695d0f96ef7f09fecbf54e1cee4a55b81a382b +PCRE2.v10.43.0+1.aarch64-linux-musl.tar.gz/md5/fb041ccace415ccc26263968c6435a47 +PCRE2.v10.43.0+1.aarch64-linux-musl.tar.gz/sha512/06672ebe18e0f6bfa1dd2d5c02e10d9fd67236a73fd38ee2e8f4496d98f297f7866760f0be3b9cebeca348a5d748a3719e416b84cec96a90c71eac55afbbd905 +PCRE2.v10.43.0+1.aarch64-unknown-freebsd.tar.gz/md5/8c73fe6faa94102616cfafcc6cc1bf9d +PCRE2.v10.43.0+1.aarch64-unknown-freebsd.tar.gz/sha512/464a892e646fb5aa028d2e96e6f8beaa0c15f0ef56a6ba3388cba4ce85151448b0dfd51357a3e8dea4505957394ffbab14ceb29b9fc73a67e2b2f54dd28a7aed +PCRE2.v10.43.0+1.armv6l-linux-gnueabihf.tar.gz/md5/4f303a4cbf26abb7bf4ffb8bfe3d636d +PCRE2.v10.43.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/dddb3b227ee48d8329f6c65c5d0fce9f460eccaec98594a05bf28d1d9af01397cf7ef86c96e88b0e96030a7f6d8406461f78dd5fa558db8fc8f7bfb3b522ed54 +PCRE2.v10.43.0+1.armv6l-linux-musleabihf.tar.gz/md5/eade1fff90404bf3584fd15b62be0cfa +PCRE2.v10.43.0+1.armv6l-linux-musleabihf.tar.gz/sha512/351f6fa11c39b90fcc4086bd00b1b1126ed92272595f0b745757ca4e7e360c84d244446a871029245c3bcf838b23f42d908f858e44fae7deb9002a36cb76753c +PCRE2.v10.43.0+1.armv7l-linux-gnueabihf.tar.gz/md5/daa0a34b2cf0b71a6f8e1f9456cd4b06 +PCRE2.v10.43.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/ae72956ae7a9a5f315bfc816fdbb500937a170dfea306a28289ec9eac57d883cf2fa5a467ce9406eea80546b632a272c63bbb48b89ebe6d9f69d30366fd84180 +PCRE2.v10.43.0+1.armv7l-linux-musleabihf.tar.gz/md5/90bfb9e4efd7c92a2bb6a1a48fd88ecb +PCRE2.v10.43.0+1.armv7l-linux-musleabihf.tar.gz/sha512/147ac98d82fec4695de0c43c87d3d9242b9c024bc6df7ad7504d17ef6a12a029ed703c4deade0e2b24faf5283d66309f880d62f8c4834f27b2cc8889587d7abe +PCRE2.v10.43.0+1.i686-linux-gnu.tar.gz/md5/6fde649bf449c4122438fff32c0706ab +PCRE2.v10.43.0+1.i686-linux-gnu.tar.gz/sha512/edfaa15490497723c095eaa5df26194637b0606e9dce7b89b400024ef8ac42e21f010bb31c2cee5c735ce82fc8de0c42bf2b35b095a1e70a9a111d3bfba6da64 +PCRE2.v10.43.0+1.i686-linux-musl.tar.gz/md5/73aa8d13cc48338a5071e30b3a899109 +PCRE2.v10.43.0+1.i686-linux-musl.tar.gz/sha512/200e2d3ffd68f49b76c70a5be80cb0ae9703049214674485a2ab24abaaea7aefd6dec2042a14bd48cc52b04379f57322ec1e1788dc8c00896e1074921725d9cc +PCRE2.v10.43.0+1.i686-w64-mingw32.tar.gz/md5/4ddf0f31c97463e5216ed71afc4fb014 +PCRE2.v10.43.0+1.i686-w64-mingw32.tar.gz/sha512/75903d81668a66a5c4d830e31657391d507883943d86245998f224655406dcc6a95ba4f5fad20dcf608a98d6ccf49abe50107993448669b03c42a878d8466611 +PCRE2.v10.43.0+1.powerpc64le-linux-gnu.tar.gz/md5/64cb71080da1c97eba3a440ff53d298c +PCRE2.v10.43.0+1.powerpc64le-linux-gnu.tar.gz/sha512/16348b96a45c7a7d86775cb1d082b4d1c060e5a8acfb37554885d8da0db87430d8a40f834f008a90f4a7b1c07b8329df96836ba0430ecec506a143b7347bb101 +PCRE2.v10.43.0+1.x86_64-apple-darwin.tar.gz/md5/31bbb2485f5e06c3616fb061ffb2f022 +PCRE2.v10.43.0+1.x86_64-apple-darwin.tar.gz/sha512/3284ee63ed1e5631267efacb354a1d90bd1b7db0bc81d7233c9580eee4a9af06093c1c4f240786c34299df89a36a17ed92598fc302074f5a200c56cc96081bf1 +PCRE2.v10.43.0+1.x86_64-linux-gnu.tar.gz/md5/2fb7e0e9bbc32dddf543f4d395b50d3f +PCRE2.v10.43.0+1.x86_64-linux-gnu.tar.gz/sha512/5a533a3a01f817689077377835dc88edf914459ed0df7323f8f4dba602a47fd6af700075feb1f448221366b1cf7e2d717c615a5c506eb4ca2db9c600fd290fb0 +PCRE2.v10.43.0+1.x86_64-linux-musl.tar.gz/md5/b432063c93aa477dd0883428191041f8 +PCRE2.v10.43.0+1.x86_64-linux-musl.tar.gz/sha512/36475e90e29d7324046fe1da669fb37f667245a680df23f3978394964e14eb9bda3fd56703ad62cd56e27a5af77d8b6b9612516457ae803cef0627bd919e4628 +PCRE2.v10.43.0+1.x86_64-unknown-freebsd.tar.gz/md5/6124870a991e70c2ed8a64d8f3258760 +PCRE2.v10.43.0+1.x86_64-unknown-freebsd.tar.gz/sha512/4645a2d05af149467f2e4ce5e48853b57c585d6a5950c70726d04bc71a5d82f50809af141ad98e99671e764ac74965651ecad1c49a849caa8fd077c7f4911c7c +PCRE2.v10.43.0+1.x86_64-w64-mingw32.tar.gz/md5/cc4e9f45471f538c1fefa657ab99b878 +PCRE2.v10.43.0+1.x86_64-w64-mingw32.tar.gz/sha512/eed45e621263cb307b6e8ab42e2c12cf9e1d61ad523760fd721a85765c359b74d580752ca7c3d222e0cba26a74e872a6d43dbf2dbf08e4733a3e709417e48651 pcre2-10.43.tar.bz2/md5/c8e2043cbc4abb80e76dba323f7c409f pcre2-10.43.tar.bz2/sha512/8ac1520c32e9e5672404aaf6104e23c9ee5c3c28ad28ff101435599d813cbb20e0491a3fd34e012b4411b3e0366a4c6dfa3f02d093acaa6ff0ab25478bb7ade9 diff --git a/deps/checksums/suitesparse b/deps/checksums/suitesparse index acec99b39879c..bac143325196f 100644 --- a/deps/checksums/suitesparse +++ b/deps/checksums/suitesparse @@ -1,34 +1,36 @@ SuiteSparse-7.8.0.tar.gz/md5/ad42a80d28bb56a1fce15f6e7332e04e SuiteSparse-7.8.0.tar.gz/sha512/91aff0aee26e938ba88a8f92db15b0db0ecc6ada3b60153bb299f53a45ccda131db4bc66f890c220034c900180d0bb3a5fb3e2686fec7d6174f5900a3ee64424 -SuiteSparse.v7.8.0+0.aarch64-apple-darwin.tar.gz/md5/38379e14a53663a9c23f32ed56801676 -SuiteSparse.v7.8.0+0.aarch64-apple-darwin.tar.gz/sha512/3f2a7aa7778a22d150bad9ecb8d03edfa75707a07545e65660c8ccc4b0a9fb058ccab29e21e4728741d40d390d28922d521d3841e16258cf8e26acacadfc1fbd -SuiteSparse.v7.8.0+0.aarch64-linux-gnu.tar.gz/md5/bc52c7df0a442c0fb9aafb83d60878f4 -SuiteSparse.v7.8.0+0.aarch64-linux-gnu.tar.gz/sha512/436e79ea0774d6ffb571b513e385ef48d9cc70b72010cffdc23d606ad6c8984c8b49e2422ce8881def0722f3f608e4ecb87e6752dd80cf7988addd330c5ded13 -SuiteSparse.v7.8.0+0.aarch64-linux-musl.tar.gz/md5/87e4c2588efc39723621ac5010ddf2e5 -SuiteSparse.v7.8.0+0.aarch64-linux-musl.tar.gz/sha512/17115826716bb48f16e4593941be275d47012d112e54d8826c75fde119ffc9f66accd02353b309365b59779d7af3ac220f31ab7cf7eea165b209a93ecdc4102f -SuiteSparse.v7.8.0+0.armv6l-linux-gnueabihf.tar.gz/md5/b1490603aa129942d8e4c9581853cd0a -SuiteSparse.v7.8.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/e23c3532784e295ae72b811d285c3729c3f8ac1b5ee1621e831b6b2824a5b357e4bfa49e09174de7763fc3ebcab6b84ef16536bc1cf6f4bc0543b1b229209178 -SuiteSparse.v7.8.0+0.armv6l-linux-musleabihf.tar.gz/md5/f8199358882f76dd30bcce741b837de1 -SuiteSparse.v7.8.0+0.armv6l-linux-musleabihf.tar.gz/sha512/2c8d4ec21bfe253d3d32a5f5f09601b9b2864149f63f53067b157f5f7315fb04236bf5b19a1e5b4569e2c73127dcbb1703d56c7d06fc3ab9ae155902b7a1c2a9 -SuiteSparse.v7.8.0+0.armv7l-linux-gnueabihf.tar.gz/md5/cc3aa1a013cc91e7076dddf20fba9f60 -SuiteSparse.v7.8.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/a6b8cfbc345a089f12e55d8d44061dcce30f94c2d79fc520d6c5dfe433ac2e362d049fac72278cb59d4b3760ca08d5e350b7e2658fa5e8c77ce8608f67c2c4c4 -SuiteSparse.v7.8.0+0.armv7l-linux-musleabihf.tar.gz/md5/0d7797d31c30c53bf219cdc0a48e64dc -SuiteSparse.v7.8.0+0.armv7l-linux-musleabihf.tar.gz/sha512/a7df8938ee6a04f62169bedd29c8408951cf33a43e0f529fb4d1e360bdad6462a50b2af297adb5f51fd726e1ced1fc8fcda7feeeafbeb44000bfe02a8e29c29e -SuiteSparse.v7.8.0+0.i686-linux-gnu.tar.gz/md5/e48fa3d2e00f210e964c21e4ff27efae -SuiteSparse.v7.8.0+0.i686-linux-gnu.tar.gz/sha512/3088c2af476285eb8549cf6aa56381156d49513a274348f86fbf01aa9ce0712961471f83fa50b261f3f365a302b88eb20ef0bb35b58c07a2cfb5dc337fdb72c1 -SuiteSparse.v7.8.0+0.i686-linux-musl.tar.gz/md5/e55202dbeca107a0c25a4f09d5d68915 -SuiteSparse.v7.8.0+0.i686-linux-musl.tar.gz/sha512/0f4de2e62016914b4d1bcb9b13bd8cb2bebefc5f0a532e103948b9aae79a20462ac7b74a3e968d4f99076c37dbbafb747699cd151e831ff89d297f78478fb84f -SuiteSparse.v7.8.0+0.i686-w64-mingw32.tar.gz/md5/cb971bc1042196e527f95015c8bc5ef8 -SuiteSparse.v7.8.0+0.i686-w64-mingw32.tar.gz/sha512/d445a7790e3ac5392f75c9f4ec30cd1c812354b04388b4c6c6cea2423d2f0dac7173b17a8a2b7a7f4af10321601f96819a7702f9beac0397d85916d99493bc39 -SuiteSparse.v7.8.0+0.powerpc64le-linux-gnu.tar.gz/md5/12058f122b548a37070770d1847f3ce9 -SuiteSparse.v7.8.0+0.powerpc64le-linux-gnu.tar.gz/sha512/f375feeb8448ea90ce8d9f31c7e1230f6868316f06094ba0155069dded4f8da2e1b54d462ef9cfc77abd76147740d4066236dcf1fcea91f8a7141819962ad0ae -SuiteSparse.v7.8.0+0.x86_64-apple-darwin.tar.gz/md5/1bd473f2a25f1ebcea8acc858e2594b4 -SuiteSparse.v7.8.0+0.x86_64-apple-darwin.tar.gz/sha512/034af137deee5bf0ebf3746745d09ad50ce135cd4768a2049bb9811478ff90e6ed8e2c990e277b4c3b38a3a5e9eaa856938eb86239ca445fa64b6dab6af7e996 -SuiteSparse.v7.8.0+0.x86_64-linux-gnu.tar.gz/md5/c58a86d9f25e6705941105d9e41f084c -SuiteSparse.v7.8.0+0.x86_64-linux-gnu.tar.gz/sha512/56447062802f01815ffb014624423c6fd3ab6e16b642b2fe37972a151b02865965c95ca3d1a455c6d51cd31633aea8a732b235b55d68e6779c17b293c488fa43 -SuiteSparse.v7.8.0+0.x86_64-linux-musl.tar.gz/md5/ba6e10ba61c209df94f18ab51fe2dd90 -SuiteSparse.v7.8.0+0.x86_64-linux-musl.tar.gz/sha512/3b8fc504cfb4a3b628d5b955a482bad08c85e09e529f833855a84b847721247aaa469f96adef6b218a1ba5896cde91664cc819ba33115e3cc309e72140841ca3 -SuiteSparse.v7.8.0+0.x86_64-unknown-freebsd.tar.gz/md5/a50c69142a42c14edac4ce94b86b138a -SuiteSparse.v7.8.0+0.x86_64-unknown-freebsd.tar.gz/sha512/963be0dccd1a594df08fe5135ef4ac13e1d707841c3e97d31ba5477d0d6ec26bad9be1c52d9fd78f199740a53950353adbdd767469f3bf01ea1e3ee843eb6c1a -SuiteSparse.v7.8.0+0.x86_64-w64-mingw32.tar.gz/md5/7ca11ba89bd09183cc5a9320d6e8a4a7 -SuiteSparse.v7.8.0+0.x86_64-w64-mingw32.tar.gz/sha512/e1d5def1103bbf0bb29c08cdd3bf21ba60456353694985c66f8e55a31d54a32c5b891e56e1ffe30f9e1223c49283d267e483e2f1b999f566099c239b3eed1d78 +SuiteSparse.v7.8.0+1.aarch64-apple-darwin.tar.gz/md5/38379e14a53663a9c23f32ed56801676 +SuiteSparse.v7.8.0+1.aarch64-apple-darwin.tar.gz/sha512/3f2a7aa7778a22d150bad9ecb8d03edfa75707a07545e65660c8ccc4b0a9fb058ccab29e21e4728741d40d390d28922d521d3841e16258cf8e26acacadfc1fbd +SuiteSparse.v7.8.0+1.aarch64-linux-gnu.tar.gz/md5/bc52c7df0a442c0fb9aafb83d60878f4 +SuiteSparse.v7.8.0+1.aarch64-linux-gnu.tar.gz/sha512/436e79ea0774d6ffb571b513e385ef48d9cc70b72010cffdc23d606ad6c8984c8b49e2422ce8881def0722f3f608e4ecb87e6752dd80cf7988addd330c5ded13 +SuiteSparse.v7.8.0+1.aarch64-linux-musl.tar.gz/md5/87e4c2588efc39723621ac5010ddf2e5 +SuiteSparse.v7.8.0+1.aarch64-linux-musl.tar.gz/sha512/17115826716bb48f16e4593941be275d47012d112e54d8826c75fde119ffc9f66accd02353b309365b59779d7af3ac220f31ab7cf7eea165b209a93ecdc4102f +SuiteSparse.v7.8.0+1.aarch64-unknown-freebsd.tar.gz/md5/108a78ec5d21c910b1f0d3cd58b2b18e +SuiteSparse.v7.8.0+1.aarch64-unknown-freebsd.tar.gz/sha512/730f93e317305073acda619044296eb1844bc1380719a9c2f2f255bebd7c0c827317ff99ce06a081521f9441c3ca7fbcb2362a310ef3c5d289f485b2628c3d80 +SuiteSparse.v7.8.0+1.armv6l-linux-gnueabihf.tar.gz/md5/b1490603aa129942d8e4c9581853cd0a +SuiteSparse.v7.8.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/e23c3532784e295ae72b811d285c3729c3f8ac1b5ee1621e831b6b2824a5b357e4bfa49e09174de7763fc3ebcab6b84ef16536bc1cf6f4bc0543b1b229209178 +SuiteSparse.v7.8.0+1.armv6l-linux-musleabihf.tar.gz/md5/f8199358882f76dd30bcce741b837de1 +SuiteSparse.v7.8.0+1.armv6l-linux-musleabihf.tar.gz/sha512/2c8d4ec21bfe253d3d32a5f5f09601b9b2864149f63f53067b157f5f7315fb04236bf5b19a1e5b4569e2c73127dcbb1703d56c7d06fc3ab9ae155902b7a1c2a9 +SuiteSparse.v7.8.0+1.armv7l-linux-gnueabihf.tar.gz/md5/cc3aa1a013cc91e7076dddf20fba9f60 +SuiteSparse.v7.8.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/a6b8cfbc345a089f12e55d8d44061dcce30f94c2d79fc520d6c5dfe433ac2e362d049fac72278cb59d4b3760ca08d5e350b7e2658fa5e8c77ce8608f67c2c4c4 +SuiteSparse.v7.8.0+1.armv7l-linux-musleabihf.tar.gz/md5/0d7797d31c30c53bf219cdc0a48e64dc +SuiteSparse.v7.8.0+1.armv7l-linux-musleabihf.tar.gz/sha512/a7df8938ee6a04f62169bedd29c8408951cf33a43e0f529fb4d1e360bdad6462a50b2af297adb5f51fd726e1ced1fc8fcda7feeeafbeb44000bfe02a8e29c29e +SuiteSparse.v7.8.0+1.i686-linux-gnu.tar.gz/md5/e48fa3d2e00f210e964c21e4ff27efae +SuiteSparse.v7.8.0+1.i686-linux-gnu.tar.gz/sha512/3088c2af476285eb8549cf6aa56381156d49513a274348f86fbf01aa9ce0712961471f83fa50b261f3f365a302b88eb20ef0bb35b58c07a2cfb5dc337fdb72c1 +SuiteSparse.v7.8.0+1.i686-linux-musl.tar.gz/md5/e55202dbeca107a0c25a4f09d5d68915 +SuiteSparse.v7.8.0+1.i686-linux-musl.tar.gz/sha512/0f4de2e62016914b4d1bcb9b13bd8cb2bebefc5f0a532e103948b9aae79a20462ac7b74a3e968d4f99076c37dbbafb747699cd151e831ff89d297f78478fb84f +SuiteSparse.v7.8.0+1.i686-w64-mingw32.tar.gz/md5/e8f4de53ec4ae74554e76bd52702d7a4 +SuiteSparse.v7.8.0+1.i686-w64-mingw32.tar.gz/sha512/f944f14e62408f04a9966cd927cbbbe26b00a4beccc85ab8923dc4028875b0395c6b5e56efba1fd2f29fb954543ca83e800685ffafcdfdd97351a7d4926349a8 +SuiteSparse.v7.8.0+1.powerpc64le-linux-gnu.tar.gz/md5/12058f122b548a37070770d1847f3ce9 +SuiteSparse.v7.8.0+1.powerpc64le-linux-gnu.tar.gz/sha512/f375feeb8448ea90ce8d9f31c7e1230f6868316f06094ba0155069dded4f8da2e1b54d462ef9cfc77abd76147740d4066236dcf1fcea91f8a7141819962ad0ae +SuiteSparse.v7.8.0+1.x86_64-apple-darwin.tar.gz/md5/1bd473f2a25f1ebcea8acc858e2594b4 +SuiteSparse.v7.8.0+1.x86_64-apple-darwin.tar.gz/sha512/034af137deee5bf0ebf3746745d09ad50ce135cd4768a2049bb9811478ff90e6ed8e2c990e277b4c3b38a3a5e9eaa856938eb86239ca445fa64b6dab6af7e996 +SuiteSparse.v7.8.0+1.x86_64-linux-gnu.tar.gz/md5/c58a86d9f25e6705941105d9e41f084c +SuiteSparse.v7.8.0+1.x86_64-linux-gnu.tar.gz/sha512/56447062802f01815ffb014624423c6fd3ab6e16b642b2fe37972a151b02865965c95ca3d1a455c6d51cd31633aea8a732b235b55d68e6779c17b293c488fa43 +SuiteSparse.v7.8.0+1.x86_64-linux-musl.tar.gz/md5/ba6e10ba61c209df94f18ab51fe2dd90 +SuiteSparse.v7.8.0+1.x86_64-linux-musl.tar.gz/sha512/3b8fc504cfb4a3b628d5b955a482bad08c85e09e529f833855a84b847721247aaa469f96adef6b218a1ba5896cde91664cc819ba33115e3cc309e72140841ca3 +SuiteSparse.v7.8.0+1.x86_64-unknown-freebsd.tar.gz/md5/a50c69142a42c14edac4ce94b86b138a +SuiteSparse.v7.8.0+1.x86_64-unknown-freebsd.tar.gz/sha512/963be0dccd1a594df08fe5135ef4ac13e1d707841c3e97d31ba5477d0d6ec26bad9be1c52d9fd78f199740a53950353adbdd767469f3bf01ea1e3ee843eb6c1a +SuiteSparse.v7.8.0+1.x86_64-w64-mingw32.tar.gz/md5/7ca11ba89bd09183cc5a9320d6e8a4a7 +SuiteSparse.v7.8.0+1.x86_64-w64-mingw32.tar.gz/sha512/e1d5def1103bbf0bb29c08cdd3bf21ba60456353694985c66f8e55a31d54a32c5b891e56e1ffe30f9e1223c49283d267e483e2f1b999f566099c239b3eed1d78 diff --git a/deps/checksums/unwind b/deps/checksums/unwind index 317809053abeb..5d4967cb0cf22 100644 --- a/deps/checksums/unwind +++ b/deps/checksums/unwind @@ -1,26 +1,28 @@ -LibUnwind.v1.8.1+1.aarch64-linux-gnu.tar.gz/md5/0f789b9e5b2604a39cc363c4c513a808 -LibUnwind.v1.8.1+1.aarch64-linux-gnu.tar.gz/sha512/4c9c8250bfd84a96135a5e9ecdd4500214996c39852609d3a3983c2c5de44a728d9ce6b71bd649c1725e186db077f74df93a99f07452a31d344c17315eedb33d -LibUnwind.v1.8.1+1.aarch64-linux-musl.tar.gz/md5/356deb10e57d4c7e7bf7dbc728d6628d -LibUnwind.v1.8.1+1.aarch64-linux-musl.tar.gz/sha512/a998eebe7a4928bd417620bef0de9728c080f5d9714f15314ac190b333efa1bd7a21207156d56c132515bd3f7154d60204f1fac2dac5468560a7017682527c78 -LibUnwind.v1.8.1+1.armv6l-linux-gnueabihf.tar.gz/md5/b0ff12f5f0c801e5e280a142a1b7a188 -LibUnwind.v1.8.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/68003f39eaf55c8742e821a228889590e8673cbafb74013a5b4f6a0c08ee372cb6b102a574e89ce9f46a38dd3d31ef75de95762f72a31a8ec9d7f495affaeb77 -LibUnwind.v1.8.1+1.armv6l-linux-musleabihf.tar.gz/md5/b04c77d707875989777ecfed66bd2dad -LibUnwind.v1.8.1+1.armv6l-linux-musleabihf.tar.gz/sha512/fb20586a0cbc998a0482d4102d8b8e5b2f802af519e25c440a64f67554468b29c6999a9ec5509ba375714beb93a4b48e8dbf71e6089c25ecd63b11eead844041 -LibUnwind.v1.8.1+1.armv7l-linux-gnueabihf.tar.gz/md5/e948016b4179d34727b456bc768cd8e1 -LibUnwind.v1.8.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/6fc64e8ac7248540b95c321103d234f2c8633087f261e368251fe2cf6ea4e0654325716ac7017ae966edc4ddbb004a0f808d6e25cca766faaf505ca1f8f4aee7 -LibUnwind.v1.8.1+1.armv7l-linux-musleabihf.tar.gz/md5/660cf49c34a2ead1afbdcb44491e174a -LibUnwind.v1.8.1+1.armv7l-linux-musleabihf.tar.gz/sha512/edf337d176440c210f5860e90771758335256fe9d2f179d506656bccf92a9f9aa478d176d4b0db2213945ae847dad5bb88265110c92cfcd538d5740858b6a3f0 -LibUnwind.v1.8.1+1.i686-linux-gnu.tar.gz/md5/7032a70cfecb88cdd49cc3a4879456c6 -LibUnwind.v1.8.1+1.i686-linux-gnu.tar.gz/sha512/e34acc8f270c5156ede3ac3377d0f428c672daed869570734351c6b5a8946d65b5c0c041b713dddefedef81e55c65f5683aed0fec0d366e2d0207d8b902b0e33 -LibUnwind.v1.8.1+1.i686-linux-musl.tar.gz/md5/0541c3419020334173d299cf3482ff85 -LibUnwind.v1.8.1+1.i686-linux-musl.tar.gz/sha512/0b57745d280fb9893772936cd4872b0e04f41d86379e772b889e75baffe9324ef8dd168bb4c9761c1b8372f387ce99721dd6086b1d52b9a91215f40e8113968d -LibUnwind.v1.8.1+1.powerpc64le-linux-gnu.tar.gz/md5/fee37734fe95d1e96ebc77316df64192 -LibUnwind.v1.8.1+1.powerpc64le-linux-gnu.tar.gz/sha512/953ef70fb203db73764eeab0a37521b94e79ce70644ae16fe3157ca8d1011a0319d1928d094a3e2ed1e0489fdc0ca7dda33722095fd3aa40ed1fde150cf44c2a -LibUnwind.v1.8.1+1.x86_64-linux-gnu.tar.gz/md5/bbb201e7455fd13b805b0a96dc16183b -LibUnwind.v1.8.1+1.x86_64-linux-gnu.tar.gz/sha512/b1e21f7d772bd15bada17d287e1876ae586a97c6a8669e714347e7bf8a9b202fe53e8559cf19358f88bc458b2fe15ccbd616b64163cc715ce253f43f5133a8cd -LibUnwind.v1.8.1+1.x86_64-linux-musl.tar.gz/md5/72156f9d6da9a2742d9152822e5525f5 -LibUnwind.v1.8.1+1.x86_64-linux-musl.tar.gz/sha512/53a3f1985c5ae4816693f292604810cbe948e6332aeb227fb900ba3730f4379e863b144ae87af2c0651c2b9633b35c45c7a0a6fa34958dc9f58e0f8baa2ea701 -LibUnwind.v1.8.1+1.x86_64-unknown-freebsd.tar.gz/md5/e4346df03246d847f2867df3ab5ac624 -LibUnwind.v1.8.1+1.x86_64-unknown-freebsd.tar.gz/sha512/ee01bc12726288ae091476c1bed44de224a9ef5355687fd6fd64742da6628450434d7f33d4daf81029263aa6d23549a0aa5c5ae656599c132051255d1d742d5d +LibUnwind.v1.8.1+2.aarch64-linux-gnu.tar.gz/md5/de3690f3a8ecf0aa5d2525813bdab3c8 +LibUnwind.v1.8.1+2.aarch64-linux-gnu.tar.gz/sha512/366090b4291623603e54d3c73437efcbc3c7f52ce0c64a63e8439eff8a3ddeb4efc1ab6b2513e0a60e2714239bf259cd667159a24207f0c9ce3134530e539155 +LibUnwind.v1.8.1+2.aarch64-linux-musl.tar.gz/md5/e8adf4e842e998b6806653964e721a47 +LibUnwind.v1.8.1+2.aarch64-linux-musl.tar.gz/sha512/77411646767f5f13e2f45d32bfa48d6864b712d46d339e3fd4d62d12f4a26b6ffb8293636209ee5645d8e5552bdf70db5a848736ef0df75db74c8c878553cd40 +LibUnwind.v1.8.1+2.aarch64-unknown-freebsd.tar.gz/md5/ee8fc39c934cf1c640ae4ae41addcc30 +LibUnwind.v1.8.1+2.aarch64-unknown-freebsd.tar.gz/sha512/6245fc3003ef24fce0f84007c0fa1390658e71dc64da6a2f5d296d3928351096ed2c0c83808890413332883abe5fcee7615eb40b2baeddfc56d3484315f3dacf +LibUnwind.v1.8.1+2.armv6l-linux-gnueabihf.tar.gz/md5/4c454e174be7b5f220f4cb8f659722d8 +LibUnwind.v1.8.1+2.armv6l-linux-gnueabihf.tar.gz/sha512/f6e3d83576ae963f400972250c8558b0b15bdd9657aac6eacbd0c3f59af6a3574d0cc475c6e606ad8f2e0b178ba33f297aec0aeac8a5970d93b2c36d9ffae59d +LibUnwind.v1.8.1+2.armv6l-linux-musleabihf.tar.gz/md5/dbec8675d2b73807c9d9e3afc2ce2260 +LibUnwind.v1.8.1+2.armv6l-linux-musleabihf.tar.gz/sha512/45d9ac63282c21bdc6488b65fae8f03bbaa55d18b346ac3fc3d40f38ebd05b2a0db539f23dc6c6f88bbbad8f2ec2cdcf677db1acff83a99d9875bee93555ad1e +LibUnwind.v1.8.1+2.armv7l-linux-gnueabihf.tar.gz/md5/98517b7a4ae874099ef0aafb46e740c9 +LibUnwind.v1.8.1+2.armv7l-linux-gnueabihf.tar.gz/sha512/3a00792415a15fe45c3454f9bf480222862217178a61db0738837537c7e2c50f71b53063facd591680b14e7b3bde218c34cee9b2854ad94897b306388749af1b +LibUnwind.v1.8.1+2.armv7l-linux-musleabihf.tar.gz/md5/f276569278383f7711f40e623670620d +LibUnwind.v1.8.1+2.armv7l-linux-musleabihf.tar.gz/sha512/48160616ac1ed4b3e343556517e3cbb4959e80e9be237fc820e33e06f6668e95d9365dd7c86e68dc898fee1141cd825495bbbc27d685913a2f2808d974b54c19 +LibUnwind.v1.8.1+2.i686-linux-gnu.tar.gz/md5/2cd0203f2b70436ac2323077dad1d5d1 +LibUnwind.v1.8.1+2.i686-linux-gnu.tar.gz/sha512/fa42b3306d9b67011468b2c07bdb6cca6847f0f1632ee4aca3212c5944e991f9a1ae8f881fb4ce86e641e977695942d873a39fc212bdcf6acdf3e12c24b31d8e +LibUnwind.v1.8.1+2.i686-linux-musl.tar.gz/md5/3c456a1b3da2f5d785e02e1b6cb4cd74 +LibUnwind.v1.8.1+2.i686-linux-musl.tar.gz/sha512/fce8368ee670109b681c9d442ad89fee8fdf8eac1e115407784d1e8b82cfb98acd9d2edb4dbea29f8c63c83054da2a4d34149fe231655e2535834a4ef7319666 +LibUnwind.v1.8.1+2.powerpc64le-linux-gnu.tar.gz/md5/73b04ae80ca9fdbe06b3eeaae40d5dc5 +LibUnwind.v1.8.1+2.powerpc64le-linux-gnu.tar.gz/sha512/d4083a696a3492ced38b05fb573d44c4cc2b5332a351b65be2c3992d9e932bb6ea71f48260c643fa54219adb800b5da41160e1d56b0d9145061edf2e5dfc0ef6 +LibUnwind.v1.8.1+2.x86_64-linux-gnu.tar.gz/md5/f9d6132f4166c5ede15b2303280a1066 +LibUnwind.v1.8.1+2.x86_64-linux-gnu.tar.gz/sha512/124159e7d13ce1caee5e2527746ec98b10a776f57e5f9c99053b7ab76e7d9447b998cbc044da7671fd39356445a983f16f2c7bbefc076b29e45d2c2bb4d0364e +LibUnwind.v1.8.1+2.x86_64-linux-musl.tar.gz/md5/665d9215ef915269e009f7dde1f827b3 +LibUnwind.v1.8.1+2.x86_64-linux-musl.tar.gz/sha512/2d8754bbfa7a4b576fb58a2d22b08940bb9f615988bfc388e9ea2cc96e3a573e6c31a4023b2509a3424a0ce3d946584c09ac5d18e4bca6f0f47e52597e193944 +LibUnwind.v1.8.1+2.x86_64-unknown-freebsd.tar.gz/md5/cc8149747db86524da0c9749ed538f3d +LibUnwind.v1.8.1+2.x86_64-unknown-freebsd.tar.gz/sha512/4d416999616fbf08103553aa43603ce62109c21e9a97d6a391fb267c04d382834da380f459c96412773f19d93b8e996ddd405831623ce118d239ad1a0d9025fd libunwind-1.8.1.tar.gz/md5/10c96118ff30b88c9eeb6eac8e75599d libunwind-1.8.1.tar.gz/sha512/aba7b578c1b8cbe78f05b64e154f3530525f8a34668b2a9f1ee6acb4b22c857befe34ad4e9e8cca99dbb66689d41bc72060a8f191bd8be232725d342809431b3 diff --git a/deps/checksums/zlib b/deps/checksums/zlib index b6fc106747c67..f5e7353f32e3e 100644 --- a/deps/checksums/zlib +++ b/deps/checksums/zlib @@ -1,34 +1,36 @@ -Zlib.v1.3.1+0.aarch64-apple-darwin.tar.gz/md5/50b48e14f0b3578e3f398d130749a25d -Zlib.v1.3.1+0.aarch64-apple-darwin.tar.gz/sha512/d970e183035b3615b410f7b0da2c7a1d516234744491d65ed1ebc3800b55732f20bf00fcbb0cf91289b8b4660915282873fb23788896713cf8dfae2984a8fd85 -Zlib.v1.3.1+0.aarch64-linux-gnu.tar.gz/md5/ee42c0bae86fc39968c8cd6a77a801bf -Zlib.v1.3.1+0.aarch64-linux-gnu.tar.gz/sha512/5d21cbeab03d44008c6cbad114d45c917ebee2fe98de6b19686f4f6ba1fc67eeedf968b94ed1c2d4efb89e93be9efa342bcc8a57cb8a505085d177abae14bc2d -Zlib.v1.3.1+0.aarch64-linux-musl.tar.gz/md5/9091d1288736b218f7b016791dc1a9c8 -Zlib.v1.3.1+0.aarch64-linux-musl.tar.gz/sha512/b49cbfe734beb2af9ef8e847542d006765345cbb08aee0854779e35e03c98df25c93539b046547c6b66029987c49499ddf6cb207824b1e376900bfceaa79691a -Zlib.v1.3.1+0.armv6l-linux-gnueabihf.tar.gz/md5/b686c85047b7dad2c2f08d1d16e7978a -Zlib.v1.3.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/511fda619519dccedb264988e3b59a0e0fbf8f73d3ae290f238346209ebc0202a22f945257cea19afef64246574285e0322901a46bb48d7b48364c1e2eacd801 -Zlib.v1.3.1+0.armv6l-linux-musleabihf.tar.gz/md5/374be5cb926876f3f0492cfe0e193220 -Zlib.v1.3.1+0.armv6l-linux-musleabihf.tar.gz/sha512/4d3a2cc0c7c48146e63ed098da5a5acad75517197adc965550c123f7f8bcee0811a27be76fa37b6b0515eee4b5ba1c1a85c854e7b23bea36b5e21671805bedce -Zlib.v1.3.1+0.armv7l-linux-gnueabihf.tar.gz/md5/9febbc6a3d492e34c9ed53c95f3b799f -Zlib.v1.3.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/4cee0e2cf572eb91028a09ef356e1aa6360949e046ceec03bd37574295ddcc4a7cefca9276f7565f152697d55b35f62af2ab107cdbf402b42846818629fea9c7 -Zlib.v1.3.1+0.armv7l-linux-musleabihf.tar.gz/md5/5d0d59a6cbbd1e63193ba6f7dbb755f9 -Zlib.v1.3.1+0.armv7l-linux-musleabihf.tar.gz/sha512/ee3f48b354168342ef63509b19a26aca3301fb3e5f4f6898afe2d3b44ee3380515efd6ced5d4e06e69736d851d19352deb9595bad82c051caccaee8c55e629d8 -Zlib.v1.3.1+0.i686-linux-gnu.tar.gz/md5/834350a64b2302a9caf0250a8f6068e5 -Zlib.v1.3.1+0.i686-linux-gnu.tar.gz/sha512/63dc158c4dfc42db97875893fcdd9784d9487af855bd576dbe04d1b967ad64510222df74a4cfb1b7e67386329d2a5686d7931b81720883fc1924f0d706a0a711 -Zlib.v1.3.1+0.i686-linux-musl.tar.gz/md5/e4f96efdeafa3d74c7c348059a8dc46a -Zlib.v1.3.1+0.i686-linux-musl.tar.gz/sha512/b47a571d94887ddcab8d7b50c6dce3afed3f56513a9d1859feaefebfad4a271d428b440df1d19ef3c2ed01ca4c8fd121ffc1572f5e252f27d0930f616cb47f18 -Zlib.v1.3.1+0.i686-w64-mingw32.tar.gz/md5/6bc27bd7dbbe17243dbbfaff225d3b23 -Zlib.v1.3.1+0.i686-w64-mingw32.tar.gz/sha512/5777661682831519875fffbb114c62596bf7bdb62011667c0f3dc5da9910e14de2285200a0a05187769b9c68c99b07024caafc16fef03c76e96e618f77149790 -Zlib.v1.3.1+0.powerpc64le-linux-gnu.tar.gz/md5/27dcad8557994cfd89d6fa7072bb843c -Zlib.v1.3.1+0.powerpc64le-linux-gnu.tar.gz/sha512/3b388dd286b273881d4344cff61c7da316c2bd2bab93072bf47ce4cb1cf9662158351b8febb0d5b1f8dfd9bc73cd32f7cae37fdd19b0ca91531bd3375df104bb -Zlib.v1.3.1+0.x86_64-apple-darwin.tar.gz/md5/9187319377191ae8b34162b375baa5db -Zlib.v1.3.1+0.x86_64-apple-darwin.tar.gz/sha512/895203434f161926978be52a223dd49a99454651a79c1c5e0529fa064f3f7ac2d7a069fed47a577b32523df22afadd6eb97d564dbd59c5d67ed90083add13c00 -Zlib.v1.3.1+0.x86_64-linux-gnu.tar.gz/md5/55d4d982d60cb643aa8688eb031b07ee -Zlib.v1.3.1+0.x86_64-linux-gnu.tar.gz/sha512/d8f94d22ffc37df027de23b2408c2000014c8b7b6c8539feca669ac1f2dbbe1679ca534c3be4d32c90fe38bbba27c795689226962fb067346b5ca213e64b9c4b -Zlib.v1.3.1+0.x86_64-linux-musl.tar.gz/md5/95d735bba178da4b8bee23903419919c -Zlib.v1.3.1+0.x86_64-linux-musl.tar.gz/sha512/370370f08133a720e3fbedcc434f102dc95225fda3ec8a399e782851bd4be57fb2b64a3ed62dc0559fb0c58d2e28db9b9e960efafd940982e4cb6652be0e81f1 -Zlib.v1.3.1+0.x86_64-unknown-freebsd.tar.gz/md5/df158f50fdb8ac1179fe6dad3bc62713 -Zlib.v1.3.1+0.x86_64-unknown-freebsd.tar.gz/sha512/f4ba4ccfeaf3fd2e172a2d5b3b1ae083ee9854022e71e062e29423e4179cb1fc49b2b99df49b3f5f231e2a0c5becc59b89644e9dcaf0fda9c97e83af7ea1c25d -Zlib.v1.3.1+0.x86_64-w64-mingw32.tar.gz/md5/9cc735c54ddf5d1ea0db60e05d6631ea -Zlib.v1.3.1+0.x86_64-w64-mingw32.tar.gz/sha512/8a2fd20944866cb7f717517ea0b80a134466e063f85bec87ffba56ca844f983f91060dfdc65f8faee1981d7329348c827b723aaad4fea36041e710b9e35c43de +Zlib.v1.3.1+1.aarch64-apple-darwin.tar.gz/md5/50b48e14f0b3578e3f398d130749a25d +Zlib.v1.3.1+1.aarch64-apple-darwin.tar.gz/sha512/d970e183035b3615b410f7b0da2c7a1d516234744491d65ed1ebc3800b55732f20bf00fcbb0cf91289b8b4660915282873fb23788896713cf8dfae2984a8fd85 +Zlib.v1.3.1+1.aarch64-linux-gnu.tar.gz/md5/ee42c0bae86fc39968c8cd6a77a801bf +Zlib.v1.3.1+1.aarch64-linux-gnu.tar.gz/sha512/5d21cbeab03d44008c6cbad114d45c917ebee2fe98de6b19686f4f6ba1fc67eeedf968b94ed1c2d4efb89e93be9efa342bcc8a57cb8a505085d177abae14bc2d +Zlib.v1.3.1+1.aarch64-linux-musl.tar.gz/md5/9091d1288736b218f7b016791dc1a9c8 +Zlib.v1.3.1+1.aarch64-linux-musl.tar.gz/sha512/b49cbfe734beb2af9ef8e847542d006765345cbb08aee0854779e35e03c98df25c93539b046547c6b66029987c49499ddf6cb207824b1e376900bfceaa79691a +Zlib.v1.3.1+1.aarch64-unknown-freebsd.tar.gz/md5/c73793872e3a2259519276b3ab2899ce +Zlib.v1.3.1+1.aarch64-unknown-freebsd.tar.gz/sha512/ce1e3ed5dfb01653471ace4c0cb2d8b521ccd02bc2a2c537e433a0dc497906ad21008645c645f2e0f2bb1f39c40e9a68d8cca0aeddc74ade0e188dc80748c2e8 +Zlib.v1.3.1+1.armv6l-linux-gnueabihf.tar.gz/md5/b686c85047b7dad2c2f08d1d16e7978a +Zlib.v1.3.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/511fda619519dccedb264988e3b59a0e0fbf8f73d3ae290f238346209ebc0202a22f945257cea19afef64246574285e0322901a46bb48d7b48364c1e2eacd801 +Zlib.v1.3.1+1.armv6l-linux-musleabihf.tar.gz/md5/374be5cb926876f3f0492cfe0e193220 +Zlib.v1.3.1+1.armv6l-linux-musleabihf.tar.gz/sha512/4d3a2cc0c7c48146e63ed098da5a5acad75517197adc965550c123f7f8bcee0811a27be76fa37b6b0515eee4b5ba1c1a85c854e7b23bea36b5e21671805bedce +Zlib.v1.3.1+1.armv7l-linux-gnueabihf.tar.gz/md5/9febbc6a3d492e34c9ed53c95f3b799f +Zlib.v1.3.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/4cee0e2cf572eb91028a09ef356e1aa6360949e046ceec03bd37574295ddcc4a7cefca9276f7565f152697d55b35f62af2ab107cdbf402b42846818629fea9c7 +Zlib.v1.3.1+1.armv7l-linux-musleabihf.tar.gz/md5/5d0d59a6cbbd1e63193ba6f7dbb755f9 +Zlib.v1.3.1+1.armv7l-linux-musleabihf.tar.gz/sha512/ee3f48b354168342ef63509b19a26aca3301fb3e5f4f6898afe2d3b44ee3380515efd6ced5d4e06e69736d851d19352deb9595bad82c051caccaee8c55e629d8 +Zlib.v1.3.1+1.i686-linux-gnu.tar.gz/md5/834350a64b2302a9caf0250a8f6068e5 +Zlib.v1.3.1+1.i686-linux-gnu.tar.gz/sha512/63dc158c4dfc42db97875893fcdd9784d9487af855bd576dbe04d1b967ad64510222df74a4cfb1b7e67386329d2a5686d7931b81720883fc1924f0d706a0a711 +Zlib.v1.3.1+1.i686-linux-musl.tar.gz/md5/e4f96efdeafa3d74c7c348059a8dc46a +Zlib.v1.3.1+1.i686-linux-musl.tar.gz/sha512/b47a571d94887ddcab8d7b50c6dce3afed3f56513a9d1859feaefebfad4a271d428b440df1d19ef3c2ed01ca4c8fd121ffc1572f5e252f27d0930f616cb47f18 +Zlib.v1.3.1+1.i686-w64-mingw32.tar.gz/md5/aaa1500c06b280d142e2900dbedf2a8f +Zlib.v1.3.1+1.i686-w64-mingw32.tar.gz/sha512/bc6668baf33bc8e130ae6a72f6cd89d9f1ccc95d2f3a3bcef20cde03ed7602de511f7646feed918918a24d8a2221a0be39eb2c0884c1adb6fe0d67b91cceb683 +Zlib.v1.3.1+1.powerpc64le-linux-gnu.tar.gz/md5/27dcad8557994cfd89d6fa7072bb843c +Zlib.v1.3.1+1.powerpc64le-linux-gnu.tar.gz/sha512/3b388dd286b273881d4344cff61c7da316c2bd2bab93072bf47ce4cb1cf9662158351b8febb0d5b1f8dfd9bc73cd32f7cae37fdd19b0ca91531bd3375df104bb +Zlib.v1.3.1+1.x86_64-apple-darwin.tar.gz/md5/9187319377191ae8b34162b375baa5db +Zlib.v1.3.1+1.x86_64-apple-darwin.tar.gz/sha512/895203434f161926978be52a223dd49a99454651a79c1c5e0529fa064f3f7ac2d7a069fed47a577b32523df22afadd6eb97d564dbd59c5d67ed90083add13c00 +Zlib.v1.3.1+1.x86_64-linux-gnu.tar.gz/md5/55d4d982d60cb643aa8688eb031b07ee +Zlib.v1.3.1+1.x86_64-linux-gnu.tar.gz/sha512/d8f94d22ffc37df027de23b2408c2000014c8b7b6c8539feca669ac1f2dbbe1679ca534c3be4d32c90fe38bbba27c795689226962fb067346b5ca213e64b9c4b +Zlib.v1.3.1+1.x86_64-linux-musl.tar.gz/md5/95d735bba178da4b8bee23903419919c +Zlib.v1.3.1+1.x86_64-linux-musl.tar.gz/sha512/370370f08133a720e3fbedcc434f102dc95225fda3ec8a399e782851bd4be57fb2b64a3ed62dc0559fb0c58d2e28db9b9e960efafd940982e4cb6652be0e81f1 +Zlib.v1.3.1+1.x86_64-unknown-freebsd.tar.gz/md5/df158f50fdb8ac1179fe6dad3bc62713 +Zlib.v1.3.1+1.x86_64-unknown-freebsd.tar.gz/sha512/f4ba4ccfeaf3fd2e172a2d5b3b1ae083ee9854022e71e062e29423e4179cb1fc49b2b99df49b3f5f231e2a0c5becc59b89644e9dcaf0fda9c97e83af7ea1c25d +Zlib.v1.3.1+1.x86_64-w64-mingw32.tar.gz/md5/9cc735c54ddf5d1ea0db60e05d6631ea +Zlib.v1.3.1+1.x86_64-w64-mingw32.tar.gz/sha512/8a2fd20944866cb7f717517ea0b80a134466e063f85bec87ffba56ca844f983f91060dfdc65f8faee1981d7329348c827b723aaad4fea36041e710b9e35c43de zlib-51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf.tar.gz/md5/7ce1b2766499af7d948130113b649028 zlib-51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf.tar.gz/sha512/79d032b8c93260ce6b9806f2289cdccce67e9d80865b5bb39ac46dadffc8ee009da51c551eead59c56249c7adfa164c1d5ebcf2b10a8645e0b11b5650176cb24 diff --git a/deps/clang.version b/deps/clang.version index fcd55b72de5ff..0f49ecdd649f0 100644 --- a/deps/clang.version +++ b/deps/clang.version @@ -3,4 +3,4 @@ ## jll artifact # Clang (paired with LLVM, only here as a JLL download) CLANG_JLL_NAME := Clang -CLANG_JLL_VER := 18.1.7+2 +CLANG_JLL_VER := 18.1.7+3 diff --git a/deps/lld.version b/deps/lld.version index 3ca9960164e27..8c7008fc93d7d 100644 --- a/deps/lld.version +++ b/deps/lld.version @@ -2,4 +2,4 @@ ## jll artifact LLD_JLL_NAME := LLD -LLD_JLL_VER := 18.1.7+2 +LLD_JLL_VER := 18.1.7+3 diff --git a/deps/llvm-tools.version b/deps/llvm-tools.version index 1fcc8944dc769..8a1159fd69174 100644 --- a/deps/llvm-tools.version +++ b/deps/llvm-tools.version @@ -3,5 +3,5 @@ ## jll artifact # LLVM_tools (downloads LLVM_jll to get things like `lit` and `opt`) LLVM_TOOLS_JLL_NAME := LLVM -LLVM_TOOLS_JLL_VER := 18.1.7+2 -LLVM_TOOLS_ASSERT_JLL_VER := 18.1.7+2 +LLVM_TOOLS_JLL_VER := 18.1.7+3 +LLVM_TOOLS_ASSERT_JLL_VER := 18.1.7+3 diff --git a/deps/llvm.version b/deps/llvm.version index 8e4180ef5a277..be03d1529ce7c 100644 --- a/deps/llvm.version +++ b/deps/llvm.version @@ -2,7 +2,7 @@ ## jll artifact LLVM_JLL_NAME := libLLVM -LLVM_ASSERT_JLL_VER := 18.1.7+2 +LLVM_ASSERT_JLL_VER := 18.1.7+3 ## source build # Version number of LLVM LLVM_VER := 18.1.7 diff --git a/deps/nghttp2.version b/deps/nghttp2.version index e9587297d0e32..c9a39ea5ae757 100644 --- a/deps/nghttp2.version +++ b/deps/nghttp2.version @@ -3,4 +3,4 @@ NGHTTP2_JLL_NAME := nghttp2 ## source build -NGHTTP2_VER := 1.60.0 +NGHTTP2_VER := 1.63.0 diff --git a/stdlib/CompilerSupportLibraries_jll/Project.toml b/stdlib/CompilerSupportLibraries_jll/Project.toml index 5aab865b5f6fc..12806e4bc427a 100644 --- a/stdlib/CompilerSupportLibraries_jll/Project.toml +++ b/stdlib/CompilerSupportLibraries_jll/Project.toml @@ -4,7 +4,7 @@ uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" # NOTE: When updating this, also make sure to update the value # `CSL_NEXT_GLIBCXX_VERSION` in `Make.inc`, to properly disable # automatic usage of BB-built CSLs on extremely up-to-date systems! -version = "1.1.1+0" +version = "1.2.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/GMP_jll/Project.toml b/stdlib/GMP_jll/Project.toml index c8fcfe4f2b845..3a6fa12c95aef 100644 --- a/stdlib/GMP_jll/Project.toml +++ b/stdlib/GMP_jll/Project.toml @@ -1,6 +1,6 @@ name = "GMP_jll" uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.3.0+0" +version = "6.3.0+1" [deps] Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" diff --git a/stdlib/LLD_jll/Project.toml b/stdlib/LLD_jll/Project.toml index 6a6cc72aa3c62..1aafd275d99b7 100644 --- a/stdlib/LLD_jll/Project.toml +++ b/stdlib/LLD_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLD_jll" uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "18.1.7+2" +version = "18.1.7+3" [deps] Zlib_jll = "83775a58-1f1d-513f-b197-d71354ab007a" diff --git a/stdlib/LibUV_jll/Project.toml b/stdlib/LibUV_jll/Project.toml index fb03c6b996048..74aae1c9249df 100644 --- a/stdlib/LibUV_jll/Project.toml +++ b/stdlib/LibUV_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUV_jll" uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+18" +version = "2.0.1+19" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/LibUnwind_jll/Project.toml b/stdlib/LibUnwind_jll/Project.toml index 03ccfcd1449d8..b43f1c537ce5a 100644 --- a/stdlib/LibUnwind_jll/Project.toml +++ b/stdlib/LibUnwind_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUnwind_jll" uuid = "745a5e78-f969-53e9-954f-d19f2f74f4e3" -version = "1.8.1+1" +version = "1.8.1+2" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/MPFR_jll/Project.toml b/stdlib/MPFR_jll/Project.toml index eaa8d0988b2ca..a9987ccfa38f6 100644 --- a/stdlib/MPFR_jll/Project.toml +++ b/stdlib/MPFR_jll/Project.toml @@ -1,6 +1,6 @@ name = "MPFR_jll" uuid = "3a97d323-0669-5f0c-9066-3539efd106a3" -version = "4.2.1+0" +version = "4.2.1+1" [deps] GMP_jll = "781609d7-10c4-51f6-84f2-b8444358ff6d" diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index f9fb307190838..8953aa93ce4b2 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -23,7 +23,7 @@ version = "1.11.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" +version = "1.2.0+0" [[deps.Dates]] deps = ["Printf"] @@ -58,7 +58,7 @@ version = "1.11.0" [[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.3.0+0" +version = "6.3.0+1" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -73,7 +73,7 @@ version = "1.12.0" [[deps.LLD_jll]] deps = ["Artifacts", "Libdl", "Zlib_jll", "libLLVM_jll"] uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "18.1.7+2" +version = "18.1.7+3" [[deps.LLVMLibUnwind_jll]] deps = ["Artifacts", "Libdl"] @@ -113,12 +113,12 @@ version = "1.11.0+1" [[deps.LibUV_jll]] deps = ["Artifacts", "Libdl"] uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+17" +version = "2.0.1+19" [[deps.LibUnwind_jll]] deps = ["Artifacts", "Libdl"] uuid = "745a5e78-f969-53e9-954f-d19f2f74f4e3" -version = "1.8.1+1" +version = "1.8.1+2" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -136,7 +136,7 @@ version = "1.11.0" [[deps.MPFR_jll]] deps = ["Artifacts", "GMP_jll", "Libdl"] uuid = "3a97d323-0669-5f0c-9066-3539efd106a3" -version = "4.2.1+0" +version = "4.2.1+1" [[deps.Markdown]] deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] @@ -146,7 +146,7 @@ version = "1.11.0" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" +version = "2.28.6+1" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -163,17 +163,17 @@ version = "1.2.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.28+2" +version = "0.3.28+3" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.1+3" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.43.0+0" +version = "10.43.0+1" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] @@ -243,7 +243,7 @@ version = "1.11.0" [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.8.0+0" +version = "7.8.0+1" [[deps.TOML]] deps = ["Dates"] @@ -272,17 +272,17 @@ version = "1.11.0" [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.3.1+0" +version = "1.3.1+1" [[deps.dSFMT_jll]] deps = ["Artifacts", "Libdl"] uuid = "05ff407c-b0c1-5878-9df8-858cc2e60c36" -version = "2.2.5+0" +version = "2.2.5+1" [[deps.libLLVM_jll]] deps = ["Artifacts", "Libdl"] uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "18.1.7+2" +version = "18.1.7+3" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] @@ -292,9 +292,9 @@ version = "5.11.0+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.60.0+0" +version = "1.63.0+1" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.5.0+0" +version = "17.5.0+1" diff --git a/stdlib/MbedTLS_jll/Project.toml b/stdlib/MbedTLS_jll/Project.toml index 1fe9b5e702c61..61f3ea0d8b4dc 100644 --- a/stdlib/MbedTLS_jll/Project.toml +++ b/stdlib/MbedTLS_jll/Project.toml @@ -1,6 +1,6 @@ name = "MbedTLS_jll" uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" +version = "2.28.6+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index a9a1a04facff5..01e3af1d9467c 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.28+2" +version = "0.3.28+3" [deps] # See note in `src/OpenBLAS_jll.jl` about this dependency. diff --git a/stdlib/OpenLibm_jll/Project.toml b/stdlib/OpenLibm_jll/Project.toml index f6162f402bfcf..a4c559e1ff4ef 100644 --- a/stdlib/OpenLibm_jll/Project.toml +++ b/stdlib/OpenLibm_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenLibm_jll" uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.1+3" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/PCRE2_jll/Project.toml b/stdlib/PCRE2_jll/Project.toml index f9b3affb51b63..ae1fb74922d79 100644 --- a/stdlib/PCRE2_jll/Project.toml +++ b/stdlib/PCRE2_jll/Project.toml @@ -1,6 +1,6 @@ name = "PCRE2_jll" uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.43.0+0" +version = "10.43.0+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/SuiteSparse_jll/Project.toml b/stdlib/SuiteSparse_jll/Project.toml index 39b8447138a2d..c91ef6743d653 100644 --- a/stdlib/SuiteSparse_jll/Project.toml +++ b/stdlib/SuiteSparse_jll/Project.toml @@ -1,6 +1,6 @@ name = "SuiteSparse_jll" uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.8.0+0" +version = "7.8.0+1" [deps] libblastrampoline_jll = "8e850b90-86db-534c-a0d3-1478176c7d93" diff --git a/stdlib/Zlib_jll/Project.toml b/stdlib/Zlib_jll/Project.toml index bb5771654430b..dfe9ce845c8e0 100644 --- a/stdlib/Zlib_jll/Project.toml +++ b/stdlib/Zlib_jll/Project.toml @@ -1,6 +1,6 @@ name = "Zlib_jll" uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.3.1+0" +version = "1.3.1+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/dSFMT_jll/Project.toml b/stdlib/dSFMT_jll/Project.toml index 0db19e602f67b..ca51184b75264 100644 --- a/stdlib/dSFMT_jll/Project.toml +++ b/stdlib/dSFMT_jll/Project.toml @@ -1,6 +1,6 @@ name = "dSFMT_jll" uuid = "05ff407c-b0c1-5878-9df8-858cc2e60c36" -version = "2.2.5+0" +version = "2.2.5+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/libLLVM_jll/Project.toml b/stdlib/libLLVM_jll/Project.toml index a0eac13b3ab23..13669ec173678 100644 --- a/stdlib/libLLVM_jll/Project.toml +++ b/stdlib/libLLVM_jll/Project.toml @@ -1,6 +1,6 @@ name = "libLLVM_jll" uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "18.1.7+2" +version = "18.1.7+3" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/nghttp2_jll/Project.toml b/stdlib/nghttp2_jll/Project.toml index 88e60941f65ee..acc9444ab4a26 100644 --- a/stdlib/nghttp2_jll/Project.toml +++ b/stdlib/nghttp2_jll/Project.toml @@ -1,6 +1,6 @@ name = "nghttp2_jll" uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.60.0+0" +version = "1.63.0+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/nghttp2_jll/test/runtests.jl b/stdlib/nghttp2_jll/test/runtests.jl index b6ddefb8222cd..d667ce53e5252 100644 --- a/stdlib/nghttp2_jll/test/runtests.jl +++ b/stdlib/nghttp2_jll/test/runtests.jl @@ -11,5 +11,5 @@ end @testset "nghttp2_jll" begin info = unsafe_load(ccall((:nghttp2_version,libnghttp2), Ptr{nghttp2_info}, (Cint,), 0)) - @test VersionNumber(unsafe_string(info.version_str)) == v"1.60.0" + @test VersionNumber(unsafe_string(info.version_str)) == v"1.63.0" end diff --git a/stdlib/p7zip_jll/Project.toml b/stdlib/p7zip_jll/Project.toml index 6bca9d1d0545b..09a39880af418 100644 --- a/stdlib/p7zip_jll/Project.toml +++ b/stdlib/p7zip_jll/Project.toml @@ -1,6 +1,6 @@ name = "p7zip_jll" uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.5.0+0" +version = "17.5.0+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 969754d54a65c92a50ef6d8f87355bca91d0615c Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sun, 13 Oct 2024 18:02:43 +0200 Subject: [PATCH 376/548] typo in `Compiler.Effects` doc string: `checkbounds` -> `boundscheck` (#56140) Follows up on #56060 --- base/compiler/effects.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index b22b9396408e3..fb35162134ffa 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -47,7 +47,7 @@ following meanings: * `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 - under the assumption that its `@checkbounds` code is not elided (which happens when the + under the assumption that its `@boundscheck` code is not elided (which happens when the caller does not set nor propagate the `@inbounds` context) 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, From 7f97a9d0aa617c3c8bcc9b3cbc7408dbb923f236 Mon Sep 17 00:00:00 2001 From: cyhan Date: Mon, 14 Oct 2024 00:54:57 +0800 Subject: [PATCH 377/548] HISTORY: fix missing links (#56137) --- HISTORY.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7fb01c7e9a0e9..6142747273864 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -221,6 +221,7 @@ Tooling Improvements [#35856]: https://github.com/JuliaLang/julia/issues/35856 [#38064]: https://github.com/JuliaLang/julia/issues/38064 [#43845]: https://github.com/JuliaLang/julia/issues/43845 +[#45641]: https://github.com/JuliaLang/julia/issues/45641 [#46501]: https://github.com/JuliaLang/julia/issues/46501 [#47354]: https://github.com/JuliaLang/julia/issues/47354 [#47679]: https://github.com/JuliaLang/julia/issues/47679 @@ -238,6 +239,7 @@ Tooling Improvements [#50661]: https://github.com/JuliaLang/julia/issues/50661 [#50795]: https://github.com/JuliaLang/julia/issues/50795 [#50797]: https://github.com/JuliaLang/julia/issues/50797 +[#50864]: https://github.com/JuliaLang/julia/issues/50864 [#50958]: https://github.com/JuliaLang/julia/issues/50958 [#51229]: https://github.com/JuliaLang/julia/issues/51229 [#51416]: https://github.com/JuliaLang/julia/issues/51416 @@ -251,12 +253,10 @@ Tooling Improvements [#51799]: https://github.com/JuliaLang/julia/issues/51799 [#51897]: https://github.com/JuliaLang/julia/issues/51897 [#51929]: https://github.com/JuliaLang/julia/issues/51929 -[#52049]: https://github.com/JuliaLang/julia/issues/52049 [#52096]: https://github.com/JuliaLang/julia/issues/52096 [#52123]: https://github.com/JuliaLang/julia/issues/52123 [#52139]: https://github.com/JuliaLang/julia/issues/52139 [#52180]: https://github.com/JuliaLang/julia/issues/52180 -[#52196]: https://github.com/JuliaLang/julia/issues/52196 [#52400]: https://github.com/JuliaLang/julia/issues/52400 [#52413]: https://github.com/JuliaLang/julia/issues/52413 [#52461]: https://github.com/JuliaLang/julia/issues/52461 @@ -270,6 +270,7 @@ Tooling Improvements [#52898]: https://github.com/JuliaLang/julia/issues/52898 [#52957]: https://github.com/JuliaLang/julia/issues/52957 [#53262]: https://github.com/JuliaLang/julia/issues/53262 +[#53352]: https://github.com/JuliaLang/julia/issues/53352 Julia v1.10 Release Notes @@ -416,7 +417,6 @@ Deprecated or removed [#44247]: https://github.com/JuliaLang/julia/issues/44247 [#45164]: https://github.com/JuliaLang/julia/issues/45164 [#45396]: https://github.com/JuliaLang/julia/issues/45396 -[#45641]: https://github.com/JuliaLang/julia/issues/45641 [#45962]: https://github.com/JuliaLang/julia/issues/45962 [#46196]: https://github.com/JuliaLang/julia/issues/46196 [#46372]: https://github.com/JuliaLang/julia/issues/46372 @@ -433,6 +433,7 @@ Deprecated or removed [#48899]: https://github.com/JuliaLang/julia/issues/48899 [#48979]: https://github.com/JuliaLang/julia/issues/48979 [#49020]: https://github.com/JuliaLang/julia/issues/49020 +[#49052]: https://github.com/JuliaLang/julia/issues/49052 [#49110]: https://github.com/JuliaLang/julia/issues/49110 [#49266]: https://github.com/JuliaLang/julia/issues/49266 [#49405]: https://github.com/JuliaLang/julia/issues/49405 @@ -656,11 +657,13 @@ Tooling Improvements [#42902]: https://github.com/JuliaLang/julia/issues/42902 [#43270]: https://github.com/JuliaLang/julia/issues/43270 [#43334]: https://github.com/JuliaLang/julia/issues/43334 +[#43536]: https://github.com/JuliaLang/julia/issues/43536 [#44137]: https://github.com/JuliaLang/julia/issues/44137 [#44266]: https://github.com/JuliaLang/julia/issues/44266 [#44358]: https://github.com/JuliaLang/julia/issues/44358 [#44360]: https://github.com/JuliaLang/julia/issues/44360 [#44512]: https://github.com/JuliaLang/julia/issues/44512 +[#44527]: https://github.com/JuliaLang/julia/issues/44527 [#44534]: https://github.com/JuliaLang/julia/issues/44534 [#44571]: https://github.com/JuliaLang/julia/issues/44571 [#44714]: https://github.com/JuliaLang/julia/issues/44714 @@ -690,6 +693,8 @@ Tooling Improvements [#46609]: https://github.com/JuliaLang/julia/issues/46609 [#46862]: https://github.com/JuliaLang/julia/issues/46862 [#46976]: https://github.com/JuliaLang/julia/issues/46976 +[#47117]: https://github.com/JuliaLang/julia/issues/47117 +[#47184]: https://github.com/JuliaLang/julia/issues/47184 [#47367]: https://github.com/JuliaLang/julia/issues/47367 [#47392]: https://github.com/JuliaLang/julia/issues/47392 @@ -984,6 +989,7 @@ Tooling Improvements [#43919]: https://github.com/JuliaLang/julia/issues/43919 [#44080]: https://github.com/JuliaLang/julia/issues/44080 [#44136]: https://github.com/JuliaLang/julia/issues/44136 +[#45064]: https://github.com/JuliaLang/julia/issues/45064 Julia v1.7 Release Notes ======================== @@ -1711,9 +1717,9 @@ Tooling Improvements [#37753]: https://github.com/JuliaLang/julia/issues/37753 [#37829]: https://github.com/JuliaLang/julia/issues/37829 [#37844]: https://github.com/JuliaLang/julia/issues/37844 +[#37928]: https://github.com/JuliaLang/julia/issues/37928 [#37973]: https://github.com/JuliaLang/julia/issues/37973 [#38042]: https://github.com/JuliaLang/julia/issues/38042 -[#38062]: https://github.com/JuliaLang/julia/issues/38062 [#38168]: https://github.com/JuliaLang/julia/issues/38168 [#38449]: https://github.com/JuliaLang/julia/issues/38449 [#38475]: https://github.com/JuliaLang/julia/issues/38475 @@ -1956,6 +1962,7 @@ Tooling Improvements [#25930]: https://github.com/JuliaLang/julia/issues/25930 [#26872]: https://github.com/JuliaLang/julia/issues/26872 [#28789]: https://github.com/JuliaLang/julia/issues/28789 +[#28811]: https://github.com/JuliaLang/julia/issues/28811 [#29240]: https://github.com/JuliaLang/julia/issues/29240 [#29333]: https://github.com/JuliaLang/julia/issues/29333 [#29411]: https://github.com/JuliaLang/julia/issues/29411 @@ -1971,6 +1978,7 @@ Tooling Improvements [#33864]: https://github.com/JuliaLang/julia/issues/33864 [#33886]: https://github.com/JuliaLang/julia/issues/33886 [#33937]: https://github.com/JuliaLang/julia/issues/33937 +[#34126]: https://github.com/JuliaLang/julia/issues/34126 [#34149]: https://github.com/JuliaLang/julia/issues/34149 [#34199]: https://github.com/JuliaLang/julia/issues/34199 [#34200]: https://github.com/JuliaLang/julia/issues/34200 @@ -1997,9 +2005,12 @@ Tooling Improvements [#34896]: https://github.com/JuliaLang/julia/issues/34896 [#34953]: https://github.com/JuliaLang/julia/issues/34953 [#35001]: https://github.com/JuliaLang/julia/issues/35001 +[#35057]: https://github.com/JuliaLang/julia/issues/35057 [#35078]: https://github.com/JuliaLang/julia/issues/35078 +[#35085]: https://github.com/JuliaLang/julia/issues/35085 [#35094]: https://github.com/JuliaLang/julia/issues/35094 [#35108]: https://github.com/JuliaLang/julia/issues/35108 +[#35113]: https://github.com/JuliaLang/julia/issues/35113 [#35124]: https://github.com/JuliaLang/julia/issues/35124 [#35132]: https://github.com/JuliaLang/julia/issues/35132 [#35138]: https://github.com/JuliaLang/julia/issues/35138 @@ -2310,6 +2321,7 @@ Tooling Improvements [#32534]: https://github.com/JuliaLang/julia/issues/32534 [#32600]: https://github.com/JuliaLang/julia/issues/32600 [#32628]: https://github.com/JuliaLang/julia/issues/32628 +[#32651]: https://github.com/JuliaLang/julia/issues/32651 [#32653]: https://github.com/JuliaLang/julia/issues/32653 [#32729]: https://github.com/JuliaLang/julia/issues/32729 [#32814]: https://github.com/JuliaLang/julia/issues/32814 @@ -2319,6 +2331,7 @@ Tooling Improvements [#32851]: https://github.com/JuliaLang/julia/issues/32851 [#32872]: https://github.com/JuliaLang/julia/issues/32872 [#32875]: https://github.com/JuliaLang/julia/issues/32875 +[#32918]: https://github.com/JuliaLang/julia/issues/32918 Julia v1.2 Release Notes ======================== @@ -2461,6 +2474,7 @@ External dependencies [#31009]: https://github.com/JuliaLang/julia/issues/31009 [#31125]: https://github.com/JuliaLang/julia/issues/31125 [#31211]: https://github.com/JuliaLang/julia/issues/31211 +[#31223]: https://github.com/JuliaLang/julia/issues/31223 [#31230]: https://github.com/JuliaLang/julia/issues/31230 [#31235]: https://github.com/JuliaLang/julia/issues/31235 [#31310]: https://github.com/JuliaLang/julia/issues/31310 @@ -4428,6 +4442,7 @@ Command-line option changes [#26932]: https://github.com/JuliaLang/julia/issues/26932 [#26935]: https://github.com/JuliaLang/julia/issues/26935 [#26980]: https://github.com/JuliaLang/julia/issues/26980 +[#26991]: https://github.com/JuliaLang/julia/issues/26991 [#26997]: https://github.com/JuliaLang/julia/issues/26997 [#27067]: https://github.com/JuliaLang/julia/issues/27067 [#27071]: https://github.com/JuliaLang/julia/issues/27071 @@ -4462,6 +4477,7 @@ Command-line option changes [#28155]: https://github.com/JuliaLang/julia/issues/28155 [#28266]: https://github.com/JuliaLang/julia/issues/28266 [#28302]: https://github.com/JuliaLang/julia/issues/28302 +[#28310]: https://github.com/JuliaLang/julia/issues/28310 Julia v0.6.0 Release Notes ========================== From 60291736aa81fa5b3a850cd7358fe0ae155b4f9d Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sun, 13 Oct 2024 19:27:22 +0200 Subject: [PATCH 378/548] OpenBLAS: Fix cross-compilation detection for source build. (#56139) We may be cross-compiling Linux-to-Linux, in which case `BUILD_OS` == `OS`, so look at `XC_HOST` to determine whether we're cross compiling. --- deps/openblas.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/openblas.mk b/deps/openblas.mk index affd1c7a7aa55..8cea044ca348f 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -43,7 +43,7 @@ OPENBLAS_FFLAGS := $(JFFLAGS) $(USE_BLAS_FFLAGS) OPENBLAS_CFLAGS := -O2 # Decide whether to build for 32-bit or 64-bit arch -ifneq ($(BUILD_OS),$(OS)) +ifneq ($(XC_HOST),) OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC=$(HOSTCC) CROSS_SUFFIX=$(CROSS_COMPILE) endif ifeq ($(OS),WINNT) From 67c93b9a19cfe00211be595b2d5c50cfcfcece09 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 13 Oct 2024 23:24:25 +0530 Subject: [PATCH 379/548] `diag` for `BandedMatrix`es for off-limit bands (#56065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, one can only obtain the `diag` for a `BandedMatrix` (such as a `Diagonal`) when the band index is bounded by the size of the matrix. This PR relaxes this requirement to match the behavior for arrays, where `diag` returns an empty vector for a large band index instead of throwing an error. ```julia julia> D = Diagonal(ones(4)) 4×4 Diagonal{Float64, Vector{Float64}}: 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 ⋅ ⋅ ⋅ ⋅ 1.0 julia> diag(D, 10) Float64[] julia> diag(Array(D), 10) Float64[] ``` Something similar for `SymTridiagonal` is being done in https://github.com/JuliaLang/julia/pull/56014 --- stdlib/LinearAlgebra/src/bidiag.jl | 11 ++++------- stdlib/LinearAlgebra/src/diagonal.jl | 11 ++++------- stdlib/LinearAlgebra/src/tridiag.jl | 13 +++++-------- stdlib/LinearAlgebra/test/bidiag.jl | 4 ++-- stdlib/LinearAlgebra/test/diagonal.jl | 4 ++-- stdlib/LinearAlgebra/test/tridiag.jl | 13 ++++--------- 6 files changed, 21 insertions(+), 35 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 381afd2f09a61..a34df37153cd2 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -404,20 +404,17 @@ end function diag(M::Bidiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n + v = similar(M.dv, max(0, length(M.dv)-abs(n))) if n == 0 - return copyto!(similar(M.dv, length(M.dv)), M.dv) + copyto!(v, M.dv) elseif (n == 1 && M.uplo == 'U') || (n == -1 && M.uplo == 'L') - return copyto!(similar(M.ev, length(M.ev)), M.ev) + copyto!(v, M.ev) elseif -size(M,1) <= n <= size(M,1) - v = similar(M.dv, size(M,1)-abs(n)) for i in eachindex(v) v[i] = M[BandIndex(n,i)] end - return v - else - 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 + return v end function +(A::Bidiagonal, B::Bidiagonal) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 0c93024f33a9a..aabfb3e8ba114 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -773,18 +773,15 @@ permutedims(D::Diagonal, perm) = (Base.checkdims_perm(axes(D), axes(D), perm); D function diag(D::Diagonal, k::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of k + v = similar(D.diag, max(0, length(D.diag)-abs(k))) if k == 0 - return copyto!(similar(D.diag, length(D.diag)), D.diag) - elseif -size(D,1) <= k <= size(D,1) - v = similar(D.diag, size(D,1)-abs(k)) + copyto!(v, D.diag) + else for i in eachindex(v) v[i] = D[BandIndex(k, i)] end - return v - else - throw(ArgumentError(LazyString(lazy"requested diagonal, $k, must be at least $(-size(D, 1)) ", - lazy"and at most $(size(D, 2)) for an $(size(D, 1))-by-$(size(D, 2)) matrix"))) end + return v end tr(D::Diagonal) = sum(tr, D.diag) det(D::Diagonal) = prod(det, D.diag) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index c1af12514e020..d6382d2e16a43 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -662,22 +662,19 @@ issymmetric(S::Tridiagonal) = all(issymmetric, S.d) && all(Iterators.map((x, y) function diag(M::Tridiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n + v = similar(M.d, max(0, length(M.d)-abs(n))) if n == 0 - return copyto!(similar(M.d, length(M.d)), M.d) + copyto!(v, M.d) elseif n == -1 - return copyto!(similar(M.dl, length(M.dl)), M.dl) + copyto!(v, M.dl) elseif n == 1 - return copyto!(similar(M.du, length(M.du)), M.du) + copyto!(v, M.du) elseif abs(n) <= size(M,1) - v = similar(M.d, size(M,1)-abs(n)) for i in eachindex(v) v[i] = M[BandIndex(n,i)] end - return v - else - 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 + return v end @inline function Base.isassigned(A::Tridiagonal, i::Int, j::Int) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 628e59debe8b7..df30748e042b5 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -398,8 +398,8 @@ Random.seed!(1) @test (@inferred diag(T))::typeof(dv) == dv @test (@inferred diag(T, uplo === :U ? 1 : -1))::typeof(dv) == ev @test (@inferred diag(T,2))::typeof(dv) == zeros(elty, n-2) - @test_throws ArgumentError diag(T, -n - 1) - @test_throws ArgumentError diag(T, n + 1) + @test isempty(@inferred diag(T, -n - 1)) + @test isempty(@inferred diag(T, n + 1)) # test diag with another wrapped vector type gdv, gev = GenericArray(dv), GenericArray(ev) G = Bidiagonal(gdv, gev, uplo) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 85fe963e3592b..f7a9ccb705de9 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -109,8 +109,8 @@ Random.seed!(1) end @testset "diag" begin - @test_throws ArgumentError diag(D, n+1) - @test_throws ArgumentError diag(D, -n-1) + @test isempty(@inferred diag(D, n+1)) + @test isempty(@inferred diag(D, -n-1)) @test (@inferred diag(D))::typeof(dd) == dd @test (@inferred diag(D, 0))::typeof(dd) == dd @test (@inferred diag(D, 1))::typeof(dd) == zeros(elty, n-1) diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index b1d52ab8c5679..aa3baec8f6be8 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -287,13 +287,8 @@ end @test (@inferred diag(A, 1))::typeof(d) == (mat_type == Tridiagonal ? du : dl) @test (@inferred diag(A, -1))::typeof(d) == dl @test (@inferred diag(A, n-1))::typeof(d) == zeros(elty, 1) - if A isa SymTridiagonal - @test isempty(@inferred diag(A, -n - 1)) - @test isempty(@inferred diag(A, n + 1)) - else - @test_throws ArgumentError diag(A, -n - 1) - @test_throws ArgumentError diag(A, n + 1) - end + @test isempty(@inferred diag(A, -n - 1)) + @test isempty(@inferred diag(A, n + 1)) GA = mat_type == Tridiagonal ? mat_type(GenericArray.((dl, d, du))...) : mat_type(GenericArray.((d, dl))...) @test (@inferred diag(GA))::typeof(GenericArray(d)) == GenericArray(d) @test (@inferred diag(GA, -1))::typeof(GenericArray(d)) == GenericArray(dl) @@ -527,8 +522,8 @@ end @test @inferred diag(A, -1) == fill(M, n-1) @test_broken diag(A, -2) == fill(M, n-2) @test_broken diag(A, 2) == fill(M, n-2) - @test_throws ArgumentError diag(A, n+1) - @test_throws ArgumentError diag(A, -n-1) + @test isempty(@inferred diag(A, n+1)) + @test isempty(@inferred diag(A, -n-1)) for n in 0:2 dv, ev = fill(M, n), fill(M, max(n-1,0)) From 7241673d1858731e2f4a0c62dad6c0f64fded110 Mon Sep 17 00:00:00 2001 From: Christian Guinard <28689358+christiangnrd@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:21:45 -0300 Subject: [PATCH 380/548] Port progress bar improvements from Pkg (#56125) Includes changes from https://github.com/JuliaLang/Pkg.jl/pull/4038 and https://github.com/JuliaLang/Pkg.jl/pull/4044. Co-authored-by: Kristoffer Carlsson --- base/precompilation.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index ea98b0c415ab4..7a821222c52d1 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -297,7 +297,8 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere end termwidth = @something termwidth displaysize(io)[2] max_progress_width = max(0, min(termwidth - textwidth(p.header) - textwidth(progress_text) - 10 , p.width)) - n_filled = ceil(Int, max_progress_width * perc / 100) + n_filled = floor(Int, max_progress_width * perc / 100) + partial_filled = (max_progress_width * perc / 100) - n_filled n_left = max_progress_width - n_filled headers = split(p.header, ' ') to_print = sprint(; context=io) do io @@ -306,8 +307,15 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere printstyled(io, join(headers[2:end], ' ')) print(io, " ") printstyled(io, "━"^n_filled; color=p.color) - printstyled(io, perc >= 95 ? "━" : "╸"; color=p.color) - printstyled(io, "━"^n_left, " "; color=:light_black) + if n_left > 0 + if partial_filled > 0.5 + printstyled(io, "╸"; color=p.color) # More filled, use ╸ + else + printstyled(io, "╺"; color=:light_black) # Less filled, use ╺ + end + printstyled(io, "━"^(n_left-1); color=:light_black) + end + printstyled(io, " "; color=:light_black) print(io, progress_text) carriagereturn && print(io, "\r") end From 35bf824e85af9e5f762c3c59d0e5b28aa2c3ab4b Mon Sep 17 00:00:00 2001 From: Zentrik Date: Sun, 13 Oct 2024 21:05:03 +0100 Subject: [PATCH 381/548] Add support for LLVM 19 (#55650) Co-authored-by: Zentrik --- deps/llvm.mk | 2 + src/clangsa/GCChecker.cpp | 6 +-- src/debuginfo.cpp | 6 +-- src/disasm.cpp | 51 +++++++++++++++++++++----- src/features_x86.h | 6 +-- src/jitlayers.cpp | 2 +- src/llvm-multiversioning.cpp | 1 + src/llvm-simdloop.cpp | 4 +- src/llvmcalltest.cpp | 1 + src/processor.cpp | 4 ++ src/processor_x86.cpp | 8 +--- test/llvmpasses/multiversioning-x86.ll | 22 +++++------ 12 files changed, 74 insertions(+), 39 deletions(-) diff --git a/deps/llvm.mk b/deps/llvm.mk index 3f4bc3e6746f0..09dd4f187d611 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -234,7 +234,9 @@ $$(LLVM_BUILDDIR_withtype)/build-compiled: $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patc LLVM_PATCH_PREV := $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patch-applied endef +ifeq ($(shell test $(LLVM_VER_MAJ) -lt 19 && echo true),true) $(eval $(call LLVM_PATCH,llvm-ittapi-cmake)) +endif ifeq ($(USE_SYSTEM_ZLIB), 0) $(LLVM_BUILDDIR_withtype)/build-configured: | $(build_prefix)/manifest/zlib diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index ecaeb460ebf91..31631eb70a4ad 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -865,7 +865,7 @@ bool GCChecker::isGCTracked(const Expr *E) { bool GCChecker::isGloballyRootedType(QualType QT) const { return isJuliaType( - [](StringRef Name) { return Name.endswith("jl_sym_t"); }, QT); + [](StringRef Name) { return Name.ends_with("jl_sym_t"); }, QT); } bool GCChecker::isSafepoint(const CallEvent &Call, CheckerContext &C) const { @@ -1166,10 +1166,10 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent, // TODO: We may want to refine this. This is to track pointers through the // array list in jl_module_t. bool ParentIsModule = isJuliaType( - [](StringRef Name) { return Name.endswith("jl_module_t"); }, + [](StringRef Name) { return Name.ends_with("jl_module_t"); }, Parent->getType()); bool ResultIsArrayList = isJuliaType( - [](StringRef Name) { return Name.endswith("arraylist_t"); }, + [](StringRef Name) { return Name.ends_with("arraylist_t"); }, Result->getType()); if (!(ParentIsModule && ResultIsArrayList) && isGCTracked(Parent)) { ResultTracked = false; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index cfaf8d4c70ee9..f6fca47e9a889 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -296,7 +296,7 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, uint8_t *catchjmp = NULL; for (const object::SymbolRef &sym_iter : Object.symbols()) { StringRef sName = cantFail(sym_iter.getName()); - if (sName.equals("__UnwindData") || sName.equals("__catchjmp")) { + if (sName == "__UnwindData" || sName == "__catchjmp") { uint64_t Addr = cantFail(sym_iter.getAddress()); // offset into object (including section offset) auto Section = cantFail(sym_iter.getSection()); assert(Section != EndSection && Section->isText()); @@ -310,10 +310,10 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, SectionAddrCheck = SectionAddr; SectionLoadCheck = SectionLoadAddr; Addr += SectionLoadAddr - SectionAddr; - if (sName.equals("__UnwindData")) { + if (sName == "__UnwindData") { UnwindData = (uint8_t*)Addr; } - else if (sName.equals("__catchjmp")) { + else if (sName == "__catchjmp") { catchjmp = (uint8_t*)Addr; } } diff --git a/src/disasm.cpp b/src/disasm.cpp index b71503c3f7a77..ebe8f2ac397c0 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -103,6 +103,7 @@ // for outputting assembly #include #include +#include #include #include #include @@ -920,11 +921,17 @@ static void jl_dump_asm_internal( // LLVM will destroy the formatted stream, and we keep the raw stream. std::unique_ptr ustream(new formatted_raw_ostream(rstream)); std::unique_ptr Streamer( - TheTarget->createAsmStreamer(Ctx, std::move(ustream), /*asmverbose*/true, - /*useDwarfDirectory*/ true, - IP.release(), - std::move(CE), std::move(MAB), - /*ShowInst*/ false)); +#if JL_LLVM_VERSION >= 190000 + TheTarget->createAsmStreamer(Ctx, std::move(ustream), + + IP.release(), std::move(CE), std::move(MAB)) +#else + TheTarget->createAsmStreamer(Ctx, std::move(ustream), /*asmverbose*/ true, + /*useDwarfDirectory*/ true, IP.release(), + std::move(CE), std::move(MAB), + /*ShowInst*/ false) +#endif + ); Streamer->initSections(true, *STI); // Make the MemoryObject wrapper @@ -1148,7 +1155,11 @@ addPassesToGenerateCode(LLVMTargetMachine *TM, PassManagerBase &PM) { return &MMIWP->getMMI().getContext(); } +#if JL_LLVM_VERSION >= 190000 +class LineNumberPrinterHandler : public DebugHandlerBase { +#else class LineNumberPrinterHandler : public AsmPrinterHandler { +#endif MCStreamer &S; LineNumberAnnotatedWriter LinePrinter; std::string Buffer; @@ -1157,7 +1168,11 @@ class LineNumberPrinterHandler : public AsmPrinterHandler { public: LineNumberPrinterHandler(AsmPrinter &Printer, const char *debuginfo) - : S(*Printer.OutStreamer), + : +#if JL_LLVM_VERSION >= 190000 + DebugHandlerBase(&Printer), +#endif + S(*Printer.OutStreamer), LinePrinter("; ", true, debuginfo), RawStream(Buffer), Stream(RawStream) {} @@ -1176,12 +1191,20 @@ class LineNumberPrinterHandler : public AsmPrinterHandler { //virtual void beginModule(Module *M) override {} virtual void endModule() override {} /// note that some AsmPrinter implementations may not call beginFunction at all +#if JL_LLVM_VERSION >= 190000 + virtual void beginFunctionImpl(const MachineFunction *MF) override { +#else virtual void beginFunction(const MachineFunction *MF) override { +#endif LinePrinter.emitFunctionAnnot(&MF->getFunction(), Stream); emitAndReset(); } //virtual void markFunctionEnd() override {} +#if JL_LLVM_VERSION >= 190000 + virtual void endFunctionImpl(const MachineFunction *MF) override { +#else virtual void endFunction(const MachineFunction *MF) override { +#endif LinePrinter.emitEnd(Stream); emitAndReset(); } @@ -1257,15 +1280,23 @@ jl_value_t *jl_dump_function_asm_impl(jl_llvmf_dump_t* dump, char emit_mc, const } auto FOut = std::make_unique(asmfile); std::unique_ptr S(TM->getTarget().createAsmStreamer( - *Context, std::move(FOut), true, - true, InstPrinter, - std::move(MCE), std::move(MAB), - false)); +#if JL_LLVM_VERSION >= 190000 + *Context, std::move(FOut), InstPrinter, std::move(MCE), std::move(MAB) +#else + *Context, std::move(FOut), true, true, InstPrinter, std::move(MCE), + std::move(MAB), false +#endif + )); std::unique_ptr Printer( TM->getTarget().createAsmPrinter(*TM, std::move(S))); +#if JL_LLVM_VERSION >= 190000 + Printer->addDebugHandler( + std::make_unique(*Printer, debuginfo)); +#else Printer->addAsmPrinterHandler(AsmPrinter::HandlerInfo( std::unique_ptr(new LineNumberPrinterHandler(*Printer, debuginfo)), "emit", "Debug Info Emission", "Julia", "Julia::LineNumberPrinterHandler Markup")); +#endif if (!Printer) return jl_an_empty_string; PM.add(Printer.release()); diff --git a/src/features_x86.h b/src/features_x86.h index 08f979df546b7..2ecc8fee32a38 100644 --- a/src/features_x86.h +++ b/src/features_x86.h @@ -45,15 +45,15 @@ JL_FEATURE_DEF(avx512ifma, 32 * 2 + 21, 0) // JL_FEATURE_DEF(pcommit, 32 * 2 + 22, 0) // Deprecated JL_FEATURE_DEF(clflushopt, 32 * 2 + 23, 0) JL_FEATURE_DEF(clwb, 32 * 2 + 24, 0) -JL_FEATURE_DEF(avx512pf, 32 * 2 + 26, 0) -JL_FEATURE_DEF(avx512er, 32 * 2 + 27, 0) +// JL_FEATURE_DEF(avx512pf, 32 * 2 + 26, 0) // Deprecated in LLVM 19 +// JL_FEATURE_DEF(avx512er, 32 * 2 + 27, 0) // Deprecated in LLVM 19 JL_FEATURE_DEF(avx512cd, 32 * 2 + 28, 0) JL_FEATURE_DEF(sha, 32 * 2 + 29, 0) JL_FEATURE_DEF(avx512bw, 32 * 2 + 30, 0) JL_FEATURE_DEF(avx512vl, 32 * 2 + 31, 0) // EAX=7,ECX=0: ECX -JL_FEATURE_DEF(prefetchwt1, 32 * 3 + 0, 0) +// JL_FEATURE_DEF(prefetchwt1, 32 * 3 + 0, 0) // Deprecated in LLVM 19 JL_FEATURE_DEF(avx512vbmi, 32 * 3 + 1, 0) JL_FEATURE_DEF(pku, 32 * 3 + 4, 0) // ospke JL_FEATURE_DEF(waitpkg, 32 * 3 + 5, 0) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index d1757cadee05c..4ff7400df13dd 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1968,7 +1968,7 @@ void JuliaOJIT::enableJITDebuggingSupport() void JuliaOJIT::enableIntelJITEventListener() { #if JL_LLVM_VERSION >= 190000 - if (TT.isOSBinFormatELF()) { + if (TM->getTargetTriple().isOSBinFormatELF()) { orc::SymbolMap VTuneFunctions; auto RegisterImplAddr = addAbsoluteToMap(VTuneFunctions,llvm_orc_registerVTuneImpl); auto UnregisterImplAddr = addAbsoluteToMap(VTuneFunctions,llvm_orc_unregisterVTuneImpl); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index d544f182637b9..22ef973decfe9 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #if JL_LLVM_VERSION >= 170000 diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 07afa8c930deb..ed2a04e650f2a 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -178,9 +178,9 @@ static bool processLoop(Loop &L, OptimizationRemarkEmitter &ORE, ScalarEvolution if (S) { LLVM_DEBUG(dbgs() << "LSL: found " << S->getString() << "\n"); if (S->getString().starts_with("julia")) { - if (S->getString().equals("julia.simdloop")) + if (S->getString() == "julia.simdloop") simd = true; - if (S->getString().equals("julia.ivdep")) + if (S->getString() == "julia.ivdep") ivdep = true; continue; } diff --git a/src/llvmcalltest.cpp b/src/llvmcalltest.cpp index 93c442445d79a..2ab16f3ac6d67 100644 --- a/src/llvmcalltest.cpp +++ b/src/llvmcalltest.cpp @@ -6,6 +6,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" #include #include "julia.h" diff --git a/src/processor.cpp b/src/processor.cpp index 025043ac362d4..bc12f5b54be19 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -978,8 +978,12 @@ static std::string jl_get_cpu_name_llvm(void) static std::string jl_get_cpu_features_llvm(void) { +#if JL_LLVM_VERSION >= 190000 + auto HostFeatures = llvm::sys::getHostCPUFeatures(); +#else llvm::StringMap HostFeatures; llvm::sys::getHostCPUFeatures(HostFeatures); +#endif std::string attr; for (auto &ele: HostFeatures) { if (ele.getValue()) { diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index db954680289ea..f1dff063de1d9 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -144,8 +144,6 @@ static constexpr FeatureDep deps[] = { {avx512f, avx2}, {avx512dq, avx512f}, {avx512ifma, avx512f}, - {avx512pf, avx512f}, - {avx512er, avx512f}, {avx512cd, avx512f}, {avx512bw, avx512f}, {avx512bf16, avx512bw}, @@ -183,7 +181,7 @@ constexpr auto tremont = goldmont_plus | get_feature_masks(clwb, gfni); constexpr auto knl = get_feature_masks(sse3, ssse3, sse41, sse42, cx16, sahf, popcnt, aes, pclmul, avx, xsave, xsaveopt, rdrnd, f16c, fsgsbase, avx2, bmi, bmi2, fma, lzcnt, movbe, adx, rdseed, prfchw, - avx512f, avx512er, avx512cd, avx512pf, prefetchwt1); + avx512f, avx512cd); constexpr auto knm = knl | get_feature_masks(avx512vpopcntdq); constexpr auto yonah = get_feature_masks(sse3); constexpr auto prescott = yonah; @@ -584,7 +582,7 @@ template static inline void features_disable_avx512(T &features) { using namespace Feature; - unset_bits(features, avx512f, avx512dq, avx512ifma, avx512pf, avx512er, avx512cd, + unset_bits(features, avx512f, avx512dq, avx512ifma, avx512cd, avx512bw, avx512vl, avx512vbmi, avx512vpopcntdq, avx512vbmi2, avx512vnni, avx512bitalg, avx512vp2intersect, avx512bf16); } @@ -948,7 +946,6 @@ static void ensure_jit_target(bool imaging) Feature::vaes, Feature::vpclmulqdq, Feature::sse4a, Feature::avx512f, Feature::avx512dq, Feature::avx512ifma, - Feature::avx512pf, Feature::avx512er, Feature::avx512cd, Feature::avx512bw, Feature::avx512vl, Feature::avx512vbmi, Feature::avx512vpopcntdq, Feature::avxvnni, @@ -1142,7 +1139,6 @@ llvm::SmallVector jl_get_llvm_clone_targets(void) Feature::vaes, Feature::vpclmulqdq, Feature::sse4a, Feature::avx512f, Feature::avx512dq, Feature::avx512ifma, - Feature::avx512pf, Feature::avx512er, Feature::avx512cd, Feature::avx512bw, Feature::avx512vl, Feature::avx512vbmi, Feature::avx512vpopcntdq, Feature::avxvnni, diff --git a/test/llvmpasses/multiversioning-x86.ll b/test/llvmpasses/multiversioning-x86.ll index 1fd0ce2d5f40c..ff4a8abba5252 100644 --- a/test/llvmpasses/multiversioning-x86.ll +++ b/test/llvmpasses/multiversioning-x86.ll @@ -101,14 +101,14 @@ define noundef i32 @simd_test_call(<4 x i32> noundef %0) { ; CHECK: @simd_test_call{{.*}}#[[NOT_BORING_CLONE2:[0-9]+]] ; CHECK: %2 = call noundef i32 @simd_test.2(<4 x i32> noundef %0) -; CHECK-DAG: attributes #[[BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[NOT_BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[SIMD_BASE_RELOC]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[BORING_CLONE]] = { "julia.mv.clone"="1" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[NOT_BORING_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[NOT_BORING_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[SIMD_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } -; CHECK-DAG: attributes #[[SIMD_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_BASE_RELOC]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[BORING_CLONE]] = { "julia.mv.clone"="1" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } !llvm.module.flags = !{!0, !2} @@ -118,6 +118,6 @@ define noundef i32 @simd_test_call(<4 x i32> noundef %0) { !1 = !{!1} !2 = !{i32 1, !"julia.mv.specs", !3} !3 = !{!4, !5, !6} -!4 = !{!"x86-64", !"+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 0} -!5 = !{!"sandybridge", !"+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 2} -!6 = !{!"haswell", !"+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 1, i32 284} +!4 = !{!"x86-64", !"+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 0} +!5 = !{!"sandybridge", !"+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 2} +!6 = !{!"haswell", !"+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512cd,-sha,-avx512bw,-avx512vl,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 1, i32 284} From 952e952cd729d780a96a4a259cc8edaf7f060754 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 13 Oct 2024 23:27:00 -0400 Subject: [PATCH 382/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20fbaa2e337=20to=2027c1b1ee5=20(#56146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 | 1 + .../Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 | 1 + .../Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 | 1 - .../Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 create mode 100644 deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 diff --git a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 new file mode 100644 index 0000000000000..137460d1a05a1 --- /dev/null +++ b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 @@ -0,0 +1 @@ +74d656c054c1406a7e88910d673019f7 diff --git a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 new file mode 100644 index 0000000000000..0b8463176a867 --- /dev/null +++ b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 @@ -0,0 +1 @@ +a8e589ce68cc14883a7a21f68862695bfaa9ab38dfa0e704c32aaa801667708af0d851a41199ad09ae81a4c0b928befb680d639c1eca3377ce2db2dcc34b98e5 diff --git a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 deleted file mode 100644 index 762a180d93031..0000000000000 --- a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -4ea351427d5b43617abae557670c3313 diff --git a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 b/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 deleted file mode 100644 index eef70ab9b62d5..0000000000000 --- a/deps/checksums/Pkg-fbaa2e3370b4ab922919892640e5d1b0bcb14037.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9e91076974ab1dcb1c85e2c8acaf3404f4e82dcd2118d215d4a8413a1e00462ca47891bdae983441a8621015c082421de1f2e26b9b2ee18c1e3c13d58bd1d261 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index fc67189981d59..470acefbc6c83 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = fbaa2e3370b4ab922919892640e5d1b0bcb14037 +PKG_SHA1 = 27c1b1ee5cf15571eb5e54707e812d646ac1dde3 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 9e92a9d17415fabb765f5dfebb34f35fdccb9122 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Mon, 14 Oct 2024 16:59:12 +0200 Subject: [PATCH 383/548] HISTORY entry for deletion of `length(::Stateful)` (#55861) xref #47790 xref #51747 xref #54953 xref #55858 --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 6142747273864..aa7f9f0ccdad6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -119,6 +119,11 @@ New library features Standard library changes ------------------------ +* It's not possible to define `length` for stateful iterators in a generally consistent manner. The + potential for silently incorrect results for `Stateful` iterators is addressed by deleting the + `length(::Stateful)` method. The last type parameter of `Stateful` is gone, too. Issue: ([#47790]), + PR: ([#51747]). + #### StyledStrings * A new standard library for handling styling in a more comprehensive and structured way ([#49586]). @@ -225,6 +230,7 @@ Tooling Improvements [#46501]: https://github.com/JuliaLang/julia/issues/46501 [#47354]: https://github.com/JuliaLang/julia/issues/47354 [#47679]: https://github.com/JuliaLang/julia/issues/47679 +[#47790]: https://github.com/JuliaLang/julia/issues/47790 [#48273]: https://github.com/JuliaLang/julia/issues/48273 [#48625]: https://github.com/JuliaLang/julia/issues/48625 [#49546]: https://github.com/JuliaLang/julia/issues/49546 @@ -250,6 +256,7 @@ Tooling Improvements [#51616]: https://github.com/JuliaLang/julia/issues/51616 [#51647]: https://github.com/JuliaLang/julia/issues/51647 [#51704]: https://github.com/JuliaLang/julia/issues/51704 +[#51747]: https://github.com/JuliaLang/julia/issues/51747 [#51799]: https://github.com/JuliaLang/julia/issues/51799 [#51897]: https://github.com/JuliaLang/julia/issues/51897 [#51929]: https://github.com/JuliaLang/julia/issues/51929 From a19569d443f12b3a082258cad419094df2ae1612 Mon Sep 17 00:00:00 2001 From: Denis Barucic Date: Mon, 14 Oct 2024 20:56:32 +0200 Subject: [PATCH 384/548] ntuple: ensure eltype is always `Int` (#55901) Fixes #55790 --- base/ntuple.jl | 6 ++++-- test/tuple.jl | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/base/ntuple.jl b/base/ntuple.jl index f81d2686b9764..185c42601280f 100644 --- a/base/ntuple.jl +++ b/base/ntuple.jl @@ -14,7 +14,7 @@ julia> ntuple(i -> 2*i, 4) (2, 4, 6, 8) ``` """ -@inline function ntuple(f::F, n::Integer) where F +@inline function ntuple(f::F, n::Int) where F # marked inline since this benefits from constant propagation of `n` t = n == 0 ? () : n == 1 ? (f(1),) : @@ -30,8 +30,10 @@ julia> ntuple(i -> 2*i, 4) _ntuple(f, n) return t end +ntuple(f::F, n::Integer) where F = ntuple(f, convert(Int, n)::Int) -function _ntuple(f::F, n) where F +# `n` should always be an Int (#55790) +function _ntuple(f::F, n::Int) where F @noinline (n >= 0) || throw(ArgumentError(LazyString("tuple length should be ≥ 0, got ", n))) ([f(i) for i = 1:n]...,) diff --git a/test/tuple.jl b/test/tuple.jl index 355ad965f9584..13af5ac992434 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -534,6 +534,11 @@ end end @test Base.infer_return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} + + # issue #55790 + for n in 1:32 + @test typeof(ntuple(identity, UInt64(n))) == NTuple{n, Int} + end end struct A_15703{N} From 159adbf6e930ba7ffe58adcc441e7828f4cca429 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 14 Oct 2024 18:17:43 -0300 Subject: [PATCH 385/548] Improve remarks of the alloc opt pass slightly. (#55995) The Value printer LLVM uses just prints the kind of instruction so it just shows call. --------- Co-authored-by: Oscar Smith --- src/llvm-alloc-helpers.cpp | 28 +++++++++++++++++++----- src/llvm-alloc-opt.cpp | 45 ++++++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 75030f8565221..59fce1235e14e 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -134,12 +134,16 @@ JL_USED_FUNC void AllocUseInfo::dump(llvm::raw_ostream &OS) OS << " zeroed"; OS << '\n'; OS << "Uses: " << uses.size() << '\n'; - for (auto inst: uses) + for (auto inst: uses) { inst->print(OS); + OS << '\n'; + } if (!preserves.empty()) { OS << "Preserves: " << preserves.size() << '\n'; - for (auto inst: preserves) + for (auto inst: preserves) { inst->print(OS); + OS << '\n'; + } } OS << "MemOps: " << memops.size() << '\n'; for (auto &field: memops) { @@ -268,9 +272,12 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r } LLVM_DEBUG(dbgs() << "Unknown call, marking escape\n"); REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + inst->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "UnknownCall", inst) - << "Unknown call, marking escape (" << ore::NV("Call", inst) << ")"; + << "Unknown call, marking escape (" << ore::NV("Call", StringRef(str)) << ")"; }); required.use_info.escaped = true; return false; @@ -284,9 +291,12 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r if (use->getOperandNo() != StoreInst::getPointerOperandIndex()) { LLVM_DEBUG(dbgs() << "Object address is stored somewhere, marking escape\n"); REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + inst->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "StoreObjAddr", inst) - << "Object address is stored somewhere, marking escape (" << ore::NV("Store", inst) << ")"; + << "Object address is stored somewhere, marking escape (" << ore::NV("Store", StringRef(str)) << ")"; }); required.use_info.escaped = true; return false; @@ -310,9 +320,12 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r if (use->getOperandNo() != isa(inst) ? AtomicCmpXchgInst::getPointerOperandIndex() : AtomicRMWInst::getPointerOperandIndex()) { LLVM_DEBUG(dbgs() << "Object address is cmpxchg/rmw-ed somewhere, marking escape\n"); REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + inst->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "StoreObjAddr", inst) - << "Object address is cmpxchg/rmw-ed somewhere, marking escape (" << ore::NV("Store", inst) << ")"; + << "Object address is cmpxchg/rmw-ed somewhere, marking escape (" << ore::NV("Store", StringRef(str)) << ")"; }); required.use_info.escaped = true; return false; @@ -363,9 +376,12 @@ void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs r } LLVM_DEBUG(dbgs() << "Unknown instruction, marking escape\n"); REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + inst->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "UnknownInst", inst) - << "Unknown instruction, marking escape (" << ore::NV("Inst", inst) << ")"; + << "Unknown instruction, marking escape (" << ore::NV("Inst", StringRef(str)) << ")"; }); required.use_info.escaped = true; return false; diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 188955fd50972..0ec88c9d56356 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -224,8 +224,11 @@ void Optimizer::optimizeAll() checkInst(orig); if (use_info.escaped) { REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) - << "GC allocation escaped " << ore::NV("GC Allocation", orig); + << "GC allocation escaped " << ore::NV("GC Allocation", StringRef(str)); }); if (use_info.hastypeof) optimizeTag(orig); @@ -233,8 +236,11 @@ void Optimizer::optimizeAll() } if (use_info.haserror || use_info.returned) { REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) - << "GC allocation has error or was returned " << ore::NV("GC Allocation", orig); + << "GC allocation has error or was returned " << ore::NV("GC Allocation", StringRef(str)); }); if (use_info.hastypeof) optimizeTag(orig); @@ -243,8 +249,11 @@ void Optimizer::optimizeAll() if (!use_info.addrescaped && !use_info.hasload && (!use_info.haspreserve || !use_info.refstore)) { REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemark(DEBUG_TYPE, "Dead Allocation", orig) - << "GC allocation removed " << ore::NV("GC Allocation", orig); + << "GC allocation removed " << ore::NV("GC Allocation", StringRef(str)); }); // No one took the address, no one reads anything and there's no meaningful // preserve of fields (either no preserve/ccall or no object reference fields) @@ -270,8 +279,11 @@ void Optimizer::optimizeAll() } if (has_refaggr) { REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) - << "GC allocation has unusual object reference, unable to move to stack " << ore::NV("GC Allocation", orig); + << "GC allocation has unusual object reference, unable to move to stack " << ore::NV("GC Allocation", StringRef(str)); }); if (use_info.hastypeof) optimizeTag(orig); @@ -279,8 +291,11 @@ void Optimizer::optimizeAll() } if (!use_info.hasunknownmem && !use_info.addrescaped) { REMARK([&](){ + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemark(DEBUG_TYPE, "Stack Split Allocation", orig) - << "GC allocation split on stack " << ore::NV("GC Allocation", orig); + << "GC allocation split on stack " << ore::NV("GC Allocation", StringRef(str)); }); // No one actually care about the memory layout of this object, split it. splitOnStack(orig); @@ -292,16 +307,22 @@ void Optimizer::optimizeAll() // This later causes the GC rooting pass, to miss-characterize the float as a pointer to a GC value if (has_unboxed && has_ref) { REMARK([&]() { + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) - << "GC allocation could not be split since it contains both boxed and unboxed values, unable to move to stack " << ore::NV("GC Allocation", orig); + << "GC allocation could not be split since it contains both boxed and unboxed values, unable to move to stack " << ore::NV("GC Allocation", StringRef(str)); }); if (use_info.hastypeof) optimizeTag(orig); continue; } REMARK([&](){ + std::string str; + llvm::raw_string_ostream rso(str); + orig->print(rso); return OptimizationRemark(DEBUG_TYPE, "Stack Move Allocation", orig) - << "GC allocation moved to stack " << ore::NV("GC Allocation", orig); + << "GC allocation moved to stack " << ore::NV("GC Allocation", StringRef(str)); }); // The object has no fields with mix reference access moveToStack(orig, sz, has_ref, use_info.allockind); @@ -380,7 +401,10 @@ void Optimizer::checkInst(CallInst *I) std::string suse_info; llvm::raw_string_ostream osuse_info(suse_info); use_info.dump(osuse_info); - return OptimizationRemarkAnalysis(DEBUG_TYPE, "EscapeAnalysis", I) << "escape analysis for " << ore::NV("GC Allocation", I) << "\n" << ore::NV("UseInfo", osuse_info.str()); + std::string str; + llvm::raw_string_ostream rso(str); + I->print(rso); + return OptimizationRemarkAnalysis(DEBUG_TYPE, "EscapeAnalysis", I) << "escape analysis for " << ore::NV("GC Allocation", StringRef(str)) << "\n" << ore::NV("UseInfo", osuse_info.str()); }); } @@ -905,8 +929,11 @@ void Optimizer::optimizeTag(CallInst *orig_inst) if (pass.typeof_func == callee) { ++RemovedTypeofs; REMARK([&](){ + std::string str; + llvm::raw_string_ostream rso(str); + orig_inst->print(rso); return OptimizationRemark(DEBUG_TYPE, "typeof", call) - << "removed typeof call for GC allocation " << ore::NV("Alloc", orig_inst); + << "removed typeof call for GC allocation " << ore::NV("Alloc", StringRef(str)); }); call->replaceAllUsesWith(tag); // Push to the removed instructions to trigger `finalize` to From 8c2bcf67e03f13771841605f5289dc56eb46932e Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Tue, 15 Oct 2024 01:43:30 +0200 Subject: [PATCH 386/548] Implement Base.fd() for TCPSocket, UDPSocket, and TCPServer (#53721) This is quite handy if you want to pass off the file descriptor to a C library. I also added a warning to the `fd()` docstring to warn folks about duplicating the file descriptor first. --- base/iostream.jl | 19 ++++++++++++++++--- base/libc.jl | 7 +++++++ base/public.jl | 1 + doc/src/base/libc.md | 1 + stdlib/Sockets/src/Sockets.jl | 6 ++++++ stdlib/Sockets/test/runtests.jl | 25 +++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/base/iostream.jl b/base/iostream.jl index 74908344e078e..d91330960d59a 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -47,16 +47,29 @@ macro _lock_ios(s, expr) end """ - fd(stream) -> RawFD + fd(x) -> RawFD -Return the file descriptor backing the stream or file. Note that this function only applies -to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. +Return the file descriptor backing the stream, file, or socket. `RawFD` objects can be passed directly to other languages via the `ccall` interface. !!! compat "Julia 1.12" Prior to 1.12, this function returned an `Int` instead of a `RawFD`. You may use `RawFD(fd(x))` to produce a `RawFD` in all Julia versions. + +!!! compat "Julia 1.12" + Getting the file descriptor of sockets are supported as of Julia 1.12. + +!!! warning + Duplicate the returned file descriptor with [`Libc.dup()`](@ref) before + passing it to another system that will take ownership of it (e.g. a C + library). Otherwise both the Julia object `x` and the other system may try + to close the file descriptor, which will cause errors. + +!!! warning + The file descriptors for sockets are asynchronous (i.e. `O_NONBLOCK` on + POSIX and `OVERLAPPED` on Windows), they may behave differently than regular + file descriptors. """ fd(s::IOStream) = RawFD(ccall(:jl_ios_fd, Clong, (Ptr{Cvoid},), s.ios)) diff --git a/base/libc.jl b/base/libc.jl index 21f9554f7e6db..7364f6e6677fe 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -36,6 +36,13 @@ RawFD(fd::Integer) = bitcast(RawFD, Cint(fd)) RawFD(fd::RawFD) = fd Base.cconvert(::Type{Cint}, fd::RawFD) = bitcast(Cint, fd) +""" + dup(src::RawFD[, target::RawFD])::RawFD + +Duplicate the file descriptor `src` so that the duplicate refers to the same OS +resource (e.g. a file or socket). A `target` file descriptor may be optionally +be passed to use for the new duplicate. +""" dup(x::RawFD) = ccall((@static Sys.iswindows() ? :_dup : :dup), RawFD, (RawFD,), x) dup(src::RawFD, target::RawFD) = systemerror("dup", -1 == ccall((@static Sys.iswindows() ? :_dup2 : :dup2), Int32, diff --git a/base/public.jl b/base/public.jl index 2e8e777d2f91d..1a23550485d84 100644 --- a/base/public.jl +++ b/base/public.jl @@ -102,6 +102,7 @@ public # functions reseteof, link_pipe!, + dup, # filesystem operations rename, diff --git a/doc/src/base/libc.md b/doc/src/base/libc.md index c0448b04d9db7..b598baaa16bab 100644 --- a/doc/src/base/libc.md +++ b/doc/src/base/libc.md @@ -18,6 +18,7 @@ Base.Libc.strftime Base.Libc.strptime Base.Libc.TmStruct Base.Libc.FILE +Base.Libc.dup Base.Libc.flush_cstdio Base.Libc.systemsleep Base.Libc.mkfifo diff --git a/stdlib/Sockets/src/Sockets.jl b/stdlib/Sockets/src/Sockets.jl index 3c30b214305fb..f9e0f2f88dd78 100644 --- a/stdlib/Sockets/src/Sockets.jl +++ b/stdlib/Sockets/src/Sockets.jl @@ -107,6 +107,8 @@ if OS_HANDLE != RawFD TCPSocket(fd::RawFD) = TCPSocket(Libc._get_osfhandle(fd)) end +Base.fd(sock::TCPSocket) = Base._fd(sock) + mutable struct TCPServer <: LibuvServer handle::Ptr{Cvoid} @@ -139,6 +141,8 @@ function TCPServer(; delay=true) return tcp end +Base.fd(server::TCPServer) = Base._fd(server) + """ accept(server[, client]) @@ -199,6 +203,8 @@ end show(io::IO, stream::UDPSocket) = print(io, typeof(stream), "(", uv_status_string(stream), ")") +Base.fd(sock::UDPSocket) = Base._fd(sock) + function _uv_hook_close(sock::UDPSocket) lock(sock.cond) try diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 778d9f7415bcc..669237acccb0a 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -605,6 +605,31 @@ end end end +@testset "fd() methods" begin + function valid_fd(x) + if Sys.iswindows() + return x isa Base.OS_HANDLE + elseif !Sys.iswindows() + value = Base.cconvert(Cint, x) + + # 2048 is a bit arbitrary, it depends on the process not having too many + # file descriptors open. But select() has a limit of 1024 and people + # don't seem to hit it too often so let's hope twice that is safe. + return value > 0 && value < 2048 + end + end + + sock = TCPSocket(; delay=false) + @test valid_fd(fd(sock)) + + sock = UDPSocket() + bind(sock, Sockets.localhost, 0) + @test valid_fd(fd(sock)) + + server = listen(Sockets.localhost, 0) + @test valid_fd(fd(server)) +end + @testset "TCPServer constructor" begin s = Sockets.TCPServer(; delay=false) if ccall(:jl_has_so_reuseport, Int32, ()) == 1 From b86e647159a4d9f1285e4f8c70a18e1b2bf2aa7d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 15 Oct 2024 02:02:11 +0200 Subject: [PATCH 387/548] Fix `JULIA_CPU_TARGET` being propagated to workers precompiling stdlib pkgimages (#54093) Apparently (thanks ChatGPT) each line in a makefile is executed in a separate shell so adding an `export` line on one line does not propagate to the next line. --- pkgimage.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgimage.mk b/pkgimage.mk index 740b9760cab48..0bc035ee03b08 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -25,8 +25,7 @@ print-depot-path: @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') $(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled - export JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" - @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') + @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') touch $@ $(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT) From f42066a9720f42fc330373d384fe9bc22aabf107 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 15 Oct 2024 06:27:31 +0530 Subject: [PATCH 388/548] Merge tr methods for triangular matrices (#56154) Since the methods do identical things, we don't need multiple of these. --- stdlib/LinearAlgebra/src/triangular.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index ee63865b65d6e..3b949fa54b287 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -523,10 +523,8 @@ for TM in (:LowerTriangular, :UpperTriangular) @eval -(A::$TM{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast(-, A) end -tr(A::LowerTriangular) = tr(A.data) -tr(A::UnitLowerTriangular) = size(A, 1) * oneunit(eltype(A)) -tr(A::UpperTriangular) = tr(A.data) -tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A)) +tr(A::UpperOrLowerTriangular) = tr(A.data) +tr(A::Union{UnitLowerTriangular, UnitUpperTriangular}) = size(A, 1) * oneunit(eltype(A)) for T in (:UpperOrUnitUpperTriangular, :LowerOrUnitLowerTriangular) @eval @propagate_inbounds function copyto!(dest::$T, U::$T) From b69c8682c8d2deb5716703f925066f86ed7339f2 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 15 Oct 2024 06:28:38 +0530 Subject: [PATCH 389/548] Reduce duplication in triangular indexing methods (#56152) This uses an orthogonal design to reduce code duplication in the indexing methods for triangular matrices. --- stdlib/LinearAlgebra/src/triangular.jl | 60 +++++++++++--------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 3b949fa54b287..71660bc5ca28c 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -208,45 +208,33 @@ function full!(A::UnitUpperTriangular) B end -Base.isassigned(A::UnitLowerTriangular, i::Int, j::Int) = - i > j ? isassigned(A.data, i, j) : true -Base.isassigned(A::LowerTriangular, i::Int, j::Int) = - i >= j ? isassigned(A.data, i, j) : true -Base.isassigned(A::UnitUpperTriangular, i::Int, j::Int) = - i < j ? isassigned(A.data, i, j) : true -Base.isassigned(A::UpperTriangular, i::Int, j::Int) = - i <= j ? isassigned(A.data, i, j) : true - -Base.isstored(A::UnitLowerTriangular, i::Int, j::Int) = - i > j ? Base.isstored(A.data, i, j) : false -Base.isstored(A::LowerTriangular, i::Int, j::Int) = - i >= j ? Base.isstored(A.data, i, j) : false -Base.isstored(A::UnitUpperTriangular, i::Int, j::Int) = - i < j ? Base.isstored(A.data, i, j) : false -Base.isstored(A::UpperTriangular, i::Int, j::Int) = - i <= j ? Base.isstored(A.data, i, j) : false - -@propagate_inbounds getindex(A::UnitLowerTriangular{T}, i::Int, j::Int) where {T} = - i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) -@propagate_inbounds getindex(A::LowerTriangular, i::Int, j::Int) = - i >= j ? A.data[i,j] : _zero(A.data,j,i) -@propagate_inbounds getindex(A::UnitUpperTriangular{T}, i::Int, j::Int) where {T} = - i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) -@propagate_inbounds getindex(A::UpperTriangular, i::Int, j::Int) = - i <= j ? A.data[i,j] : _zero(A.data,j,i) +_shouldforwardindex(U::UpperTriangular, row::Integer, col::Integer) = row <= col +_shouldforwardindex(U::LowerTriangular, row::Integer, col::Integer) = row >= col +_shouldforwardindex(U::UnitUpperTriangular, row::Integer, col::Integer) = row < col +_shouldforwardindex(U::UnitLowerTriangular, row::Integer, col::Integer) = row > col + +Base.isassigned(A::UpperOrLowerTriangular, i::Int, j::Int) = + _shouldforwardindex(A, i, j) ? isassigned(A.data, i, j) : true + +Base.isstored(A::UpperOrLowerTriangular, i::Int, j::Int) = + _shouldforwardindex(A, i, j) ? Base.isstored(A.data, i, j) : false + +@propagate_inbounds getindex(A::Union{UnitLowerTriangular{T}, UnitUpperTriangular{T}}, i::Int, j::Int) where {T} = + _shouldforwardindex(A, i, j) ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) +@propagate_inbounds getindex(A::Union{LowerTriangular, UpperTriangular}, i::Int, j::Int) = + _shouldforwardindex(A, i, j) ? A.data[i,j] : _zero(A.data,j,i) + +_shouldforwardindex(U::UpperTriangular, b::BandIndex) = b.band >= 0 +_shouldforwardindex(U::LowerTriangular, b::BandIndex) = b.band <= 0 +_shouldforwardindex(U::UnitUpperTriangular, b::BandIndex) = b.band > 0 +_shouldforwardindex(U::UnitLowerTriangular, b::BandIndex) = b.band < 0 # these specialized getindex methods enable constant-propagation of the band -Base.@constprop :aggressive @propagate_inbounds function getindex(A::UnitLowerTriangular{T}, b::BandIndex) where {T} - b.band < 0 ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) -end -Base.@constprop :aggressive @propagate_inbounds function getindex(A::LowerTriangular, b::BandIndex) - b.band <= 0 ? A.data[b] : _zero(A.data, b) -end -Base.@constprop :aggressive @propagate_inbounds function getindex(A::UnitUpperTriangular{T}, b::BandIndex) where {T} - b.band > 0 ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) +Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{UnitLowerTriangular{T}, UnitUpperTriangular{T}}, b::BandIndex) where {T} + _shouldforwardindex(A, b) ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) end -Base.@constprop :aggressive @propagate_inbounds function getindex(A::UpperTriangular, b::BandIndex) - b.band >= 0 ? A.data[b] : _zero(A.data, b) +Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{LowerTriangular, UpperTriangular}, b::BandIndex) + _shouldforwardindex(A, b) ? A.data[b] : _zero(A.data, b) end _zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" From 3b3a70fe206783293d87de9320fbf1e369db4c80 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 14 Oct 2024 23:48:24 -0400 Subject: [PATCH 390/548] update LLVM docs (#56162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dump with raw=true so you don't get random erorrs, and show how to run single modules. --------- Co-authored-by: Valentin Churavy Co-authored-by: Mosè Giordano <765740+giordano@users.noreply.github.com> Co-authored-by: Jameson Nash --- doc/src/devdocs/llvm.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index fdecb0472388a..c05a4f9dc4e7f 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -142,9 +142,9 @@ cc -shared -o sys.so sys.o ``` To generate a system image with the new pass manager, one could do: ``` -opt -load-pass-plugin=libjulia-codegen.so --passes='julia' -o opt.bc unopt.bc -llc -o sys.o opt.bc -cc -shared -o sys.so sys.o +./usr/tools/opt -load-pass-plugin=libjulia-codegen.so --passes='julia' -o opt.bc unopt.bc +./usr/tools/llc -o sys.o opt.bc +./usr/tools/cc -shared -o sys.so sys.o ``` This system image can then be loaded by `julia` as usual. @@ -154,11 +154,15 @@ using: fun, T = +, Tuple{Int,Int} # Substitute your function of interest here optimize = false open("plus.ll", "w") do file - println(file, InteractiveUtils._dump_function(fun, T, false, false, false, true, :att, optimize, :default, false)) + code_llvm(file, fun, T; raw=true, dump_module=true, optimize) end ``` These files can be processed the same way as the unoptimized sysimg IR shown -above. +above, or if you want to see the LLVM IR yourself and get extra verification run, you can use +``` +./usr/tools/opt -load-pass-plugin=libjulia-codegen.so --passes='julia' -S -verify-each plus.ll +``` +(note on MacOS this would be `libjulia-codegen.dylib` and on Windows `libjulia-codegen.dll`) ## Running the LLVM test suite From d749f0eb444dfead2016d6ab0fb32e4ae1846792 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 15 Oct 2024 09:24:07 +0530 Subject: [PATCH 391/548] Fix zero elements for block-matrix kron involving Diagonal (#55941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, it's assumed that the zero element is identical for the matrix, but this is not necessary if the elements are matrices themselves and have different sizes. This PR ensures that `kron` for a `Diagonal` has the correct zero elements. Current: ```julia julia> D = Diagonal(1:2) 2×2 Diagonal{Int64, UnitRange{Int64}}: 1 ⋅ ⋅ 2 julia> B = reshape([ones(2,2), ones(3,2), ones(2,3), ones(3,3)], 2, 2); julia> size.(kron(D, B)) 4×4 Matrix{Tuple{Int64, Int64}}: (2, 2) (2, 3) (2, 2) (2, 2) (3, 2) (3, 3) (2, 2) (2, 2) (2, 2) (2, 2) (2, 2) (2, 3) (2, 2) (2, 2) (3, 2) (3, 3) ``` This PR ```julia julia> size.(kron(D, B)) 4×4 Matrix{Tuple{Int64, Int64}}: (2, 2) (2, 3) (2, 2) (2, 3) (3, 2) (3, 3) (3, 2) (3, 3) (2, 2) (2, 3) (2, 2) (2, 3) (3, 2) (3, 3) (3, 2) (3, 3) ``` Note the differences e.g. in the `CartesianIndex(4,1)`, `CartesianIndex(3,2)` and `CartesianIndex(3,3)` elements. --- stdlib/LinearAlgebra/src/diagonal.jl | 70 ++++++++++++++++++++++++--- stdlib/LinearAlgebra/test/diagonal.jl | 10 ++++ 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index aabfb3e8ba114..17ff232f5b262 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -686,16 +686,33 @@ for Tri in (:UpperTriangular, :LowerTriangular) end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) - valA = A.diag; nA = length(valA) - valB = B.diag; nB = length(valB) + valA = A.diag; mA, nA = size(A) + valB = B.diag; mB, nB = size(B) nC = checksquare(C) @boundscheck nC == nA*nB || throw(DimensionMismatch(lazy"expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)")) - isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) + zerofilled = false + if !(isempty(A) || isempty(B)) + z = A[1,1] * B[1,1] + if haszero(typeof(z)) + # in this case, the zero is unique + fill!(C, zero(z)) + zerofilled = true + end + end @inbounds for i = 1:nA, j = 1:nB idx = (i-1)*nB+j C[idx, idx] = valA[i] * valB[j] end + if !zerofilled + for j in 1:nA, i in 1:mA + Δrow, Δcol = (i-1)*mB, (j-1)*nB + for k in 1:nB, l in 1:mB + i == j && k == l && continue + C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] + end + end + end return C end @@ -722,7 +739,15 @@ end (mC, nC) = size(C) @boundscheck (mC, nC) == (mA * mB, nA * nB) || throw(DimensionMismatch(lazy"expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) - isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) + zerofilled = false + if !(isempty(A) || isempty(B)) + z = A[1,1] * B[1,1] + if haszero(typeof(z)) + # in this case, the zero is unique + fill!(C, zero(z)) + zerofilled = true + end + end m = 1 @inbounds for j = 1:nA A_jj = A[j,j] @@ -733,6 +758,18 @@ end end m += (nA - 1) * mB end + if !zerofilled + # populate the zero elements + for i in 1:mA + i == j && continue + A_ij = A[i, j] + Δrow, Δcol = (i-1)*mB, (j-1)*nB + for k in 1:nB, l in 1:nA + B_lk = B[l, k] + C[Δrow + l, Δcol + k] = A_ij * B_lk + end + end + end m += mB end return C @@ -745,17 +782,36 @@ end (mC, nC) = size(C) @boundscheck (mC, nC) == (mA * mB, nA * nB) || throw(DimensionMismatch(lazy"expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) - isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) + zerofilled = false + if !(isempty(A) || isempty(B)) + z = A[1,1] * B[1,1] + if haszero(typeof(z)) + # in this case, the zero is unique + fill!(C, zero(z)) + zerofilled = true + end + end m = 1 @inbounds for j = 1:nA for l = 1:mB Bll = B[l,l] - for k = 1:mA - C[m] = A[k,j] * Bll + for i = 1:mA + C[m] = A[i,j] * Bll m += nB end m += 1 end + if !zerofilled + for i in 1:mA + A_ij = A[i, j] + Δrow, Δcol = (i-1)*mB, (j-1)*nB + for k in 1:nB, l in 1:mB + l == k && continue + B_lk = B[l, k] + C[Δrow + l, Δcol + k] = A_ij * B_lk + end + end + end m -= nB end return C diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index f7a9ccb705de9..8b56ee15e56e3 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1391,4 +1391,14 @@ end @test checkbounds(Bool, D, diagind(D, IndexCartesian())) end +@testset "zeros in kron with block matrices" begin + D = Diagonal(1:2) + B = reshape([ones(2,2), ones(3,2), ones(2,3), ones(3,3)], 2, 2) + @test kron(D, B) == kron(Array(D), B) + @test kron(B, D) == kron(B, Array(D)) + D2 = Diagonal([ones(2,2), ones(3,3)]) + @test kron(D, D2) == kron(D, Array{eltype(D2)}(D2)) + @test kron(D2, D) == kron(Array{eltype(D2)}(D2), D) +end + end # module TestDiagonal From 0af99e641a4329b57e48a314e2cedb592e02cd3b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 15 Oct 2024 09:25:56 +0530 Subject: [PATCH 392/548] Call `MulAddMul` instead of multiplication in _generic_matmatmul! (#56089) Fix https://github.com/JuliaLang/julia/issues/56085 by calling a newly created `MulAddMul` object that only wraps the `alpha` (with `beta` set to `false`). This avoids the explicit multiplication if `alpha` is known to be `isone`. --- stdlib/LinearAlgebra/src/matmul.jl | 6 ++++-- stdlib/LinearAlgebra/test/matmul.jl | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index b70f7d47b28dd..02ecd74152531 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -919,7 +919,7 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) @noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, - _add::MulAddMul) where {T,S,R} + _add::MulAddMul{ais1}) where {T,S,R,ais1} AxM = axes(A, 1) AxK = axes(A, 2) # we use two `axes` calls in case of `AbstractVector` BxK = axes(B, 1) @@ -935,11 +935,13 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A if BxN != CxN throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) end + _rmul_alpha = MulAddMul{ais1,true,typeof(_add.alpha),Bool}(_add.alpha,false) if isbitstype(R) && sizeof(R) ≤ 16 && !(A isa Adjoint || A isa Transpose) _rmul_or_fill!(C, _add.beta) (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C @inbounds for n in BxN, k in BxK - Balpha = B[k,n]*_add.alpha + # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) + Balpha = _rmul_alpha(B[k,n]) @simd for m in AxM C[m,n] = muladd(A[m,k], Balpha, C[m,n]) end diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 4c79451ebfc8b..0d1e2776d2bb3 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1130,4 +1130,22 @@ end @test a * transpose(B) ≈ A * transpose(B) end +@testset "issue #56085" begin + struct Thing + data::Float64 + end + + Base.zero(::Type{Thing}) = Thing(0.) + Base.zero(::Thing) = Thing(0.) + Base.one(::Type{Thing}) = Thing(1.) + Base.one(::Thing) = Thing(1.) + Base.:+(t::Thing...) = +(getfield.(t, :data)...) + Base.:*(t::Thing...) = *(getfield.(t, :data)...) + + M = Float64[1 2; 3 4] + A = Thing.(M) + + @test A * A ≈ M * M +end + end # module TestMatmul From fe82988327183f90ddccb3f7e270a88c9e9c2021 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:27:10 +0900 Subject: [PATCH 393/548] improve `allunique`'s type stability (#56161) Caught by https://github.com/aviatesk/JET.jl/issues/667. --- base/set.jl | 4 ++-- test/sets.jl | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/base/set.jl b/base/set.jl index 2f96cef626b6f..d1f9458039cd4 100644 --- a/base/set.jl +++ b/base/set.jl @@ -549,8 +549,8 @@ function allunique(A::StridedArray) if length(A) < 32 _indexed_allunique(A) elseif OrderStyle(eltype(A)) === Ordered() - a1, rest1 = Iterators.peel(A) - a2, rest = Iterators.peel(rest1) + a1, rest1 = Iterators.peel(A)::Tuple{Any,Any} + a2, rest = Iterators.peel(rest1)::Tuple{Any,Any} if !isequal(a1, a2) compare = isless(a1, a2) ? isless : (a,b) -> isless(b,a) for a in rest diff --git a/test/sets.jl b/test/sets.jl index 4ab360c9fedd4..b78d2f15dd989 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -644,6 +644,7 @@ end @test !allunique((NaN, NaN)) # Known length 1, need not evaluate: @test allunique(error(x) for x in [1]) + # @test_opt allunique(Int[]) end @testset "allunique(f, xs)" begin From 9223088faefd9680d8217b44d0bf82e478a311c1 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:07:19 -0400 Subject: [PATCH 394/548] Add invalidation barriers for `displaysize` and `implicit_typeinfo` (#56159) These are invalidated by our own stdlibs (Dates and REPL) unfortunately so we need to put this barrier in. This fix is _very_ un-satisfying, because it doesn't do anything to solve this problem for downstream libraries that use e.g. `displaysize`. To fix that, I think we need a way to make sure callers get these invalidation barriers by default... --- base/arrayshow.jl | 16 +++++++++++----- base/logging/ConsoleLogger.jl | 2 +- base/precompilation.jl | 4 ++-- base/show.jl | 4 ++-- base/stream.jl | 7 +++++++ 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index 164a9257d8412..3bc69e563a967 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -545,6 +545,12 @@ typeinfo_eltype(typeinfo::Type{<:AbstractArray{T}}) where {T} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractDict{K,V}}) where {K,V} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractSet{T}}) where {T} = eltype(typeinfo) +# This is a fancy way to make de-specialize a call to `typeinfo_implicit(T)` +# which is unfortunately invalidated by Dates +# (https://github.com/JuliaLang/julia/issues/56080) +# +# This makes the call less efficient, but avoids being invalidated by Dates. +_typeinfo_implicit(@nospecialize(T)) = Base.invoke_in_world(Base.tls_world_age(), typeinfo_implicit, T)::Bool # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) @@ -553,9 +559,9 @@ function typeinfo_implicit(@nospecialize(T)) return true end return isconcretetype(T) && - ((T <: Array && typeinfo_implicit(eltype(T))) || - ((T <: Tuple || T <: NamedTuple || T <: Pair) && all(typeinfo_implicit, fieldtypes(T))) || - (T <: AbstractDict && typeinfo_implicit(keytype(T)) && typeinfo_implicit(valtype(T)))) + ((T <: Array && _typeinfo_implicit(eltype(T))) || + ((T <: Tuple || T <: NamedTuple || T <: Pair) && all(_typeinfo_implicit, fieldtypes(T))) || + (T <: AbstractDict && _typeinfo_implicit(keytype(T)) && _typeinfo_implicit(valtype(T)))) end # X not constrained, can be any iterable (cf. show_vector) @@ -573,7 +579,7 @@ function typeinfo_prefix(io::IO, X) if X isa AbstractDict if eltype_X == eltype_ctx sprint(show_type_name, typeof(X).name; context=io), false - elseif !isempty(X) && typeinfo_implicit(keytype(X)) && typeinfo_implicit(valtype(X)) + elseif !isempty(X) && _typeinfo_implicit(keytype(X)) && _typeinfo_implicit(valtype(X)) sprint(show_type_name, typeof(X).name; context=io), true else sprint(print, typeof(X); context=io), false @@ -582,7 +588,7 @@ function typeinfo_prefix(io::IO, X) # Types hard-coded here are those which are created by default for a given syntax if eltype_X == eltype_ctx "", false - elseif !isempty(X) && typeinfo_implicit(eltype_X) + elseif !isempty(X) && _typeinfo_implicit(eltype_X) "", true elseif print_without_params(eltype_X) sprint(show_type_name, unwrap_unionall(eltype_X).name; context=io), false # Print "Array" rather than "Array{T,N}" diff --git a/base/logging/ConsoleLogger.jl b/base/logging/ConsoleLogger.jl index c4596dd86c3f5..818b2272b773c 100644 --- a/base/logging/ConsoleLogger.jl +++ b/base/logging/ConsoleLogger.jl @@ -130,7 +130,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module if !(isopen(stream)::Bool) stream = stderr end - dsize = displaysize(stream)::Tuple{Int,Int} + dsize = Base.displaysize_(stream)::Tuple{Int,Int} nkwargs = length(kwargs)::Int if nkwargs > hasmaxlog valbuf = IOBuffer() diff --git a/base/precompilation.jl b/base/precompilation.jl index 7a821222c52d1..4b7da84a17d55 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -672,7 +672,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; n_print_rows = 0 while !printloop_should_exit lock(print_lock) do - term_size = Base.displaysize(io)::Tuple{Int,Int} + term_size = Base.displaysize_(io) num_deps_show = max(term_size[1] - 3, 2) # show at least 2 deps pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) @@ -687,7 +687,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; bar.max = n_total - n_already_precomp # when sizing to the terminal width subtract a little to give some tolerance to resizing the # window between print cycles - termwidth = displaysize(io)[2] - 4 + termwidth = Base.displaysize_(io)[2] - 4 if !final_loop str = sprint(io -> show_progress(io, bar; termwidth, carriagereturn=false); context=io) print(iostr, Base._truncate_at_width_or_chars(true, str, termwidth), "\n") diff --git a/base/show.jl b/base/show.jl index 66560265e3b42..a147c2037d70e 100644 --- a/base/show.jl +++ b/base/show.jl @@ -427,7 +427,7 @@ get(io::IO, key, default) = default keys(io::IOContext) = keys(io.dict) keys(io::IO) = keys(ImmutableDict{Symbol,Any}()) -displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : Base.displaysize_(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) @@ -2622,7 +2622,7 @@ end function type_limited_string_from_context(out::IO, str::String) typelimitflag = get(out, :stacktrace_types_limited, nothing) if typelimitflag isa RefValue{Bool} - sz = get(out, :displaysize, displaysize(out))::Tuple{Int, Int} + sz = get(out, :displaysize, Base.displaysize_(out))::Tuple{Int, Int} str_lim = type_depth_limit(str, max(sz[2], 120)) if sizeof(str_lim) < sizeof(str) typelimitflag[] = true diff --git a/base/stream.jl b/base/stream.jl index 93aeead79eb9c..3ca5717be29db 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -569,6 +569,13 @@ displaysize(io::IO) = displaysize() displaysize() = (parse(Int, get(ENV, "LINES", "24")), parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int} +# This is a fancy way to make de-specialize a call to `displaysize(io::IO)` +# which is unfortunately invalidated by REPL +# (https://github.com/JuliaLang/julia/issues/56080) +# +# This makes the call less efficient, but avoids being invalidated by REPL. +displaysize_(io::IO) = Base.invoke_in_world(Base.tls_world_age(), displaysize, io)::Tuple{Int,Int} + function displaysize(io::TTY) check_open(io) From 9f92989274640b14d4d0015107e501e7252344b2 Mon Sep 17 00:00:00 2001 From: abhro <5664668+abhro@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:30:52 -0400 Subject: [PATCH 395/548] Fix markdown list in installation.md (#56165) Documenter.jl requires all trailing list content to follow the same indentation as the header. So, in the current view (https://docs.julialang.org/en/v1/manual/installation/#Command-line-arguments) the list appears broken. --- doc/src/manual/installation.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/src/manual/installation.md b/doc/src/manual/installation.md index 07acfd1c62681..f45aba2c37a28 100644 --- a/doc/src/manual/installation.md +++ b/doc/src/manual/installation.md @@ -44,21 +44,21 @@ curl -fsSL https://install.julialang.org | sh -s -- Here `` should be replaced with one or more of the following arguments: - `--yes` (or `-y`): Run the installer in a non-interactive mode. All -configuration values use their default or a value supplied as a command line -argument. + configuration values use their default or a value supplied as a command line + argument. - `--default-channel=`: Configure the default Juliaup channel. For -example `--default-channel lts` would install the `lts` channel and configure it -as the default. + example `--default-channel lts` would install the `lts` channel and configure it + as the default. - `--add-to-path=`: Configure whether Julia should be added to the `PATH` -environment variable. Valid values are `yes` (default) and `no`. + environment variable. Valid values are `yes` (default) and `no`. - `--background-selfupdate=`: Configure an optional CRON job that -auto-updates Juliaup if `` has a value larger than 0. The actual value -controls how often the CRON job will run to check for a new Juliaup version in -seconds. The default value is 0, i.e. no CRON job will be created. + auto-updates Juliaup if `` has a value larger than 0. The actual value + controls how often the CRON job will run to check for a new Juliaup version in + seconds. The default value is 0, i.e. no CRON job will be created. - `--startup-selfupdate=`: Configure how often Julia will check for new -versions of Juliaup when Julia is started. The default is every 1440 minutes. + versions of Juliaup when Julia is started. The default is every 1440 minutes. - `-p=` (or `--path`): Configure where the Julia and Juliaup binaries are -installed. The default is `~/.juliaup`. + installed. The default is `~/.juliaup`. ## Alternative installation methods From d09abe55f1c979df347fa682037c4f68f69b48c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:57:39 +0100 Subject: [PATCH 396/548] [Random] Add more comments and a helper function in Xoshiro code (#56144) Follow up to #55994 and #55997. This should basically be a non-functional change and I see no performance difference, but the comments and the definition of a helper function should make the code easier to follow (I initially struggled in #55997) and extend to other types. --- stdlib/Random/src/Xoshiro.jl | 21 +++++++++++++-------- stdlib/Random/src/XoshiroSimd.jl | 21 +++++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index 09a3e386e9a2b..94c7e1ab24e1d 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -296,11 +296,16 @@ rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt52Raw{UInt64}}) = ran rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt52{UInt64}}) = rand(r, UInt64) >>> 12 rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) -rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float16}}) = - Float16(rand(r, UInt16) >>> 5) * Float16(0x1.0p-11) - -rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float32}}) = - Float32(rand(r, UInt32) >>> 8) * Float32(0x1.0p-24) - -rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01_64}) = - Float64(rand(r, UInt64) >>> 11) * 0x1.0p-53 +for FT in (Float16, Float32, Float64) + UT = Base.uinttype(FT) + # Helper function: scale an unsigned integer to a floating point number of the same size + # in the interval [0, 1). This is equivalent to, but more easily extensible than + # Float16(i >>> 5) * Float16(0x1.0p-11) + # Float32(i >>> 8) * Float32(0x1.0p-24) + # Float32(i >>> 11) * Float64(0x1.0p-53) + @eval @inline _uint2float(i::$(UT), ::Type{$(FT)}) = + $(FT)(i >>> $(8 * sizeof(FT) - precision(FT))) * $(FT(2) ^ -precision(FT)) + + @eval rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{$(FT)}}) = + _uint2float(rand(r, $(UT)), $(FT)) +end diff --git a/stdlib/Random/src/XoshiroSimd.jl b/stdlib/Random/src/XoshiroSimd.jl index 1c5f8306cc302..58544714dd9f5 100644 --- a/stdlib/Random/src/XoshiroSimd.jl +++ b/stdlib/Random/src/XoshiroSimd.jl @@ -3,7 +3,7 @@ module XoshiroSimd # Getting the xoroshiro RNG to reliably vectorize is somewhat of a hassle without Simd.jl. import ..Random: rand! -using ..Random: TaskLocalRNG, rand, Xoshiro, CloseOpen01, UnsafeView, SamplerType, SamplerTrivial, getstate, setstate! +using ..Random: TaskLocalRNG, rand, Xoshiro, CloseOpen01, UnsafeView, SamplerType, SamplerTrivial, getstate, setstate!, _uint2float using Base: BitInteger_types using Base.Libc: memcpy using Core.Intrinsics: llvmcall @@ -30,7 +30,12 @@ simdThreshold(::Type{Bool}) = 640 Tuple{UInt64, Int64}, x, y) -@inline _bits2float(x::UInt64, ::Type{Float64}) = reinterpret(UInt64, Float64(x >>> 11) * 0x1.0p-53) +# `_bits2float(x::UInt64, T)` takes `x::UInt64` as input, it splits it in `N` parts where +# `N = sizeof(UInt64) / sizeof(T)` (`N = 1` for `Float64`, `N = 2` for `Float32, etc...), it +# truncates each part to the unsigned type of the same size as `T`, scales all of these +# numbers to a value of type `T` in the range [0,1) with `_uint2float`, and then +# recomposes another `UInt64` using all these parts. +@inline _bits2float(x::UInt64, ::Type{Float64}) = reinterpret(UInt64, _uint2float(x, Float64)) @inline function _bits2float(x::UInt64, ::Type{Float32}) #= # this implementation uses more high bits, but is harder to vectorize @@ -40,8 +45,8 @@ simdThreshold(::Type{Bool}) = 640 =# ui = (x>>>32) % UInt32 li = x % UInt32 - u = Float32(ui >>> 8) * Float32(0x1.0p-24) - l = Float32(li >>> 8) * Float32(0x1.0p-24) + u = _uint2float(ui, Float32) + l = _uint2float(ui, Float32) (UInt64(reinterpret(UInt32, u)) << 32) | UInt64(reinterpret(UInt32, l)) end @inline function _bits2float(x::UInt64, ::Type{Float16}) @@ -49,10 +54,10 @@ end i2 = (x>>>32) % UInt16 i3 = (x>>>16) % UInt16 i4 = x % UInt16 - f1 = Float16(i1 >>> 5) * Float16(0x1.0p-11) - f2 = Float16(i2 >>> 5) * Float16(0x1.0p-11) - f3 = Float16(i3 >>> 5) * Float16(0x1.0p-11) - f4 = Float16(i4 >>> 5) * Float16(0x1.0p-11) + f1 = _uint2float(i1, Float16) + f2 = _uint2float(i2, Float16) + f3 = _uint2float(i3, Float16) + f4 = _uint2float(i4, Float16) return (UInt64(reinterpret(UInt16, f1)) << 48) | (UInt64(reinterpret(UInt16, f2)) << 32) | (UInt64(reinterpret(UInt16, f3)) << 16) | UInt64(reinterpret(UInt16, f4)) end From 8a18f27e24b3fe83815f81fdbe97d6e77f1802df Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 26 Aug 2024 12:59:45 -0400 Subject: [PATCH 397/548] add objects to concisely specify initialization PerProcess: once per process PerThread: once per thread id PerTask: once per task object --- NEWS.md | 6 + base/docs/basedocs.jl | 2 + base/exports.jl | 3 + base/lock.jl | 252 +++++++++++++++++++++++++++++++++++++++++- doc/src/base/base.md | 3 + test/precompile.jl | 21 ++++ test/threads.jl | 66 +++++++++++ 7 files changed, 352 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 9aebf5d42d954..724d0793e67cd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -68,6 +68,12 @@ variables. ([#53742]). Multi-threading changes ----------------------- +* New types are defined to handle the pattern of code that must run once per process, called + a `PerProcess{T}` type, which allows defining a function that should be run exactly once + the first time it is called, and then always return the same result value of type `T` + every subsequent time afterwards. There are also `PerThread{T}` and `PerTask{T}` types for + similar usage with threads or tasks. ([#TBD]) + Build system changes -------------------- diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index a142ecffdb732..f93d9a5ba0647 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -153,6 +153,8 @@ runtime initialization functions of external C libraries and initializing global that involve pointers returned by external libraries. See the [manual section about modules](@ref modules) for more details. +See also: [`PerProcess`](@ref). + # Examples ```julia const foo_data_ptr = Ref{Ptr{Cvoid}}(0) diff --git a/base/exports.jl b/base/exports.jl index daba9a010a9e6..66de141c228b6 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -70,6 +70,9 @@ export OrdinalRange, Pair, PartialQuickSort, + PerProcess, + PerTask, + PerThread, PermutedDimsArray, QuickSort, Rational, diff --git a/base/lock.jl b/base/lock.jl index b473045e5809d..80ab2b3bb9b42 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -500,7 +500,7 @@ Create a level-triggered event source. Tasks that call [`wait`](@ref) on an After `notify` is called, the `Event` remains in a signaled state and tasks will no longer block when waiting for it, until `reset` is called. -If `autoreset` is true, at most one task will be released from `wait` for +If `autoreset` is true, at most one task will be released from `wait` for) each call to `notify`. This provides an acquire & release memory ordering on notify/wait. @@ -570,3 +570,253 @@ end import .Base: Event export Event end + + +""" + PerProcess{T} + +Calling a `PerProcess` object returns a value of type `T` by running the +function `initializer` exactly once per process. All concurrent and future +calls in the same process will return exactly the same value. This is useful in +code that will be precompiled, as it allows setting up caches or other state +which won't get serialized. + +## Example + +```jldoctest +julia> const global_state = Base.PerProcess{Vector{UInt32}}() do + println("Making lazy global value...done.") + return [Libc.rand()] + end; + +julia> procstate = global_state(); +Making lazy global value...done. + +julia> procstate === global_state() +true + +julia> procstate === fetch(@async global_state()) +true +``` +""" +mutable struct PerProcess{T, F} + x::Union{Nothing,T} + @atomic state::UInt8 # 0=initial, 1=hasrun, 2=error + @atomic allow_compile_time::Bool + const initializer::F + const lock::ReentrantLock + + PerProcess{T}(initializer::F) where {T, F} = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) + PerProcess{T,F}(initializer::F) where {T, F} = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) + PerProcess(initializer) = new{Base.promote_op(initializer), typeof(initializer)}(nothing, 0x00, true, initializer, ReentrantLock()) +end +@inline function (once::PerProcess{T})() where T + state = (@atomic :acquire once.state) + if state != 0x01 + (@noinline function init_perprocesss(once, state) + state == 0x02 && error("PerProcess initializer failed previously") + Base.__precompile__(once.allow_compile_time) + lock(once.lock) + try + state = @atomic :monotonic once.state + if state == 0x00 + once.x = once.initializer() + elseif state == 0x02 + error("PerProcess initializer failed previously") + elseif state != 0x01 + error("invalid state for PerProcess") + end + catch + state == 0x02 || @atomic :release once.state = 0x02 + unlock(once.lock) + rethrow() + end + state == 0x01 || @atomic :release once.state = 0x01 + unlock(once.lock) + nothing + end)(once, state) + end + return once.x::T +end + +function copyto_monotonic!(dest::AtomicMemory, src) + i = 1 + for j in eachindex(src) + if isassigned(src, j) + @atomic :monotonic dest[i] = src[j] + end + i += 1 + end + dest +end + +function fill_monotonic!(dest::AtomicMemory, x) + for i = 1:length(dest) + @atomic :monotonic dest[i] = x + end + dest +end + + +# share a lock, since we just need it briefly, so some contention is okay +const PerThreadLock = ThreadSynchronizer() +""" + PerThread{T} + +Calling a `PerThread` object returns a value of type `T` by running the function +`initializer` exactly once per thread. All future calls in the same thread, and +concurrent or future calls with the same thread id, will return exactly the +same value. The object can also be indexed by the threadid for any existing +thread, to get (or initialize *on this thread*) the value stored for that +thread. Incorrect usage can lead to data-races or memory corruption so use only +if that behavior is correct within your library's threading-safety design. + +Warning: it is not necessarily true that a Task only runs on one thread, therefore the value +returned here may alias other values or change in the middle of your program. This type may +get deprecated in the future. If initializer yields, the thread running the current task +after the call might not be the same as the one at the start of the call. + +See also: [`PerTask`](@ref). + +## Example + +```jldoctest +julia> const thread_state = Base.PerThread{Vector{UInt32}}() do + println("Making lazy thread value...done.") + return [Libc.rand()] + end; + +julia> threadvec = thread_state(); +Making lazy thread value...done. + +julia> threadvec === fetch(@async thread_state()) +true + +julia> threadvec === thread_state[Threads.threadid()] +true +``` +""" +mutable struct PerThread{T, F} + @atomic xs::AtomicMemory{T} # values + @atomic ss::AtomicMemory{UInt8} # states: 0=initial, 1=hasrun, 2=error, 3==concurrent + const initializer::F + + PerThread{T}(initializer::F) where {T, F} = new{T,F}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer) + PerThread{T,F}(initializer::F) where {T, F} = new{T,F}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer) + PerThread(initializer) = (T = Base.promote_op(initializer); new{T, typeof(initializer)}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer)) +end +@inline function getindex(once::PerThread, tid::Integer) + tid = Int(tid) + ss = @atomic :acquire once.ss + xs = @atomic :monotonic once.xs + # n.b. length(xs) >= length(ss) + if tid > length(ss) || (@atomic :acquire ss[tid]) != 0x01 + (@noinline function init_perthread(once, tid) + local xs = @atomic :acquire once.xs + local ss = @atomic :monotonic once.ss + local len = length(ss) + # slow path to allocate it + nt = Threads.maxthreadid() + 0 < tid <= nt || ArgumentError("thread id outside of allocated range") + if tid <= length(ss) && (@atomic :acquire ss[tid]) == 0x02 + error("PerThread initializer failed previously") + end + newxs = xs + newss = ss + if tid > len + # attempt to do all allocations outside of PerThreadLock for better scaling + @assert length(xs) == length(ss) "logical constraint violation" + newxs = typeof(xs)(undef, len + nt) + newss = typeof(ss)(undef, len + nt) + end + # uses state and locks to ensure this runs exactly once per tid argument + lock(PerThreadLock) + try + ss = @atomic :monotonic once.ss + xs = @atomic :monotonic once.xs + if tid > length(ss) + @assert length(ss) >= len && newxs !== xs && newss != ss "logical constraint violation" + fill_monotonic!(newss, 0x00) + xs = copyto_monotonic!(newxs, xs) + ss = copyto_monotonic!(newss, ss) + @atomic :release once.xs = xs + @atomic :release once.ss = ss + end + state = @atomic :monotonic ss[tid] + while state == 0x04 + # lost race, wait for notification this is done running elsewhere + wait(PerThreadLock) # wait for initializer to finish without releasing this thread + ss = @atomic :monotonic once.ss + state = @atomic :monotonic ss[tid] == 0x04 + end + if state == 0x00 + # won the race, drop lock in exchange for state, and run user initializer + @atomic :monotonic ss[tid] = 0x04 + result = try + unlock(PerThreadLock) + once.initializer() + catch + lock(PerThreadLock) + ss = @atomic :monotonic once.ss + @atomic :release ss[tid] = 0x02 + notify(PerThreadLock) + rethrow() + end + # store result and notify waiters + lock(PerThreadLock) + xs = @atomic :monotonic once.xs + @atomic :release xs[tid] = result + ss = @atomic :monotonic once.ss + @atomic :release ss[tid] = 0x01 + notify(PerThreadLock) + elseif state == 0x02 + error("PerThread initializer failed previously") + elseif state != 0x01 + error("invalid state for PerThread") + end + finally + unlock(PerThreadLock) + end + nothing + end)(once, tid) + xs = @atomic :monotonic once.xs + end + return xs[tid] +end +@inline (once::PerThread)() = once[Threads.threadid()] + +""" + PerTask{T} + +Calling a `PerTask` object returns a value of type `T` by running the function `initializer` +exactly once per Task. All future calls in the same Task will return exactly the same value. + +See also: [`task_local_storage`](@ref). + +## Example + +```jldoctest +julia> const task_state = Base.PerTask{Vector{UInt32}}() do + println("Making lazy task value...done.") + return [Libc.rand()] + end; + +julia> taskvec = task_state(); +Making lazy task value...done. + +julia> taskvec === task_state() +true + +julia> taskvec === fetch(@async task_state()) +Making lazy task value...done. +false +``` +""" +mutable struct PerTask{T, F} + const initializer::F + + PerTask{T}(initializer::F) where {T, F} = new{T,F}(initializer) + PerTask{T,F}(initializer::F) where {T, F} = new{T,F}(initializer) + PerTask(initializer) = new{Base.promote_op(initializer), typeof(initializer)}(initializer) +end +@inline (once::PerTask)() = get!(once.initializer, task_local_storage(), once) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index b5d50a846ce89..b11e985782709 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -34,6 +34,9 @@ Main.include Base.include_string Base.include_dependency __init__ +Base.PerProcess +Base.PerTask +Base.PerThread Base.which(::Any, ::Any) Base.methods Base.@show diff --git a/test/precompile.jl b/test/precompile.jl index 7a6e41061f9b1..e44771fb6a86f 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -94,6 +94,17 @@ precompile_test_harness(false) do dir end abstract type AbstractAlgebraMap{A} end struct GAPGroupHomomorphism{A, B} <: AbstractAlgebraMap{GAPGroupHomomorphism{B, A}} end + + global process_state_calls::Int = 0 + const process_state = Base.PerProcess{typeof(getpid())}() do + @assert (global process_state_calls += 1) == 1 + return getpid() + end + const mypid = process_state() + @assert process_state_calls === 1 + process_state_calls = 0 + @assert process_state() === process_state() + @assert process_state_calls === 0 end """) write(Foo2_file, @@ -272,6 +283,9 @@ precompile_test_harness(false) do dir oid_vec_int = objectid(a_vec_int) oid_mat_int = objectid(a_mat_int) + + using $FooBase_module: process_state, mypid as FooBase_pid, process_state_calls + const mypid = process_state() end """) # Issue #52063 @@ -333,6 +347,13 @@ precompile_test_harness(false) do dir @test isready(Foo.ch2) @test take!(Foo.ch2) === 2 @test !isready(Foo.ch2) + + @test Foo.process_state_calls === 0 + @test Foo.process_state() === getpid() + @test Foo.mypid !== getpid() + @test Foo.FooBase_pid !== getpid() + @test Foo.mypid !== Foo.FooBase_pid + @test Foo.process_state_calls === 1 end let diff --git a/test/threads.jl b/test/threads.jl index 6265368c2ac79..f1a8aba418412 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -374,3 +374,69 @@ end end end end + +let once = PerProcess(() -> return [nothing]) + @test typeof(once) <: PerProcess{Vector{Nothing}} + x = once() + @test x === once() + @atomic once.state = 0xff + @test_throws ErrorException("invalid state for PerProcess") once() + @test_throws ErrorException("PerProcess initializer failed previously") once() + @atomic once.state = 0x01 + @test x === once() +end +let once = PerProcess{Int}(() -> error("expected")) + @test_throws ErrorException("expected") once() + @test_throws ErrorException("PerProcess initializer failed previously") once() +end + +let once = PerThread(() -> return [nothing]) + @test typeof(once) <: PerThread{Vector{Nothing}} + x = once() + @test x === once() === fetch(@async once()) + tids = zeros(UInt, 50) + onces = Vector{Vector{Nothing}}(undef, length(tids)) + for i = 1:length(tids) + function cl() + local y = once() + onces[i] = y + @test x !== y === once() + nothing + end + function threadcallclosure(cl::F) where {F} # create sparam so we can reference the type of cl in the ccall type + threadwork = @cfunction cl -> cl() Cvoid (Ref{F},) # create a cfunction that specializes on cl as an argument and calls it + err = @ccall uv_thread_create(Ref(tids, i)::Ptr{UInt}, threadwork::Ptr{Cvoid}, cl::Ref{F})::Cint # call that on a thread + err == 0 || Base.uv_error("uv_thread_create", err) + end + threadcallclosure(cl) + end + @noinline function waitallthreads(tids) + for i = 1:length(tids) + tid = Ref(tids, i) + tidp = Base.unsafe_convert(Ptr{UInt}, tid)::Ptr{UInt} + gc_state = @ccall jl_gc_safe_enter()::Int8 + GC.@preserve tid err = @ccall uv_thread_join(tidp::Ptr{UInt})::Cint + @ccall jl_gc_safe_leave(gc_state::Int8)::Cvoid + err == 0 || Base.uv_error("uv_thread_join", err) + end + end + waitallthreads(tids) + @test length(IdSet{eltype(onces)}(onces)) == length(onces) # make sure every object is unique + +end +let once = PerThread{Int}(() -> error("expected")) + @test_throws ErrorException("expected") once() + @test_throws ErrorException("PerThread initializer failed previously") once() +end + +let once = PerTask(() -> return [nothing]) + @test typeof(once) <: PerTask{Vector{Nothing}} + x = once() + @test x === once() !== fetch(@async once()) + delete!(task_local_storage(), once) + @test x !== once() === once() +end +let once = PerTask{Int}(() -> error("expected")) + @test_throws ErrorException("expected") once() + @test_throws ErrorException("expected") once() +end From a66733f0e9ea547eb43a2c7637afb3429471e10b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 26 Aug 2024 15:46:12 -0400 Subject: [PATCH 398/548] add precompile support for recording fields to change Somewhat generalizes our support for changing Ptr to C_NULL. Not particularly fast, since it is just using the builtins implementation of setfield, and delaying the actual stores, but it should suffice. --- base/lock.jl | 38 ++++++++++++--- base/task.jl | 7 --- src/builtins.c | 2 +- src/gc-stock.c | 2 + src/julia_internal.h | 2 + src/staticdata.c | 111 ++++++++++++++++++++++++++++++++++++++++++- test/threads.jl | 22 ++++++++- 7 files changed, 167 insertions(+), 17 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index 80ab2b3bb9b42..b53607af05a3a 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -2,6 +2,13 @@ const ThreadSynchronizer = GenericCondition{Threads.SpinLock} +""" + current_task() + +Get the currently running [`Task`](@ref). +""" +current_task() = ccall(:jl_get_current_task, Ref{Task}, ()) + # Advisory reentrant lock """ ReentrantLock() @@ -606,16 +613,23 @@ mutable struct PerProcess{T, F} const initializer::F const lock::ReentrantLock - PerProcess{T}(initializer::F) where {T, F} = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) - PerProcess{T,F}(initializer::F) where {T, F} = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) - PerProcess(initializer) = new{Base.promote_op(initializer), typeof(initializer)}(nothing, 0x00, true, initializer, ReentrantLock()) + function PerProcess{T,F}(initializer::F) where {T, F} + once = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) + ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), + once, :x, nothing) + ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), + once, :state, 0x00) + return once + end end +PerProcess{T}(initializer::F) where {T, F} = PerProcess{T, F}(initializer) +PerProcess(initializer) = PerProcess{Base.promote_op(initializer), typeof(initializer)}(initializer) @inline function (once::PerProcess{T})() where T state = (@atomic :acquire once.state) if state != 0x01 (@noinline function init_perprocesss(once, state) state == 0x02 && error("PerProcess initializer failed previously") - Base.__precompile__(once.allow_compile_time) + once.allow_compile_time || __precompile__(false) lock(once.lock) try state = @atomic :monotonic once.state @@ -644,6 +658,8 @@ function copyto_monotonic!(dest::AtomicMemory, src) for j in eachindex(src) if isassigned(src, j) @atomic :monotonic dest[i] = src[j] + #else + # _unsafeindex_atomic!(dest, i, src[j], :monotonic) end i += 1 end @@ -701,10 +717,18 @@ mutable struct PerThread{T, F} @atomic ss::AtomicMemory{UInt8} # states: 0=initial, 1=hasrun, 2=error, 3==concurrent const initializer::F - PerThread{T}(initializer::F) where {T, F} = new{T,F}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer) - PerThread{T,F}(initializer::F) where {T, F} = new{T,F}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer) - PerThread(initializer) = (T = Base.promote_op(initializer); new{T, typeof(initializer)}(AtomicMemory{T}(), AtomicMemory{UInt8}(), initializer)) + function PerThread{T,F}(initializer::F) where {T, F} + xs, ss = AtomicMemory{T}(), AtomicMemory{UInt8}() + once = new{T,F}(xs, ss, initializer) + ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), + once, :xs, xs) + ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), + once, :ss, ss) + return once + end end +PerThread{T}(initializer::F) where {T, F} = PerThread{T,F}(initializer) +PerThread(initializer) = PerThread{Base.promote_op(initializer), typeof(initializer)}(initializer) @inline function getindex(once::PerThread, tid::Integer) tid = Int(tid) ss = @atomic :acquire once.ss diff --git a/base/task.jl b/base/task.jl index 6cb1ff785eeee..f3a134f374421 100644 --- a/base/task.jl +++ b/base/task.jl @@ -143,13 +143,6 @@ macro task(ex) :(Task($thunk)) end -""" - current_task() - -Get the currently running [`Task`](@ref). -""" -current_task() = ccall(:jl_get_current_task, Ref{Task}, ()) - # task states const task_state_runnable = UInt8(0) diff --git a/src/builtins.c b/src/builtins.c index 96c4cec0f5087..b129cca0ee71d 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1008,7 +1008,7 @@ static inline size_t get_checked_fieldindex(const char *name, jl_datatype_t *st, else { jl_value_t *ts[2] = {(jl_value_t*)jl_long_type, (jl_value_t*)jl_symbol_type}; jl_value_t *t = jl_type_union(ts, 2); - jl_type_error("getfield", t, arg); + jl_type_error(name, t, arg); } if (mutabl && jl_field_isconst(st, idx)) { jl_errorf("%s: const field .%s of type %s cannot be changed", name, diff --git a/src/gc-stock.c b/src/gc-stock.c index 6b97881909bbd..37c7b4df48218 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2741,6 +2741,8 @@ static void gc_mark_roots(jl_gc_markqueue_t *mq) gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_global_roots_list, "global_roots_list"); gc_try_claim_and_push(mq, jl_global_roots_keyset, NULL); gc_heap_snapshot_record_gc_roots((jl_value_t*)jl_global_roots_keyset, "global_roots_keyset"); + gc_try_claim_and_push(mq, precompile_field_replace, NULL); + gc_heap_snapshot_record_gc_roots((jl_value_t*)precompile_field_replace, "precompile_field_replace"); } // find unmarked objects that need to be finalized from the finalizer list "list". diff --git a/src/julia_internal.h b/src/julia_internal.h index 20d90fede3d5e..3c93d9fd0963d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -858,6 +858,8 @@ extern jl_genericmemory_t *jl_global_roots_keyset JL_GLOBALLY_ROOTED; extern arraylist_t *jl_entrypoint_mis; JL_DLLEXPORT int jl_is_globally_rooted(jl_value_t *val JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert) JL_GLOBALLY_ROOTED; +extern jl_svec_t *precompile_field_replace JL_GLOBALLY_ROOTED; +JL_DLLEXPORT void jl_set_precompile_field_replace(jl_value_t *val, jl_value_t *field, jl_value_t *newval) JL_GLOBALLY_ROOTED; jl_opaque_closure_t *jl_new_opaque_closure(jl_tupletype_t *argt, jl_value_t *rt_lb, jl_value_t *rt_ub, jl_value_t *source, jl_value_t **env, size_t nenv, int do_compile); diff --git a/src/staticdata.c b/src/staticdata.c index 0a8cbe6db7c67..5188659d8618d 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -495,6 +495,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 bits_replace; static htable_t relocatable_ext_cis; // array of definitions for the predefined function pointers @@ -1649,7 +1650,23 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED write_padding(f, offset - tot); tot = offset; size_t fsz = jl_field_size(t, i); - if (t->name->mutabl && jl_is_cpointer_type(jl_field_type_concrete(t, i)) && *(intptr_t*)slot != -1) { + jl_value_t *replace = (jl_value_t*)ptrhash_get(&bits_replace, (void*)slot); + if (replace != HT_NOTFOUND) { + assert(t->name->mutabl && !jl_field_isptr(t, i)); + jl_value_t *rty = jl_typeof(replace); + size_t sz = jl_datatype_size(rty); + ios_write(f, (const char*)replace, sz); + jl_value_t *ft = jl_field_type_concrete(t, i); + int isunion = jl_is_uniontype(ft); + unsigned nth = 0; + if (!jl_find_union_component(ft, rty, &nth)) + assert(0 && "invalid field assignment to isbits union"); + assert(sz <= fsz - isunion); + write_padding(f, fsz - sz - isunion); + if (isunion) + write_uint8(f, nth); + } + else if (t->name->mutabl && jl_is_cpointer_type(jl_field_type_concrete(t, i)) && *(intptr_t*)slot != -1) { // reset Ptr fields to C_NULL (but keep MAP_FAILED / INVALID_HANDLE) assert(!jl_field_isptr(t, i)); write_pointer(f); @@ -2643,6 +2660,65 @@ jl_mutex_t global_roots_lock; extern jl_mutex_t world_counter_lock; extern size_t jl_require_world; +jl_mutex_t precompile_field_replace_lock; +jl_svec_t *precompile_field_replace JL_GLOBALLY_ROOTED; + +static inline jl_value_t *get_checked_fieldindex(const char *name, jl_datatype_t *st, jl_value_t *v, jl_value_t *arg, int mutabl) +{ + if (mutabl) { + if (st == jl_module_type) + jl_error("cannot assign variables in other modules"); + if (!st->name->mutabl) + jl_errorf("%s: immutable struct of type %s cannot be changed", name, jl_symbol_name(st->name->name)); + } + size_t idx; + if (jl_is_long(arg)) { + idx = jl_unbox_long(arg) - 1; + if (idx >= jl_datatype_nfields(st)) + jl_bounds_error(v, arg); + } + else if (jl_is_symbol(arg)) { + idx = jl_field_index(st, (jl_sym_t*)arg, 1); + arg = jl_box_long(idx); + } + else { + jl_value_t *ts[2] = {(jl_value_t*)jl_long_type, (jl_value_t*)jl_symbol_type}; + jl_value_t *t = jl_type_union(ts, 2); + jl_type_error(name, t, arg); + } + if (mutabl && jl_field_isconst(st, idx)) { + jl_errorf("%s: const field .%s of type %s cannot be changed", name, + jl_symbol_name((jl_sym_t*)jl_svecref(jl_field_names(st), idx)), jl_symbol_name(st->name->name)); + } + return arg; +} + +JL_DLLEXPORT void jl_set_precompile_field_replace(jl_value_t *val, jl_value_t *field, jl_value_t *newval) +{ + if (!jl_generating_output()) + return; + jl_datatype_t *st = (jl_datatype_t*)jl_typeof(val); + jl_value_t *idx = get_checked_fieldindex("setfield!", st, val, field, 1); + JL_GC_PUSH1(&idx); + size_t idxval = jl_unbox_long(idx); + jl_value_t *ft = jl_field_type_concrete(st, idxval); + if (!jl_isa(newval, ft)) + jl_type_error("setfield!", ft, newval); + JL_LOCK(&precompile_field_replace_lock); + if (precompile_field_replace == NULL) { + precompile_field_replace = jl_alloc_svec(3); + jl_svecset(precompile_field_replace, 0, jl_alloc_vec_any(0)); + jl_svecset(precompile_field_replace, 1, jl_alloc_vec_any(0)); + jl_svecset(precompile_field_replace, 2, jl_alloc_vec_any(0)); + } + jl_array_ptr_1d_push((jl_array_t*)jl_svecref(precompile_field_replace, 0), val); + jl_array_ptr_1d_push((jl_array_t*)jl_svecref(precompile_field_replace, 1), idx); + jl_array_ptr_1d_push((jl_array_t*)jl_svecref(precompile_field_replace, 2), newval); + JL_GC_POP(); + JL_UNLOCK(&precompile_field_replace_lock); +} + + JL_DLLEXPORT int jl_is_globally_rooted(jl_value_t *val JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT { if (jl_is_datatype(val)) { @@ -2762,6 +2838,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED { htable_new(&field_replace, 0); + htable_new(&bits_replace, 0); // strip metadata and IR when requested if (jl_options.strip_metadata || jl_options.strip_ir) jl_strip_all_codeinfos(); @@ -2773,6 +2850,37 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, arraylist_new(&gvars, 0); arraylist_t external_fns; arraylist_new(&external_fns, 0); + // prepare hash table with any fields the user wanted us to rewrite during serialization + if (precompile_field_replace) { + jl_array_t *vals = (jl_array_t*)jl_svecref(precompile_field_replace, 0); + jl_array_t *fields = (jl_array_t*)jl_svecref(precompile_field_replace, 1); + jl_array_t *newvals = (jl_array_t*)jl_svecref(precompile_field_replace, 2); + size_t i, l = jl_array_nrows(vals); + assert(jl_array_nrows(fields) == l && jl_array_nrows(newvals) == l); + for (i = 0; i < l; i++) { + jl_value_t *val = jl_array_ptr_ref(vals, i); + size_t field = jl_unbox_long(jl_array_ptr_ref(fields, i)); + jl_value_t *newval = jl_array_ptr_ref(newvals, i); + jl_datatype_t *st = (jl_datatype_t*)jl_typeof(val); + size_t offs = jl_field_offset(st, field); + char *fldaddr = (char*)val + offs; + if (jl_field_isptr(st, field)) { + record_field_change((jl_value_t**)fldaddr, newval); + } + else { + // replace the bits + ptrhash_put(&bits_replace, (void*)fldaddr, newval); + // and any pointers inside + jl_datatype_t *rty = (jl_datatype_t*)jl_typeof(newval); + const jl_datatype_layout_t *layout = rty->layout; + size_t j, np = layout->npointers; + for (j = 0; j < np; j++) { + uint32_t ptr = jl_ptr_offset(rty, j); + record_field_change((jl_value_t**)fldaddr + ptr, *(((jl_value_t**)newval) + ptr)); + } + } + } + } int en = jl_gc_enable(0); if (native_functions) { @@ -3113,6 +3221,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, arraylist_free(&gvars); arraylist_free(&external_fns); htable_free(&field_replace); + htable_free(&bits_replace); htable_free(&serialization_order); htable_free(&nullptrs); htable_free(&symbol_table); diff --git a/test/threads.jl b/test/threads.jl index f1a8aba418412..d8e9fd4ce2901 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -390,10 +390,19 @@ let once = PerProcess{Int}(() -> error("expected")) @test_throws ErrorException("PerProcess initializer failed previously") once() end -let once = PerThread(() -> return [nothing]) +let e = Base.Event(true), + started = Channel{Int16}(Inf), + once = PerThread() do + push!(started, threadid()) + wait(e) + return [nothing] + end @test typeof(once) <: PerThread{Vector{Nothing}} + notify(e) x = once() @test x === once() === fetch(@async once()) + @test take!(started) == threadid() + @test isempty(started) tids = zeros(UInt, 50) onces = Vector{Vector{Nothing}}(undef, length(tids)) for i = 1:length(tids) @@ -420,7 +429,18 @@ let once = PerThread(() -> return [nothing]) err == 0 || Base.uv_error("uv_thread_join", err) end end + # let them finish in 5 batches of 10 + for i = 1:length(tids) ÷ 10 + for i = 1:10 + @test take!(started) != threadid() + end + for i = 1:10 + notify(e) + end + end + @test isempty(started) waitallthreads(tids) + @test isempty(started) @test length(IdSet{eltype(onces)}(onces)) == length(onces) # make sure every object is unique end From dbbd4d96fd560b41db2324ee31fd37eb734fa39e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 17 Sep 2024 10:03:19 -0400 Subject: [PATCH 399/548] improve OncePer implementation Address reviewer feedback, add more fixes and more tests, rename to add Once prefix. --- NEWS.md | 4 +- base/docs/basedocs.jl | 2 +- base/exports.jl | 6 +- base/lock.jl | 148 ++++++++++++++++++++++-------------------- doc/src/base/base.md | 6 +- test/precompile.jl | 2 +- test/threads.jl | 92 ++++++++++++++++++-------- 7 files changed, 153 insertions(+), 107 deletions(-) diff --git a/NEWS.md b/NEWS.md index 724d0793e67cd..e304c78f8ad66 100644 --- a/NEWS.md +++ b/NEWS.md @@ -69,9 +69,9 @@ Multi-threading changes ----------------------- * New types are defined to handle the pattern of code that must run once per process, called - a `PerProcess{T}` type, which allows defining a function that should be run exactly once + a `OncePerProcess{T}` type, which allows defining a function that should be run exactly once the first time it is called, and then always return the same result value of type `T` - every subsequent time afterwards. There are also `PerThread{T}` and `PerTask{T}` types for + every subsequent time afterwards. There are also `OncePerThread{T}` and `OncePerTask{T}` types for similar usage with threads or tasks. ([#TBD]) Build system changes diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index f93d9a5ba0647..0d5d5ac00e8d0 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -153,7 +153,7 @@ runtime initialization functions of external C libraries and initializing global that involve pointers returned by external libraries. See the [manual section about modules](@ref modules) for more details. -See also: [`PerProcess`](@ref). +See also: [`OncePerProcess`](@ref). # Examples ```julia diff --git a/base/exports.jl b/base/exports.jl index 66de141c228b6..56cd58ce269e7 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -70,9 +70,9 @@ export OrdinalRange, Pair, PartialQuickSort, - PerProcess, - PerTask, - PerThread, + OncePerProcess, + OncePerTask, + OncePerThread, PermutedDimsArray, QuickSort, Rational, diff --git a/base/lock.jl b/base/lock.jl index b53607af05a3a..a44cd4c0d63cf 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -507,7 +507,7 @@ Create a level-triggered event source. Tasks that call [`wait`](@ref) on an After `notify` is called, the `Event` remains in a signaled state and tasks will no longer block when waiting for it, until `reset` is called. -If `autoreset` is true, at most one task will be released from `wait` for) +If `autoreset` is true, at most one task will be released from `wait` for each call to `notify`. This provides an acquire & release memory ordering on notify/wait. @@ -578,11 +578,15 @@ end export Event end +const PerStateInitial = 0x00 +const PerStateHasrun = 0x01 +const PerStateErrored = 0x02 +const PerStateConcurrent = 0x03 """ - PerProcess{T} + OncePerProcess{T}(init::Function)() -> T -Calling a `PerProcess` object returns a value of type `T` by running the +Calling a `OncePerProcess` object returns a value of type `T` by running the function `initializer` exactly once per process. All concurrent and future calls in the same process will return exactly the same value. This is useful in code that will be precompiled, as it allows setting up caches or other state @@ -591,13 +595,14 @@ which won't get serialized. ## Example ```jldoctest -julia> const global_state = Base.PerProcess{Vector{UInt32}}() do +julia> const global_state = Base.OncePerProcess{Vector{UInt32}}() do println("Making lazy global value...done.") return [Libc.rand()] end; -julia> procstate = global_state(); +julia> (procstate = global_state()) |> typeof Making lazy global value...done. +Vector{UInt32} (alias for Array{UInt32, 1}) julia> procstate === global_state() true @@ -606,51 +611,51 @@ julia> procstate === fetch(@async global_state()) true ``` """ -mutable struct PerProcess{T, F} - x::Union{Nothing,T} +mutable struct OncePerProcess{T, F} + value::Union{Nothing,T} @atomic state::UInt8 # 0=initial, 1=hasrun, 2=error @atomic allow_compile_time::Bool const initializer::F const lock::ReentrantLock - function PerProcess{T,F}(initializer::F) where {T, F} - once = new{T,F}(nothing, 0x00, true, initializer, ReentrantLock()) + function OncePerProcess{T,F}(initializer::F) where {T, F} + once = new{T,F}(nothing, PerStateInitial, true, initializer, ReentrantLock()) ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), - once, :x, nothing) + once, :value, nothing) ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), - once, :state, 0x00) + once, :state, PerStateInitial) return once end end -PerProcess{T}(initializer::F) where {T, F} = PerProcess{T, F}(initializer) -PerProcess(initializer) = PerProcess{Base.promote_op(initializer), typeof(initializer)}(initializer) -@inline function (once::PerProcess{T})() where T +OncePerProcess{T}(initializer::F) where {T, F} = OncePerProcess{T, F}(initializer) +OncePerProcess(initializer) = OncePerProcess{Base.promote_op(initializer), typeof(initializer)}(initializer) +@inline function (once::OncePerProcess{T})() where T state = (@atomic :acquire once.state) - if state != 0x01 + if state != PerStateHasrun (@noinline function init_perprocesss(once, state) - state == 0x02 && error("PerProcess initializer failed previously") + state == PerStateErrored && error("OncePerProcess initializer failed previously") once.allow_compile_time || __precompile__(false) lock(once.lock) try state = @atomic :monotonic once.state - if state == 0x00 - once.x = once.initializer() - elseif state == 0x02 - error("PerProcess initializer failed previously") - elseif state != 0x01 - error("invalid state for PerProcess") + if state == PerStateInitial + once.value = once.initializer() + elseif state == PerStateErrored + error("OncePerProcess initializer failed previously") + elseif state != PerStateHasrun + error("invalid state for OncePerProcess") end catch - state == 0x02 || @atomic :release once.state = 0x02 + state == PerStateErrored || @atomic :release once.state = PerStateErrored unlock(once.lock) rethrow() end - state == 0x01 || @atomic :release once.state = 0x01 + state == PerStateHasrun || @atomic :release once.state = PerStateHasrun unlock(once.lock) nothing end)(once, state) end - return once.x::T + return once.value::T end function copyto_monotonic!(dest::AtomicMemory, src) @@ -659,7 +664,7 @@ function copyto_monotonic!(dest::AtomicMemory, src) if isassigned(src, j) @atomic :monotonic dest[i] = src[j] #else - # _unsafeindex_atomic!(dest, i, src[j], :monotonic) + # _unsetindex_atomic!(dest, i, src[j], :monotonic) end i += 1 end @@ -674,12 +679,12 @@ function fill_monotonic!(dest::AtomicMemory, x) end -# share a lock, since we just need it briefly, so some contention is okay +# share a lock/condition, since we just need it briefly, so some contention is okay const PerThreadLock = ThreadSynchronizer() """ - PerThread{T} + OncePerThread{T}(init::Function)() -> T -Calling a `PerThread` object returns a value of type `T` by running the function +Calling a `OncePerThread` object returns a value of type `T` by running the function `initializer` exactly once per thread. All future calls in the same thread, and concurrent or future calls with the same thread id, will return exactly the same value. The object can also be indexed by the threadid for any existing @@ -687,23 +692,25 @@ thread, to get (or initialize *on this thread*) the value stored for that thread. Incorrect usage can lead to data-races or memory corruption so use only if that behavior is correct within your library's threading-safety design. -Warning: it is not necessarily true that a Task only runs on one thread, therefore the value -returned here may alias other values or change in the middle of your program. This type may -get deprecated in the future. If initializer yields, the thread running the current task -after the call might not be the same as the one at the start of the call. +!!! warning + It is not necessarily true that a Task only runs on one thread, therefore the value + returned here may alias other values or change in the middle of your program. This function + may get deprecated in the future. If initializer yields, the thread running the current + task after the call might not be the same as the one at the start of the call. -See also: [`PerTask`](@ref). +See also: [`OncePerTask`](@ref). ## Example ```jldoctest -julia> const thread_state = Base.PerThread{Vector{UInt32}}() do +julia> const thread_state = Base.OncePerThread{Vector{UInt32}}() do println("Making lazy thread value...done.") return [Libc.rand()] end; -julia> threadvec = thread_state(); +julia> (threadvec = thread_state()) |> typeof Making lazy thread value...done. +Vector{UInt32} (alias for Array{UInt32, 1}) julia> threadvec === fetch(@async thread_state()) true @@ -712,12 +719,12 @@ julia> threadvec === thread_state[Threads.threadid()] true ``` """ -mutable struct PerThread{T, F} +mutable struct OncePerThread{T, F} @atomic xs::AtomicMemory{T} # values @atomic ss::AtomicMemory{UInt8} # states: 0=initial, 1=hasrun, 2=error, 3==concurrent const initializer::F - function PerThread{T,F}(initializer::F) where {T, F} + function OncePerThread{T,F}(initializer::F) where {T, F} xs, ss = AtomicMemory{T}(), AtomicMemory{UInt8}() once = new{T,F}(xs, ss, initializer) ccall(:jl_set_precompile_field_replace, Cvoid, (Any, Any, Any), @@ -727,29 +734,30 @@ mutable struct PerThread{T, F} return once end end -PerThread{T}(initializer::F) where {T, F} = PerThread{T,F}(initializer) -PerThread(initializer) = PerThread{Base.promote_op(initializer), typeof(initializer)}(initializer) -@inline function getindex(once::PerThread, tid::Integer) +OncePerThread{T}(initializer::F) where {T, F} = OncePerThread{T,F}(initializer) +OncePerThread(initializer) = OncePerThread{Base.promote_op(initializer), typeof(initializer)}(initializer) +@inline (once::OncePerThread)() = once[Threads.threadid()] +@inline function getindex(once::OncePerThread, tid::Integer) tid = Int(tid) ss = @atomic :acquire once.ss xs = @atomic :monotonic once.xs # n.b. length(xs) >= length(ss) - if tid > length(ss) || (@atomic :acquire ss[tid]) != 0x01 + if tid <= 0 || tid > length(ss) || (@atomic :acquire ss[tid]) != PerStateHasrun (@noinline function init_perthread(once, tid) - local xs = @atomic :acquire once.xs - local ss = @atomic :monotonic once.ss + local ss = @atomic :acquire once.ss + local xs = @atomic :monotonic once.xs local len = length(ss) # slow path to allocate it nt = Threads.maxthreadid() - 0 < tid <= nt || ArgumentError("thread id outside of allocated range") - if tid <= length(ss) && (@atomic :acquire ss[tid]) == 0x02 - error("PerThread initializer failed previously") + 0 < tid <= nt || throw(ArgumentError("thread id outside of allocated range")) + if tid <= length(ss) && (@atomic :acquire ss[tid]) == PerStateErrored + error("OncePerThread initializer failed previously") end newxs = xs newss = ss if tid > len # attempt to do all allocations outside of PerThreadLock for better scaling - @assert length(xs) == length(ss) "logical constraint violation" + @assert length(xs) >= length(ss) "logical constraint violation" newxs = typeof(xs)(undef, len + nt) newss = typeof(ss)(undef, len + nt) end @@ -759,30 +767,30 @@ PerThread(initializer) = PerThread{Base.promote_op(initializer), typeof(initiali ss = @atomic :monotonic once.ss xs = @atomic :monotonic once.xs if tid > length(ss) - @assert length(ss) >= len && newxs !== xs && newss != ss "logical constraint violation" - fill_monotonic!(newss, 0x00) + @assert len <= length(ss) <= length(newss) "logical constraint violation" + fill_monotonic!(newss, PerStateInitial) xs = copyto_monotonic!(newxs, xs) ss = copyto_monotonic!(newss, ss) @atomic :release once.xs = xs @atomic :release once.ss = ss end state = @atomic :monotonic ss[tid] - while state == 0x04 + while state == PerStateConcurrent # lost race, wait for notification this is done running elsewhere wait(PerThreadLock) # wait for initializer to finish without releasing this thread ss = @atomic :monotonic once.ss - state = @atomic :monotonic ss[tid] == 0x04 + state = @atomic :monotonic ss[tid] end - if state == 0x00 + if state == PerStateInitial # won the race, drop lock in exchange for state, and run user initializer - @atomic :monotonic ss[tid] = 0x04 + @atomic :monotonic ss[tid] = PerStateConcurrent result = try unlock(PerThreadLock) once.initializer() catch lock(PerThreadLock) ss = @atomic :monotonic once.ss - @atomic :release ss[tid] = 0x02 + @atomic :release ss[tid] = PerStateErrored notify(PerThreadLock) rethrow() end @@ -791,12 +799,12 @@ PerThread(initializer) = PerThread{Base.promote_op(initializer), typeof(initiali xs = @atomic :monotonic once.xs @atomic :release xs[tid] = result ss = @atomic :monotonic once.ss - @atomic :release ss[tid] = 0x01 + @atomic :release ss[tid] = PerStateHasrun notify(PerThreadLock) - elseif state == 0x02 - error("PerThread initializer failed previously") - elseif state != 0x01 - error("invalid state for PerThread") + elseif state == PerStateErrored + error("OncePerThread initializer failed previously") + elseif state != PerStateHasrun + error("invalid state for OncePerThread") end finally unlock(PerThreadLock) @@ -807,12 +815,11 @@ PerThread(initializer) = PerThread{Base.promote_op(initializer), typeof(initiali end return xs[tid] end -@inline (once::PerThread)() = once[Threads.threadid()] """ - PerTask{T} + OncePerTask{T}(init::Function)() -> T -Calling a `PerTask` object returns a value of type `T` by running the function `initializer` +Calling a `OncePerTask` object returns a value of type `T` by running the function `initializer` exactly once per Task. All future calls in the same Task will return exactly the same value. See also: [`task_local_storage`](@ref). @@ -820,13 +827,14 @@ See also: [`task_local_storage`](@ref). ## Example ```jldoctest -julia> const task_state = Base.PerTask{Vector{UInt32}}() do +julia> const task_state = Base.OncePerTask{Vector{UInt32}}() do println("Making lazy task value...done.") return [Libc.rand()] end; -julia> taskvec = task_state(); +julia> (taskvec = task_state()) |> typeof Making lazy task value...done. +Vector{UInt32} (alias for Array{UInt32, 1}) julia> taskvec === task_state() true @@ -836,11 +844,11 @@ Making lazy task value...done. false ``` """ -mutable struct PerTask{T, F} +mutable struct OncePerTask{T, F} const initializer::F - PerTask{T}(initializer::F) where {T, F} = new{T,F}(initializer) - PerTask{T,F}(initializer::F) where {T, F} = new{T,F}(initializer) - PerTask(initializer) = new{Base.promote_op(initializer), typeof(initializer)}(initializer) + OncePerTask{T}(initializer::F) where {T, F} = new{T,F}(initializer) + OncePerTask{T,F}(initializer::F) where {T, F} = new{T,F}(initializer) + OncePerTask(initializer) = new{Base.promote_op(initializer), typeof(initializer)}(initializer) end -@inline (once::PerTask)() = get!(once.initializer, task_local_storage(), once) +@inline (once::OncePerTask)() = get!(once.initializer, task_local_storage(), once) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index b11e985782709..7181965d9aa81 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -34,9 +34,9 @@ Main.include Base.include_string Base.include_dependency __init__ -Base.PerProcess -Base.PerTask -Base.PerThread +Base.OncePerProcess +Base.OncePerTask +Base.OncePerThread Base.which(::Any, ::Any) Base.methods Base.@show diff --git a/test/precompile.jl b/test/precompile.jl index e44771fb6a86f..adf10363298ba 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -96,7 +96,7 @@ precompile_test_harness(false) do dir struct GAPGroupHomomorphism{A, B} <: AbstractAlgebraMap{GAPGroupHomomorphism{B, A}} end global process_state_calls::Int = 0 - const process_state = Base.PerProcess{typeof(getpid())}() do + const process_state = Base.OncePerProcess{typeof(getpid())}() do @assert (global process_state_calls += 1) == 1 return getpid() end diff --git a/test/threads.jl b/test/threads.jl index d8e9fd4ce2901..2dbdcf99eae28 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -375,41 +375,63 @@ end end end -let once = PerProcess(() -> return [nothing]) - @test typeof(once) <: PerProcess{Vector{Nothing}} +let once = OncePerProcess(() -> return [nothing]) + @test typeof(once) <: OncePerProcess{Vector{Nothing}} x = once() @test x === once() @atomic once.state = 0xff - @test_throws ErrorException("invalid state for PerProcess") once() - @test_throws ErrorException("PerProcess initializer failed previously") once() + @test_throws ErrorException("invalid state for OncePerProcess") once() + @test_throws ErrorException("OncePerProcess initializer failed previously") once() @atomic once.state = 0x01 @test x === once() end -let once = PerProcess{Int}(() -> error("expected")) +let once = OncePerProcess{Int}(() -> error("expected")) @test_throws ErrorException("expected") once() - @test_throws ErrorException("PerProcess initializer failed previously") once() + @test_throws ErrorException("OncePerProcess initializer failed previously") once() end let e = Base.Event(true), started = Channel{Int16}(Inf), - once = PerThread() do + finish = Channel{Nothing}(Inf), + exiting = Channel{Nothing}(Inf), + starttest2 = Event(), + once = OncePerThread() do push!(started, threadid()) - wait(e) + take!(finish) + return [nothing] + end + alls = OncePerThread() do return [nothing] end - @test typeof(once) <: PerThread{Vector{Nothing}} - notify(e) + @test typeof(once) <: OncePerThread{Vector{Nothing}} + push!(finish, nothing) + @test_throws ArgumentError once[0] x = once() - @test x === once() === fetch(@async once()) + @test_throws ArgumentError once[0] + @test x === once() === fetch(@async once()) === once[threadid()] @test take!(started) == threadid() @test isempty(started) tids = zeros(UInt, 50) + newthreads = zeros(Int16, length(tids)) onces = Vector{Vector{Nothing}}(undef, length(tids)) + allonces = Vector{Vector{Vector{Nothing}}}(undef, length(tids)) for i = 1:length(tids) function cl() - local y = once() - onces[i] = y - @test x !== y === once() + GC.gc(false) # stress test the GC-safepoint mechanics of jl_adopt_thread + try + newthreads[i] = threadid() + local y = once() + onces[i] = y + @test x !== y === once() === once[threadid()] + wait(starttest2) + allonces[i] = Vector{Nothing}[alls[tid] for tid in newthreads] + catch ex + close(started, ErrorException("failed")) + close(finish, ErrorException("failed")) + @lock stderr Base.display_error(current_exceptions()) + end + push!(exiting, nothing) + GC.gc(false) # stress test the GC-safepoint mechanics of jl_delete_thread nothing end function threadcallclosure(cl::F) where {F} # create sparam so we can reference the type of cl in the ccall type @@ -429,34 +451,50 @@ let e = Base.Event(true), err == 0 || Base.uv_error("uv_thread_join", err) end end - # let them finish in 5 batches of 10 - for i = 1:length(tids) ÷ 10 - for i = 1:10 - @test take!(started) != threadid() + try + # let them finish in batches of 10 + for i = 1:length(tids) ÷ 10 + for i = 1:10 + newid = take!(started) + @test newid != threadid() + end + for i = 1:10 + push!(finish, nothing) + end end - for i = 1:10 - notify(e) + @test isempty(started) + # now run the second part of the test where they all try to access the other threads elements + notify(starttest2) + finally + for _ = 1:length(tids) + # run IO loop until all threads are close to exiting + take!(exiting) end + waitallthreads(tids) end @test isempty(started) - waitallthreads(tids) - @test isempty(started) + @test isempty(finish) @test length(IdSet{eltype(onces)}(onces)) == length(onces) # make sure every object is unique + allexpected = Vector{Nothing}[alls[tid] for tid in newthreads] + @test length(IdSet{eltype(allexpected)}(allexpected)) == length(allexpected) # make sure every object is unique + @test all(i -> allonces[i] !== allexpected && all(j -> allonces[i][j] === allexpected[j], eachindex(allexpected)), eachindex(allonces)) # make sure every thread saw the same elements + @test_throws ArgumentError once[Threads.maxthreadid() + 1] + @test_throws ArgumentError once[-1] end -let once = PerThread{Int}(() -> error("expected")) +let once = OncePerThread{Int}(() -> error("expected")) @test_throws ErrorException("expected") once() - @test_throws ErrorException("PerThread initializer failed previously") once() + @test_throws ErrorException("OncePerThread initializer failed previously") once() end -let once = PerTask(() -> return [nothing]) - @test typeof(once) <: PerTask{Vector{Nothing}} +let once = OncePerTask(() -> return [nothing]) + @test typeof(once) <: OncePerTask{Vector{Nothing}} x = once() @test x === once() !== fetch(@async once()) delete!(task_local_storage(), once) @test x !== once() === once() end -let once = PerTask{Int}(() -> error("expected")) +let once = OncePerTask{Int}(() -> error("expected")) @test_throws ErrorException("expected") once() @test_throws ErrorException("expected") once() end From 9d56856c74cd83bbf3258d14a096de208d89ee5e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 15 Oct 2024 17:35:17 +0000 Subject: [PATCH 400/548] fix use-after-free in test (detected in win32 CI) --- test/threads.jl | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/test/threads.jl b/test/threads.jl index 2dbdcf99eae28..d5a801c1a6a1c 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -415,8 +415,8 @@ let e = Base.Event(true), newthreads = zeros(Int16, length(tids)) onces = Vector{Vector{Nothing}}(undef, length(tids)) allonces = Vector{Vector{Vector{Nothing}}}(undef, length(tids)) - for i = 1:length(tids) - function cl() + # allocate closure memory to last until all threads are started + cls = [function cl() GC.gc(false) # stress test the GC-safepoint mechanics of jl_adopt_thread try newthreads[i] = threadid() @@ -434,43 +434,50 @@ let e = Base.Event(true), GC.gc(false) # stress test the GC-safepoint mechanics of jl_delete_thread nothing end - function threadcallclosure(cl::F) where {F} # create sparam so we can reference the type of cl in the ccall type - threadwork = @cfunction cl -> cl() Cvoid (Ref{F},) # create a cfunction that specializes on cl as an argument and calls it - err = @ccall uv_thread_create(Ref(tids, i)::Ptr{UInt}, threadwork::Ptr{Cvoid}, cl::Ref{F})::Cint # call that on a thread - err == 0 || Base.uv_error("uv_thread_create", err) - end - threadcallclosure(cl) - end - @noinline function waitallthreads(tids) + for i = 1:length(tids)] + GC.@preserve cls begin # this memory must survive until each corresponding thread exits (waitallthreads / uv_thread_join) + Base.preserve_handle(cls) for i = 1:length(tids) - tid = Ref(tids, i) - tidp = Base.unsafe_convert(Ptr{UInt}, tid)::Ptr{UInt} - gc_state = @ccall jl_gc_safe_enter()::Int8 - GC.@preserve tid err = @ccall uv_thread_join(tidp::Ptr{UInt})::Cint - @ccall jl_gc_safe_leave(gc_state::Int8)::Cvoid - err == 0 || Base.uv_error("uv_thread_join", err) - end - end - try - # let them finish in batches of 10 - for i = 1:length(tids) ÷ 10 - for i = 1:10 - newid = take!(started) - @test newid != threadid() + function threadcallclosure(tid::Ref{UInt}, cl::Ref{F}) where {F} # create sparam so we can reference the type of cl in the ccall type + threadwork = @cfunction cl -> cl() Cvoid (Ref{F},) # create a cfunction that specializes on cl as an argument and calls it + err = @ccall uv_thread_create(tid::Ptr{UInt}, threadwork::Ptr{Cvoid}, cl::Ref{F})::Cint # call that on a thread + err == 0 || Base.uv_error("uv_thread_create", err) + nothing end - for i = 1:10 - push!(finish, nothing) + threadcallclosure(Ref(tids, i), Ref(cls, i)) + end + @noinline function waitallthreads(tids, cls) + for i = 1:length(tids) + tid = Ref(tids, i) + tidp = Base.unsafe_convert(Ptr{UInt}, tid)::Ptr{UInt} + gc_state = @ccall jl_gc_safe_enter()::Int8 + GC.@preserve tid err = @ccall uv_thread_join(tidp::Ptr{UInt})::Cint + @ccall jl_gc_safe_leave(gc_state::Int8)::Cvoid + err == 0 || Base.uv_error("uv_thread_join", err) end + Base.unpreserve_handle(cls) end - @test isempty(started) - # now run the second part of the test where they all try to access the other threads elements - notify(starttest2) - finally - for _ = 1:length(tids) - # run IO loop until all threads are close to exiting - take!(exiting) + try + # let them finish in batches of 10 + for i = 1:length(tids) ÷ 10 + for i = 1:10 + newid = take!(started) + @test newid != threadid() + end + for i = 1:10 + push!(finish, nothing) + end + end + @test isempty(started) + # now run the second part of the test where they all try to access the other threads elements + notify(starttest2) + finally + for _ = 1:length(tids) + # run IO loop until all threads are close to exiting + take!(exiting) + end + waitallthreads(tids, cls) end - waitallthreads(tids) end @test isempty(started) @test isempty(finish) From b02d6715d3be345962a312d6080bed7c732a4300 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 15 Oct 2024 15:39:05 -0400 Subject: [PATCH 401/548] Make loading work when stdlib deps are missing in the manifest (#56148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/JuliaLang/julia/issues/56109 Simulating a bad manifest by having `LibGit2_jll` missing as a dep of `LibGit2` in my default env, say because the manifest was generated by a different julia version or different master julia commit. ## This PR, it just works ``` julia> using Revise julia> ``` i.e. ``` % JULIA_DEBUG=loading ./julia --startup-file=no julia> using Revise ... ┌ Debug: Stdlib LibGit2 [76f85450-5226-5b5a-8eaa-529ad045b433] is trying to load `LibGit2_jll` │ which is not listed as a dep in the load path manifests, so resorting to search │ in the stdlib Project.tomls for true deps └ @ Base loading.jl:387 ┌ Debug: LibGit2 [76f85450-5226-5b5a-8eaa-529ad045b433] indeed depends on LibGit2_jll in project /Users/ian/Documents/GitHub/julia/usr/share/julia/stdlib/v1.12/LibGit2/Project.toml └ @ Base loading.jl:395 ... julia> ``` ## Master ``` julia> using Revise Info Given Revise was explicitly requested, output will be shown live ERROR: LoadError: ArgumentError: Package LibGit2 does not have LibGit2_jll in its dependencies: - 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: /Users/ian/.julia/environments/v1.12/Manifest.toml - You may have a partially installed environment. Try `Pkg.instantiate()` to ensure all packages in the environment are installed. - Or, if you have LibGit2 checked out for development and have added LibGit2_jll as a dependency but haven't updated your primary environment's manifest file, try `Pkg.resolve()`. - Otherwise you may need to report an issue with LibGit2 ... ``` --- base/loading.jl | 37 ++++++++++++++ test/loading.jl | 12 +++++ test/project/deps/BadStdlibDeps/Manifest.toml | 51 +++++++++++++++++++ test/project/deps/BadStdlibDeps/Project.toml | 2 + 4 files changed, 102 insertions(+) create mode 100644 test/project/deps/BadStdlibDeps/Manifest.toml create mode 100644 test/project/deps/BadStdlibDeps/Project.toml diff --git a/base/loading.jl b/base/loading.jl index fe4a4770628da..8dff6838c27cc 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -308,6 +308,21 @@ function find_package(arg) # ::Union{Nothing,String} return locate_package(pkg, env) end +# is there a better/faster ground truth? +function is_stdlib(pkgid::PkgId) + pkgid.name in readdir(Sys.STDLIB) || return false + stdlib_root = joinpath(Sys.STDLIB, pkgid.name) + project_file = locate_project_file(stdlib_root) + if project_file isa String + d = parsed_toml(project_file) + uuid = get(d, "uuid", nothing) + if uuid !== nothing + return UUID(uuid) == pkgid.uuid + end + end + return false +end + """ Base.identify_package_env(name::String)::Union{Tuple{PkgId, String}, Nothing} Base.identify_package_env(where::Union{Module,PkgId}, name::String)::Union{Tuple{PkgId, Union{String, Nothing}}, Nothing} @@ -336,6 +351,12 @@ function identify_package_env(where::PkgId, name::String) end break # found in implicit environment--return "not found" end + if pkg_env === nothing && is_stdlib(where) + # if not found it could be that manifests are from a different julia version/commit + # where stdlib dependencies have changed, so look up deps based on the stdlib Project.toml + # as a fallback + pkg_env = identify_stdlib_project_dep(where, name) + end end if cache !== nothing cache.identified_where[(where, name)] = pkg_env @@ -362,6 +383,22 @@ function identify_package_env(name::String) return pkg_env end +function identify_stdlib_project_dep(stdlib::PkgId, depname::String) + @debug """ + Stdlib $(repr("text/plain", stdlib)) is trying to load `$depname` + which is not listed as a dep in the load path manifests, so resorting to search + in the stdlib Project.tomls for true deps""" + stdlib_projfile = locate_project_file(joinpath(Sys.STDLIB, stdlib.name)) + stdlib_projfile === nothing && return nothing + found = explicit_project_deps_get(stdlib_projfile, depname) + if found !== nothing + @debug "$(repr("text/plain", stdlib)) indeed depends on $depname in project $stdlib_projfile" + pkgid = PkgId(found, depname) + return pkgid, stdlib_projfile + end + return nothing +end + _nothing_or_first(x) = x === nothing ? nothing : first(x) """ diff --git a/test/loading.jl b/test/loading.jl index 1674a9f59a0c3..4877b256a6ad9 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1341,6 +1341,18 @@ end end end +@testset "Fallback for stdlib deps if manifest deps aren't found" begin + mktempdir() do depot + # This manifest has a LibGit2 entry that is missing LibGit2_jll, which should be + # handled by falling back to the stdlib Project.toml for dependency truth. + badmanifest_test_dir = joinpath(@__DIR__, "project", "deps", "BadStdlibDeps.jl") + @test success(addenv( + `$(Base.julia_cmd()) --project=$badmanifest_test_dir --startup-file=no -e 'using LibGit2'`, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + )) + end +end + @testset "code coverage disabled during precompilation" begin mktempdir() do depot cov_test_dir = joinpath(@__DIR__, "project", "deps", "CovTest.jl") diff --git a/test/project/deps/BadStdlibDeps/Manifest.toml b/test/project/deps/BadStdlibDeps/Manifest.toml new file mode 100644 index 0000000000000..32aaa0b83dc0a --- /dev/null +++ b/test/project/deps/BadStdlibDeps/Manifest.toml @@ -0,0 +1,51 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.12.0-DEV" +manifest_format = "2.0" +project_hash = "dc9d33b0ee13d9466bdb75b8d375808a534a79ec" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +# This is intentionally missing LibGit2_jll for testing purposes +[[deps.LibGit2]] +deps = ["NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.8.0+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.6+1" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" diff --git a/test/project/deps/BadStdlibDeps/Project.toml b/test/project/deps/BadStdlibDeps/Project.toml new file mode 100644 index 0000000000000..223889185ea15 --- /dev/null +++ b/test/project/deps/BadStdlibDeps/Project.toml @@ -0,0 +1,2 @@ +[deps] +LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" From 924dc170ce12ce831bd31656cb08e79fb2920617 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 15 Oct 2024 17:41:31 -0300 Subject: [PATCH 402/548] Remove llvm-muladd pass and move it's functionality to to llvm-simdloop (#55802) Closes https://github.com/JuliaLang/julia/issues/55785 I'm not sure if we want to backport this like this. Because that removes some functionality (the pass itself). So LLVM.jl and friends might need annoying version code. We can maybe keep the code there and just not run the pass in a backport. --- doc/src/devdocs/llvm-passes.md | 12 --- doc/src/devdocs/llvm.md | 1 - src/Makefile | 2 +- src/jl_exported_funcs.inc | 1 - src/llvm-julia-passes.inc | 1 - src/llvm-muladd.cpp | 117 ------------------------------ src/llvm-simdloop.cpp | 66 +++++++++++++++++ src/passes.h | 4 - src/pipeline.cpp | 1 - test/llvmpasses/julia-simdloop.ll | 69 +++++++++++++++--- test/llvmpasses/muladd.ll | 62 ---------------- test/llvmpasses/parsing.ll | 2 +- 12 files changed, 125 insertions(+), 213 deletions(-) delete mode 100644 src/llvm-muladd.cpp delete mode 100644 test/llvmpasses/muladd.ll diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 36383acaef512..736faf54c219b 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -114,18 +114,6 @@ This pass is used to verify Julia's invariants about LLVM IR. This includes thin These passes are used to perform transformations on LLVM IR that LLVM will not perform itself, e.g. fast math flag propagation, escape analysis, and optimizations on Julia-specific internal functions. They use knowledge about Julia's semantics to perform these optimizations. -### CombineMulAdd - -* Filename: `llvm-muladd.cpp` -* Class Name: `CombineMulAddPass` -* Opt Name: `function(CombineMulAdd)` - -This pass serves to optimize the particular combination of a regular `fmul` with a fast `fadd` into a contract `fmul` with a fast `fadd`. This is later optimized by the backend to a [fused multiply-add](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation#Fused_multiply%E2%80%93add) instruction, which can provide significantly faster operations at the cost of more [unpredictable semantics](https://simonbyrne.github.io/notes/fastmath/). - -!!! note - - This optimization only occurs when the `fmul` has a single use, which is the fast `fadd`. - ### AllocOpt * Filename: `llvm-alloc-opt.cpp` diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index c05a4f9dc4e7f..2155e5da6fd7b 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -30,7 +30,6 @@ The code for lowering Julia AST to LLVM IR or interpreting it directly is in dir | `llvm-julia-licm.cpp` | Custom LLVM pass to hoist/sink Julia-specific intrinsics | | `llvm-late-gc-lowering.cpp` | Custom LLVM pass to root GC-tracked values | | `llvm-lower-handlers.cpp` | Custom LLVM pass to lower try-catch blocks | -| `llvm-muladd.cpp` | Custom LLVM pass for fast-match FMA | | `llvm-multiversioning.cpp` | Custom LLVM pass to generate sysimg code on multiple architectures | | `llvm-propagate-addrspaces.cpp` | Custom LLVM pass to canonicalize addrspaces | | `llvm-ptls.cpp` | Custom LLVM pass to lower TLS operations | diff --git a/src/Makefile b/src/Makefile index a6b1f433b73ce..4dbb094c65321 100644 --- a/src/Makefile +++ b/src/Makefile @@ -52,7 +52,7 @@ RT_LLVMLINK := CG_LLVMLINK := ifeq ($(JULIACODEGEN),LLVM) -CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop llvm-muladd \ +CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \ llvm-final-gc-lowering llvm-pass-helpers llvm-late-gc-lowering llvm-ptls \ llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces \ llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \ diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index a00a0171d23b7..f712f154ed896 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -554,7 +554,6 @@ YY(LLVMExtraMPMAddRemoveAddrspacesPass) \ YY(LLVMExtraMPMAddLowerPTLSPass) \ YY(LLVMExtraFPMAddDemoteFloat16Pass) \ - YY(LLVMExtraFPMAddCombineMulAddPass) \ YY(LLVMExtraFPMAddLateLowerGCPass) \ YY(LLVMExtraFPMAddAllocOptPass) \ YY(LLVMExtraFPMAddPropagateJuliaAddrspacesPass) \ diff --git a/src/llvm-julia-passes.inc b/src/llvm-julia-passes.inc index bd89c01c6fdfe..c41ecbba87b6a 100644 --- a/src/llvm-julia-passes.inc +++ b/src/llvm-julia-passes.inc @@ -11,7 +11,6 @@ MODULE_PASS("LowerPTLSPass", LowerPTLSPass, LowerPTLSPass()) //Function passes #ifdef FUNCTION_PASS FUNCTION_PASS("DemoteFloat16", DemoteFloat16Pass, DemoteFloat16Pass()) -FUNCTION_PASS("CombineMulAdd", CombineMulAddPass, CombineMulAddPass()) FUNCTION_PASS("LateLowerGCFrame", LateLowerGCPass, LateLowerGCPass()) FUNCTION_PASS("AllocOpt", AllocOptPass, AllocOptPass()) FUNCTION_PASS("PropagateJuliaAddrspaces", PropagateJuliaAddrspacesPass, PropagateJuliaAddrspacesPass()) diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp deleted file mode 100644 index 12f1c8ad765d9..0000000000000 --- a/src/llvm-muladd.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// This file is a part of Julia. License is MIT: https://julialang.org/license - -#include "llvm-version.h" -#include "passes.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "julia.h" -#include "julia_assert.h" - -#define DEBUG_TYPE "combine-muladd" -#undef DEBUG - -using namespace llvm; -STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); - -#ifndef __clang_gcanalyzer__ -#define REMARK(remark) ORE.emit(remark) -#else -#define REMARK(remark) (void) 0; -#endif - -/** - * Combine - * ``` - * %v0 = fmul ... %a, %b - * %v = fadd contract ... %v0, %c - * ``` - * to - * `%v = call contract @llvm.fmuladd.<...>(... %a, ... %b, ... %c)` - * when `%v0` has no other use - */ - -// Return true if we changed the mulOp -static bool checkCombine(Value *maybeMul, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT -{ - auto mulOp = dyn_cast(maybeMul); - if (!mulOp || mulOp->getOpcode() != Instruction::FMul) - return false; - if (!mulOp->hasOneUse()) { - LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) - << "fmul had multiple uses " << ore::NV("fmul", mulOp); - }); - return false; - } - // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. - auto fmf = mulOp->getFastMathFlags(); - if (!fmf.allowContract()) { - LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) - << "marked for fma " << ore::NV("fmul", mulOp); - }); - ++TotalContracted; - fmf.setAllowContract(true); - mulOp->copyFastMathFlags(fmf); - return true; - } - return false; -} - -static bool combineMulAdd(Function &F) JL_NOTSAFEPOINT -{ - OptimizationRemarkEmitter ORE(&F); - bool modified = false; - for (auto &BB: F) { - for (auto it = BB.begin(); it != BB.end();) { - auto &I = *it; - it++; - switch (I.getOpcode()) { - case Instruction::FAdd: { - if (!I.hasAllowContract()) - continue; - modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); - break; - } - case Instruction::FSub: { - if (!I.hasAllowContract()) - continue; - modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); - break; - } - default: - break; - } - } - } -#ifdef JL_VERIFY_PASSES - assert(!verifyLLVMIR(F)); -#endif - return modified; -} - -PreservedAnalyses CombineMulAddPass::run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT -{ - if (combineMulAdd(F)) { - return PreservedAnalyses::allInSet(); - } - return PreservedAnalyses::all(); -} diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index ed2a04e650f2a..e12b30e3db466 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -41,6 +41,7 @@ STATISTIC(ReductionChainLength, "Total sum of instructions folded from reduction STATISTIC(MaxChainLength, "Max length of reduction chain"); STATISTIC(AddChains, "Addition reduction chains"); STATISTIC(MulChains, "Multiply reduction chains"); +STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); #ifndef __clang_gcanalyzer__ #define REMARK(remark) ORE.emit(remark) @@ -49,6 +50,49 @@ STATISTIC(MulChains, "Multiply reduction chains"); #endif namespace { +/** + * Combine + * ``` + * %v0 = fmul ... %a, %b + * %v = fadd contract ... %v0, %c + * ``` + * to + * %v0 = fmul contract ... %a, %b + * %v = fadd contract ... %v0, %c + * when `%v0` has no other use + */ + +static bool checkCombine(Value *maybeMul, Loop &L, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT +{ + auto mulOp = dyn_cast(maybeMul); + if (!mulOp || mulOp->getOpcode() != Instruction::FMul) + return false; + if (!L.contains(mulOp)) + return false; + if (!mulOp->hasOneUse()) { + LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) + << "fmul had multiple uses " << ore::NV("fmul", mulOp); + }); + return false; + } + // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. + auto fmf = mulOp->getFastMathFlags(); + if (!fmf.allowContract()) { + LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) + << "marked for fma " << ore::NV("fmul", mulOp); + }); + ++TotalContracted; + fmf.setAllowContract(true); + mulOp->copyFastMathFlags(fmf); + return true; + } + return false; +} + static unsigned getReduceOpcode(Instruction *J, Instruction *operand) JL_NOTSAFEPOINT { switch (J->getOpcode()) { @@ -150,6 +194,28 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop &L, OptimizationRe }); (*K)->setHasAllowReassoc(true); (*K)->setHasAllowContract(true); + switch ((*K)->getOpcode()) { + case Instruction::FAdd: { + if (!(*K)->hasAllowContract()) + continue; + // (*K)->getOperand(0)->print(dbgs()); + // (*K)->getOperand(1)->print(dbgs()); + checkCombine((*K)->getOperand(0), L, ORE); + checkCombine((*K)->getOperand(1), L, ORE); + break; + } + case Instruction::FSub: { + if (!(*K)->hasAllowContract()) + continue; + // (*K)->getOperand(0)->print(dbgs()); + // (*K)->getOperand(1)->print(dbgs()); + checkCombine((*K)->getOperand(0), L, ORE); + checkCombine((*K)->getOperand(1), L, ORE); + break; + } + default: + break; + } if (SE) SE->forgetValue(*K); ++length; diff --git a/src/passes.h b/src/passes.h index 6557a5813063d..4c9cba164d049 100644 --- a/src/passes.h +++ b/src/passes.h @@ -15,10 +15,6 @@ struct DemoteFloat16Pass : PassInfoMixin { static bool isRequired() { return true; } }; -struct CombineMulAddPass : PassInfoMixin { - PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; -}; - struct LateLowerGCPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; static bool isRequired() { return true; } diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 236be179e12c9..f300e4d7757b2 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -568,7 +568,6 @@ static void buildCleanupPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimi if (options.cleanup) { if (O.getSpeedupLevel() >= 2) { FunctionPassManager FPM; - JULIA_PASS(FPM.addPass(CombineMulAddPass())); FPM.addPass(DivRemPairsPass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } diff --git a/test/llvmpasses/julia-simdloop.ll b/test/llvmpasses/julia-simdloop.ll index a8d5ea3342b20..9a23a2826da70 100644 --- a/test/llvmpasses/julia-simdloop.ll +++ b/test/llvmpasses/julia-simdloop.ll @@ -3,18 +3,18 @@ ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='loop(LowerSIMDLoop)' -S %s | FileCheck %s ; CHECK-LABEL: @simd_test( -define void @simd_test(double *%a, double *%b) { +define void @simd_test(ptr %a, ptr %b) { top: br label %loop loop: %i = phi i64 [0, %top], [%nexti, %loop] - %aptr = getelementptr double, double *%a, i64 %i - %bptr = getelementptr double, double *%b, i64 %i + %aptr = getelementptr double, ptr %a, i64 %i + %bptr = getelementptr double, ptr %b, i64 %i ; CHECK: llvm.mem.parallel_loop_access - %aval = load double, double *%aptr - %bval = load double, double *%aptr + %aval = load double, ptr %aptr + %bval = load double, ptr %aptr %cval = fadd double %aval, %bval - store double %cval, double *%bptr + store double %cval, ptr %bptr %nexti = add i64 %i, 1 %done = icmp sgt i64 %nexti, 500 br i1 %done, label %loopdone, label %loop, !llvm.loop !1 @@ -23,15 +23,15 @@ loopdone: } ; CHECK-LABEL: @simd_test_sub( -define double @simd_test_sub(double *%a) { +define double @simd_test_sub(ptr %a) { top: br label %loop loop: %i = phi i64 [0, %top], [%nexti, %loop] %v = phi double [0.000000e+00, %top], [%nextv, %loop] - %aptr = getelementptr double, double *%a, i64 %i + %aptr = getelementptr double, ptr %a, i64 %i ; CHECK: llvm.mem.parallel_loop_access - %aval = load double, double *%aptr + %aval = load double, ptr %aptr %nextv = fsub double %v, %aval ; CHECK: fsub reassoc contract double %v, %aval %nexti = add i64 %i, 1 @@ -42,14 +42,14 @@ loopdone: } ; CHECK-LABEL: @simd_test_sub2( -define double @simd_test_sub2(double *%a) { +define double @simd_test_sub2(ptr %a) { top: br label %loop loop: %i = phi i64 [0, %top], [%nexti, %loop] %v = phi double [0.000000e+00, %top], [%nextv, %loop] - %aptr = getelementptr double, double *%a, i64 %i - %aval = load double, double *%aptr + %aptr = getelementptr double, ptr %a, i64 %i + %aval = load double, ptr %aptr %nextv = fsub double %v, %aval ; CHECK: fsub reassoc contract double %v, %aval %nexti = add i64 %i, 1 @@ -59,6 +59,26 @@ loopdone: ret double %nextv } +; CHECK-LABEL: @simd_test_sub4( +define double @simd_test_sub4(ptr %a) { +top: + br label %loop +loop: + %i = phi i64 [0, %top], [%nexti, %loop] + %v = phi double [0.000000e+00, %top], [%nextv, %loop] + %aptr = getelementptr double, double *%a, i64 %i + %aval = load double, double *%aptr + %nextv2 = fmul double %aval, %aval + ; CHECK: fmul contract double %aval, %aval + %nextv = fsub double %v, %nextv2 +; CHECK: fsub reassoc contract double %v, %nextv2 + %nexti = add i64 %i, 1 + %done = icmp sgt i64 %nexti, 500 + br i1 %done, label %loopdone, label %loop, !llvm.loop !0 +loopdone: + ret double %nextv +} + ; Tests if we correctly pass through other metadata ; CHECK-LABEL: @disabled( define i32 @disabled(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32 %N) { @@ -82,6 +102,31 @@ for.end: ; preds = %for.body ret i32 %1 } +; Check that we don't add contract to non loop things +; CHECK-LABEL: @dont_add_no_loop( +define double @dont_add_no_loop(ptr nocapture noundef nonnull readonly align 8 dereferenceable(72) %"a::Tuple", ptr nocapture noundef nonnull readonly align 8 dereferenceable(24) %"b::Tuple") #0 { +top: + %"a::Tuple[9]_ptr" = getelementptr inbounds i8, ptr %"a::Tuple", i64 64 + %"b::Tuple[3]_ptr" = getelementptr inbounds i8, ptr %"b::Tuple", i64 16 + %"a::Tuple[6]_ptr" = getelementptr inbounds i8, ptr %"a::Tuple", i64 40 + %"b::Tuple[2]_ptr" = getelementptr inbounds i8, ptr %"b::Tuple", i64 8 + %"a::Tuple[3]_ptr" = getelementptr inbounds i8, ptr %"a::Tuple", i64 16 + %"a::Tuple[3]_ptr.unbox" = load double, ptr %"a::Tuple[3]_ptr", align 8 + %"b::Tuple.unbox" = load double, ptr %"b::Tuple", align 8 + %0 = fmul double %"a::Tuple[3]_ptr.unbox", %"b::Tuple.unbox" +; CHECK: fmul double % + %"a::Tuple[6]_ptr.unbox" = load double, ptr %"a::Tuple[6]_ptr", align 8 + %"b::Tuple[2]_ptr.unbox" = load double, ptr %"b::Tuple[2]_ptr", align 8 + %1 = fmul contract double %"a::Tuple[6]_ptr.unbox", %"b::Tuple[2]_ptr.unbox" + %2 = fadd contract double %0, %1 + %"a::Tuple[9]_ptr.unbox" = load double, ptr %"a::Tuple[9]_ptr", align 8 + %"b::Tuple[3]_ptr.unbox" = load double, ptr %"b::Tuple[3]_ptr", align 8 + %3 = fmul contract double %"a::Tuple[9]_ptr.unbox", %"b::Tuple[3]_ptr.unbox" + %4 = fadd contract double %2, %3 + ret double %4 +} + + !0 = distinct !{!0, !"julia.simdloop"} !1 = distinct !{!1, !"julia.simdloop", !"julia.ivdep"} !2 = distinct !{!2, !"julia.simdloop", !"julia.ivdep", !3} diff --git a/test/llvmpasses/muladd.ll b/test/llvmpasses/muladd.ll deleted file mode 100644 index 079582305ee72..0000000000000 --- a/test/llvmpasses/muladd.ll +++ /dev/null @@ -1,62 +0,0 @@ -; This file is a part of Julia. License is MIT: https://julialang.org/license - -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='CombineMulAdd' -S %s | FileCheck %s - - -; CHECK-LABEL: @fast_muladd1 -define double @fast_muladd1(double %a, double %b, double %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul double %a, %b - %v2 = fadd fast double %v1, %c -; CHECK: ret double - ret double %v2 -} - -; CHECK-LABEL: @fast_mulsub1 -define double @fast_mulsub1(double %a, double %b, double %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul double %a, %b - %v2 = fsub fast double %v1, %c -; CHECK: ret double - ret double %v2 -} - -; CHECK-LABEL: @fast_mulsub_vec1 -define <2 x double> @fast_mulsub_vec1(<2 x double> %a, <2 x double> %b, <2 x double> %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul <2 x double> %a, %b - %v2 = fsub fast <2 x double> %c, %v1 -; CHECK: ret <2 x double> - ret <2 x double> %v2 -} - -; COM: Should not mark fmul as contract when multiple uses of fmul exist -; CHECK-LABEL: @slow_muladd1 -define double @slow_muladd1(double %a, double %b, double %c) { -top: -; CHECK: %v1 = fmul double %a, %b - %v1 = fmul double %a, %b -; CHECK: %v2 = fadd fast double %v1, %c - %v2 = fadd fast double %v1, %c -; CHECK: %v3 = fadd fast double %v1, %b - %v3 = fadd fast double %v1, %b -; CHECK: %v4 = fadd fast double %v3, %v2 - %v4 = fadd fast double %v3, %v2 -; CHECK: ret double %v4 - ret double %v4 -} - -; COM: Should not mark fadd->fadd fast as contract -; CHECK-LABEL: @slow_addadd1 -define double @slow_addadd1(double %a, double %b, double %c) { -top: -; CHECK: %v1 = fadd double %a, %b - %v1 = fadd double %a, %b -; CHECK: %v2 = fadd fast double %v1, %c - %v2 = fadd fast double %v1, %c -; CHECK: ret double %v2 - ret double %v2 -} diff --git a/test/llvmpasses/parsing.ll b/test/llvmpasses/parsing.ll index e0a726176b225..b8aec5ee2fa71 100644 --- a/test/llvmpasses/parsing.ll +++ b/test/llvmpasses/parsing.ll @@ -1,6 +1,6 @@ ; COM: NewPM-only test, tests for ability to parse Julia passes -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null From a7521ea86952168c2f342b4c2275340ea7726a94 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:22:33 -0400 Subject: [PATCH 403/548] Fix implicit `convert(String, ...)` in several places (#56174) This removes several `convert(String, ...)` from this code, which really shouldn't be something we invalidate on in the first place (see https://github.com/JuliaLang/julia/issues/56173) but this is still an improvement in code quality so let's take it. --- base/precompilation.jl | 21 +++++++++++++-------- base/regex.jl | 2 +- stdlib/REPL/src/LineEdit.jl | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 4b7da84a17d55..a39563178632f 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -43,12 +43,12 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Collect all direct dependencies of the project for key in ["deps", "weakdeps", "extras"] - for (name, _uuid::String) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} + for (name, _uuid) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} v = key == "deps" ? project_deps : key == "weakdeps" ? project_weakdeps : key == "extras" ? project_extras : error() - uuid = UUID(_uuid) + uuid = UUID(_uuid::String) v[name] = uuid names[UUID(uuid)] = name project_uuid_to_name[name] = UUID(uuid) @@ -75,9 +75,11 @@ function ExplicitEnv(envpath::String=Base.active_project()) project_extensions = Dict{String, Vector{UUID}}() # Collect all extensions of the project - for (name, triggers::Union{String, Vector{String}}) in get(Dict{String, Any}, project_d, "extensions")::Dict{String, Any} + for (name, triggers) in get(Dict{String, Any}, project_d, "extensions")::Dict{String, Any} if triggers isa String triggers = [triggers] + else + triggers = triggers::Vector{String} end uuids = UUID[] for trigger in triggers @@ -107,8 +109,9 @@ function ExplicitEnv(envpath::String=Base.active_project()) sizehint!(name_to_uuid, length(manifest_d)) sizehint!(lookup_strategy, length(manifest_d)) - for (name, pkg_infos::Vector{Any}) in get_deps(manifest_d) - for pkg_info::Dict{String, Any} in pkg_infos + for (name, pkg_infos) in get_deps(manifest_d) + for pkg_info in pkg_infos::Vector{Any} + pkg_info = pkg_info::Dict{String, Any} m_uuid = UUID(pkg_info["uuid"]::String) # If we have multiple packages with the same name we will overwrite things here @@ -129,8 +132,8 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Expanded format: else uuids = UUID[] - for (name_dep, _dep_uuid::String) in deps_pkg - dep_uuid = UUID(_dep_uuid) + for (name_dep, _dep_uuid) in deps_pkg + dep_uuid = UUID(_dep_uuid::String) push!(uuids, dep_uuid) names[dep_uuid] = name_dep end @@ -140,9 +143,11 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Extensions deps_pkg = get(Dict{String, Any}, pkg_info, "extensions")::Dict{String, Any} - for (ext, triggers::Union{String, Vector{String}}) in deps_pkg + for (ext, triggers) in deps_pkg if triggers isa String triggers = [triggers] + else + triggers = triggers::Vector{String} end deps_pkg[ext] = triggers end diff --git a/base/regex.jl b/base/regex.jl index 38eb4cc512552..9444c9a9fb63e 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -28,7 +28,7 @@ mutable struct Regex <: AbstractPattern function Regex(pattern::AbstractString, compile_options::Integer, match_options::Integer) - pattern = String(pattern) + pattern = String(pattern)::String compile_options = UInt32(compile_options) match_options = UInt32(match_options) if (compile_options & ~PCRE.COMPILE_MASK) != 0 diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index c92dca8c8e015..e881a65ca6b1c 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -166,7 +166,7 @@ region_active(s::PromptState) = s.region_active region_active(s::ModeState) = :off -input_string(s::PromptState) = String(take!(copy(s.input_buffer))) +input_string(s::PromptState) = String(take!(copy(s.input_buffer)))::String input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) function input_string_newlines_aftercursor(s::PromptState) From a9acdae61b1fbcd997d991ca6c157684cbde57a7 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 16 Oct 2024 08:23:21 +0800 Subject: [PATCH 404/548] Change annotations to use a NamedTuple (#55741) Due to popular demand, the type of annotations is to be changed from a `Tuple{UnitRange{Int}, Pair{Symbol, Any}}` to a `NamedTuple{(:region, :label, :value), Tuple{UnitRange{Int}, Symbol, Any}}`. This requires the expected code churn to `strings/annotated.jl`, and some changes to the StyledStrings and JuliaSyntaxHighlighting libraries. Closes #55249 and closes #55245. --- base/strings/annotated.jl | 226 ++++++++++-------- base/strings/io.jl | 4 +- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - doc/src/manual/strings.md | 4 +- stdlib/JuliaSyntaxHighlighting.version | 2 +- .../src/render/terminal/formatting.jl | 4 +- stdlib/StyledStrings.version | 2 +- test/strings/annotated.jl | 180 +++++++------- 15 files changed, 226 insertions(+), 204 deletions(-) create mode 100644 deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 delete mode 100644 deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 create mode 100644 deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 create mode 100644 deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 delete mode 100644 deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 delete mode 100644 deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index 9a0b4b2825436..c5c330fe0dfcd 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -1,5 +1,8 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +const Annotation = NamedTuple{(:label, :value), Tuple{Symbol, Any}} +const RegionAnnotation = NamedTuple{(:region, :label, :value), Tuple{UnitRange{Int}, Symbol, Any}} + """ AnnotatedString{S <: AbstractString} <: AbstractString @@ -20,7 +23,8 @@ annotated with labeled values. The above diagram represents a `AnnotatedString` where three ranges have been annotated (labeled `A`, `B`, and `C`). Each annotation holds a label (`Symbol`) -and a value (`Any`), paired together as a `Pair{Symbol, <:Any}`. +and a value (`Any`). These three pieces of information are held as a +`$RegionAnnotation`. Labels do not need to be unique, the same region can hold multiple annotations with the same label. @@ -43,7 +47,7 @@ See also [`AnnotatedChar`](@ref), [`annotatedstring`](@ref), ```julia AnnotatedString(s::S<:AbstractString) -> AnnotatedString{S} -AnnotatedString(s::S<:AbstractString, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, <:Any}}}) +AnnotatedString(s::S<:AbstractString, annotations::Vector{$RegionAnnotation}) ``` A AnnotatedString can also be created with [`annotatedstring`](@ref), which acts much @@ -59,7 +63,7 @@ julia> AnnotatedString("this is an example annotated string", """ struct AnnotatedString{S <: AbstractString} <: AbstractString string::S - annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + annotations::Vector{RegionAnnotation} end """ @@ -68,8 +72,8 @@ end A Char with annotations. More specifically, this is a simple wrapper around any other -[`AbstractChar`](@ref), which holds a list of arbitrary labeled annotations -(`Pair{Symbol, <:Any}`) with the wrapped character. +[`AbstractChar`](@ref), which holds a list of arbitrary labelled annotations +(`$Annotation`) with the wrapped character. See also: [`AnnotatedString`](@ref), [`annotatedstring`](@ref), `annotations`, and `annotate!`. @@ -78,7 +82,7 @@ and `annotate!`. ```julia AnnotatedChar(s::S) -> AnnotatedChar{S} -AnnotatedChar(s::S, annotations::Vector{Pair{Symbol, <:Any}}) +AnnotatedChar(s::S, annotations::Vector{$Annotation}) ``` # Examples @@ -90,41 +94,48 @@ julia> AnnotatedChar('j', :label => 1) """ struct AnnotatedChar{C <: AbstractChar} <: AbstractChar char::C - annotations::Vector{Pair{Symbol, Any}} + annotations::Vector{Annotation} end ## Constructors ## # When called with overly-specialised arguments -AnnotatedString(s::AbstractString, annots::Vector{<:Tuple{UnitRange{Int}, <:Pair{Symbol, <:Any}}}) = - AnnotatedString(s, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}(annots)) +AnnotatedString(s::AbstractString, annots::Vector) = + AnnotatedString(s, Vector{RegionAnnotation}(annots)) + +AnnotatedString(s::AbstractString, annots) = + AnnotatedString(s, collect(RegionAnnotation, annots)) -AnnotatedChar(c::AbstractChar, annots::Vector{<:Pair{Symbol, <:Any}}) = - AnnotatedChar(c, Vector{Pair{Symbol, Any}}(annots)) +AnnotatedChar(c::AbstractChar, annots::Vector) = + AnnotatedChar(c, Vector{Annotation}(annots)) + +AnnotatedChar(c::AbstractChar, annots) = + AnnotatedChar(c, collect(Annotation, annots)) # Constructors to avoid recursive wrapping -AnnotatedString(s::AnnotatedString, annots::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}) = +AnnotatedString(s::AnnotatedString, annots::Vector{RegionAnnotation}) = AnnotatedString(s.string, vcat(s.annotations, annots)) -AnnotatedChar(c::AnnotatedChar, annots::Vector{Pair{Symbol, Any}}) = - AnnotatedChar(c.char, vcat(c.annotations, annots)) +AnnotatedChar(c::AnnotatedChar, annots::Vector{Annotation}) = + AnnotatedChar(c.char, vcat(c.annotations, Vector{Annotation}(annots))) -String(s::AnnotatedString{String}) = s.string # To avoid pointless overhead +# To avoid pointless overhead +String(s::AnnotatedString{String}) = s.string ## Conversion/promotion ## convert(::Type{AnnotatedString}, s::AnnotatedString) = s convert(::Type{AnnotatedString{S}}, s::S) where {S <: AbstractString} = - AnnotatedString(s, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}()) + AnnotatedString(s, Vector{RegionAnnotation}()) convert(::Type{AnnotatedString}, s::S) where {S <: AbstractString} = convert(AnnotatedString{S}, s) AnnotatedString(s::S) where {S <: AbstractString} = convert(AnnotatedString{S}, s) convert(::Type{AnnotatedChar}, c::AnnotatedChar) = c convert(::Type{AnnotatedChar{C}}, c::C) where { C <: AbstractChar } = - AnnotatedChar{C}(c, Vector{Pair{Symbol, Any}}()) + AnnotatedChar{C}(c, Vector{Annotation}()) convert(::Type{AnnotatedChar}, c::C) where { C <: AbstractChar } = convert(AnnotatedChar{C}, c) @@ -150,7 +161,7 @@ lastindex(s::AnnotatedString) = lastindex(s.string) function getindex(s::AnnotatedString, i::Integer) @boundscheck checkbounds(s, i) @inbounds if isvalid(s, i) - AnnotatedChar(s.string[i], Pair{Symbol, Any}[last(x) for x in annotations(s, i)]) + AnnotatedChar(s.string[i], Annotation[(; label, value) for (; label, value) in annotations(s, i)]) else string_index_err(s, i) end @@ -164,7 +175,8 @@ function show(io::IO, s::A) where {A <: AnnotatedString} print(io, '(') show(io, s.string) print(io, ", ") - show(IOContext(io, :typeinfo => typeof(annotations(s))), annotations(s)) + tupanns = Vector{Tuple{UnitRange{Int}, Symbol, Any}}(map(values, s.annotations)) + show(IOContext(io, :typeinfo => typeof(tupanns)), tupanns) print(io, ')') end @@ -233,27 +245,27 @@ function annotatedstring(xs...) size = mapreduce(_str_sizehint, +, xs) buf = IOBuffer(sizehint=size) s = IOContext(buf, :color => true) - annotations = Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}() + annotations = Vector{RegionAnnotation}() for x in xs size = filesize(s.io) if x isa AnnotatedString - for (region, annot) in x.annotations - push!(annotations, (size .+ (region), annot)) + for annot in x.annotations + push!(annotations, setindex(annot, annot.region .+ size, :region)) end print(s, x.string) elseif x isa SubString{<:AnnotatedString} - for (region, annot) in x.string.annotations - start, stop = first(region), last(region) + for annot in x.string.annotations + start, stop = first(annot.region), last(annot.region) if start <= x.offset + x.ncodeunits && stop > x.offset rstart = size + max(0, start - x.offset - 1) + 1 rstop = size + min(stop, x.offset + x.ncodeunits) - x.offset - push!(annotations, (rstart:rstop, annot)) + push!(annotations, setindex(annot, rstart:rstop, :region)) end end print(s, SubString(x.string.string, x.offset, x.ncodeunits, Val(:noshift))) elseif x isa AnnotatedChar for annot in x.annotations - push!(annotations, (1+size:1+size, annot)) + push!(annotations, (region=1+size:1+size, annot...)) end print(s, x.char) else @@ -266,7 +278,7 @@ end annotatedstring(s::AnnotatedString) = s annotatedstring(c::AnnotatedChar) = - AnnotatedString(string(c.char), [(1:ncodeunits(c), annot) for annot in c.annotations]) + AnnotatedString(string(c.char), [(region=1:ncodeunits(c), annot...) for annot in c.annotations]) AnnotatedString(s::SubString{<:AnnotatedString}) = annotatedstring(s) @@ -274,18 +286,19 @@ function repeat(str::AnnotatedString, r::Integer) r == 0 && return one(AnnotatedString) r == 1 && return str unannot = repeat(str.string, r) - annotations = Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}() + annotations = Vector{RegionAnnotation}() len = ncodeunits(str) fullregion = firstindex(str):lastindex(str) - if allequal(first, str.annotations) && first(first(str.annotations)) == fullregion + if isempty(str.annotations) + elseif allequal(a -> a.region, str.annotations) && first(str.annotations).region == fullregion newfullregion = firstindex(unannot):lastindex(unannot) - for (_, annot) in str.annotations - push!(annotations, (newfullregion, annot)) + for annot in str.annotations + push!(annotations, setindex(annot, newfullregion, :region)) end else for offset in 0:len:(r-1)*len - for (region, annot) in str.annotations - push!(annotations, (region .+ offset, annot)) + for annot in str.annotations + push!(annotations, setindex(annot, annot.region .+ offset, :region)) end end end @@ -298,16 +311,18 @@ repeat(str::SubString{<:AnnotatedString}, r::Integer) = function repeat(c::AnnotatedChar, r::Integer) str = repeat(c.char, r) fullregion = firstindex(str):lastindex(str) - AnnotatedString(str, [(fullregion, annot) for annot in c.annotations]) + AnnotatedString(str, [(region=fullregion, annot...) for annot in c.annotations]) end function reverse(s::AnnotatedString) lastind = lastindex(s) - AnnotatedString(reverse(s.string), - [(UnitRange(1 + lastind - last(region), - 1 + lastind - first(region)), - annot) - for (region, annot) in s.annotations]) + AnnotatedString( + reverse(s.string), + [setindex(annot, + UnitRange(1 + lastind - last(annot.region), + 1 + lastind - first(annot.region)), + :region) + for annot in s.annotations]) end # TODO optimise? @@ -317,18 +332,17 @@ reverse(s::SubString{<:AnnotatedString}) = reverse(AnnotatedString(s)) ## End AbstractString interface ## -function _annotate!(annlist::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) - label, val = labelval - if val === nothing - deleteat!(annlist, findall(ann -> ann[1] == range && first(ann[2]) === label, annlist)) +function _annotate!(annlist::Vector{RegionAnnotation}, region::UnitRange{Int}, label::Symbol, @nospecialize(value::Any)) + if value === nothing + deleteat!(annlist, findall(ann -> ann.region == region && ann.label === label, annlist)) else - push!(annlist, (range, Pair{Symbol, Any}(label, val))) + push!(annlist, RegionAnnotation((; region, label, value))) end end """ - annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value) - annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value) + annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol, value) + annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol, value) Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`). To remove existing `label` annotations, use a value of `nothing`. @@ -336,30 +350,30 @@ To remove existing `label` annotations, use a value of `nothing`. The order in which annotations are applied to `str` is semantically meaningful, as described in [`AnnotatedString`](@ref). """ -annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (_annotate!(s.annotations, range, labelval); s) +annotate!(s::AnnotatedString, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (_annotate!(s.annotations, range, label, val); s) -annotate!(ss::AnnotatedString, @nospecialize(labelval::Pair{Symbol, <:Any})) = - annotate!(ss, firstindex(ss):lastindex(ss), labelval) +annotate!(ss::AnnotatedString, label::Symbol, @nospecialize(val::Any)) = + annotate!(ss, firstindex(ss):lastindex(ss), label, val) -annotate!(s::SubString{<:AnnotatedString}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (annotate!(s.string, s.offset .+ (range), labelval); s) +annotate!(s::SubString{<:AnnotatedString}, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (annotate!(s.string, s.offset .+ (range), label, val); s) -annotate!(s::SubString{<:AnnotatedString}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (annotate!(s.string, s.offset .+ (1:s.ncodeunits), labelval); s) +annotate!(s::SubString{<:AnnotatedString}, label::Symbol, @nospecialize(val::Any)) = + (annotate!(s.string, s.offset .+ (1:s.ncodeunits), label, val); s) """ - annotate!(char::AnnotatedChar, label::Symbol => value) + annotate!(char::AnnotatedChar, label::Symbol, value::Any) Annotate `char` with the pair `label => value`. """ -annotate!(c::AnnotatedChar, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (push!(c.annotations, labelval); c) +annotate!(c::AnnotatedChar, label::Symbol, @nospecialize(val::Any)) = + (push!(c.annotations, Annotation((; label, val))); c) """ annotations(str::Union{AnnotatedString, SubString{AnnotatedString}}, [position::Union{Integer, UnitRange}]) -> - Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + Vector{$RegionAnnotation} Get all annotations that apply to `str`. Should `position` be provided, only annotations that overlap with `position` will be returned. @@ -375,15 +389,16 @@ See also: [`annotate!`](@ref). annotations(s::AnnotatedString) = s.annotations function annotations(s::SubString{<:AnnotatedString}) - map(((region, annot),) -> (first(region)-s.offset:last(region)-s.offset, annot), - annotations(s.string, s.offset+1:s.offset+s.ncodeunits)) + RegionAnnotation[ + setindex(ann, first(ann.region)-s.offset:last(ann.region)-s.offset, :region) + for ann in annotations(s.string, s.offset+1:s.offset+s.ncodeunits)] end function annotations(s::AnnotatedString, pos::UnitRange{<:Integer}) # TODO optimise - Tuple{UnitRange{Int64}, Pair{Symbol, Any}}[ - (max(first(pos), first(region)):min(last(pos), last(region)), annot) - for (region, annot) in s.annotations if !isempty(intersect(pos, region))] + RegionAnnotation[ + setindex(ann, max(first(pos), first(ann.region)):min(last(pos), last(ann.region)), :region) + for ann in s.annotations if !isempty(intersect(pos, ann.region))] end annotations(s::AnnotatedString, pos::Integer) = annotations(s, pos:pos) @@ -395,7 +410,7 @@ annotations(s::SubString{<:AnnotatedString}, pos::UnitRange{<:Integer}) = annotations(s.string, first(pos)+s.offset:last(pos)+s.offset) """ - annotations(chr::AnnotatedChar) -> Vector{Pair{Symbol, Any}} + annotations(chr::AnnotatedChar) -> Vector{$Annotation} Get all annotations of `chr`, in the form of a vector of annotation pairs. """ @@ -420,7 +435,7 @@ string type of `str`). """ function annotated_chartransform(f::Function, str::AnnotatedString, state=nothing) outstr = IOBuffer() - annots = Tuple{UnitRange{Int}, Pair{Symbol, Any}}[] + annots = RegionAnnotation[] bytepos = firstindex(str) - 1 offsets = [bytepos => 0] for c in str.string @@ -437,11 +452,10 @@ function annotated_chartransform(f::Function, str::AnnotatedString, state=nothin end end for annot in str.annotations - region, value = annot - start, stop = first(region), last(region) + start, stop = first(annot.region), last(annot.region) start_offset = last(offsets[findlast(<=(start) ∘ first, offsets)::Int]) stop_offset = last(offsets[findlast(<=(stop) ∘ first, offsets)::Int]) - push!(annots, ((start + start_offset):(stop + stop_offset), value)) + push!(annots, setindex(annot, (start + start_offset):(stop + stop_offset), :region)) end AnnotatedString(String(take!(outstr)), annots) end @@ -450,10 +464,10 @@ end struct AnnotatedIOBuffer <: AbstractPipe io::IOBuffer - annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + annotations::Vector{RegionAnnotation} end -AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}()) +AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{RegionAnnotation}()) AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer()) function show(io::IO, aio::AnnotatedIOBuffer) @@ -475,8 +489,8 @@ copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations annotations(io::AnnotatedIOBuffer) = io.annotations -annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (_annotate!(io.annotations, range, labelval); io) +annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (_annotate!(io.annotations, range, label, val); io) function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}}) astr = AnnotatedString(astr) @@ -487,7 +501,7 @@ function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:A end write(io::AnnotatedIOBuffer, c::AnnotatedChar) = - write(io, AnnotatedString(string(c), map(a -> (1:ncodeunits(c), a), annotations(c)))) + write(io, AnnotatedString(string(c), [(region=1:ncodeunits(c), a...) for a in c.annotations])) write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x) write(io::AnnotatedIOBuffer, s::Union{SubString{String}, String}) = write(io.io, s) write(io::AnnotatedIOBuffer, b::UInt8) = write(io.io, b) @@ -498,8 +512,8 @@ function write(dest::AnnotatedIOBuffer, src::AnnotatedIOBuffer) srcpos = position(src) nb = write(dest.io, src.io) isappending || _clear_annotations_in_region!(dest.annotations, destpos:destpos+nb) - srcannots = [(max(1 + srcpos, first(region)):last(region), annot) - for (region, annot) in src.annotations if first(region) >= srcpos] + srcannots = [setindex(annot, max(1 + srcpos, first(annot.region)):last(annot.region), :region) + for annot in src.annotations if first(annot.region) >= srcpos] _insert_annotations!(dest, srcannots, destpos - srcpos) nb end @@ -523,7 +537,7 @@ function write(io::AbstractPipe, c::AnnotatedChar) end """ - _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, span::UnitRange{Int}) + _clear_annotations_in_region!(annotations::Vector{$RegionAnnotation}, span::UnitRange{Int}) Erase the presence of `annotations` within a certain `span`. @@ -531,21 +545,27 @@ This operates by removing all elements of `annotations` that are entirely contained in `span`, truncating ranges that partially overlap, and splitting annotations that subsume `span` to just exist either side of `span`. """ -function _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, span::UnitRange{Int}) +function _clear_annotations_in_region!(annotations::Vector{RegionAnnotation}, span::UnitRange{Int}) # Clear out any overlapping pre-existing annotations. - filter!(((region, _),) -> first(region) < first(span) || last(region) > last(span), annotations) - extras = Tuple{Int, Tuple{UnitRange{Int}, Pair{Symbol, Any}}}[] + filter!(ann -> first(ann.region) < first(span) || last(ann.region) > last(span), annotations) + extras = Tuple{Int, RegionAnnotation}[] for i in eachindex(annotations) - region, annot = annotations[i] + annot = annotations[i] + region = annot.region # Test for partial overlap if first(region) <= first(span) <= last(region) || first(region) <= last(span) <= last(region) - annotations[i] = (if first(region) < first(span) - first(region):first(span)-1 - else last(span)+1:last(region) end, annot) + annotations[i] = + setindex(annot, + if first(region) < first(span) + first(region):first(span)-1 + else + last(span)+1:last(region) + end, + :region) # If `span` fits exactly within `region`, then we've only copied over # the beginning overhang, but also need to conserve the end overhang. if first(region) < first(span) && last(span) < last(region) - push!(extras, (i, (last(span)+1:last(region), annot))) + push!(extras, (i, setindex(annot, last(span)+1:last(region), :region))) end end end @@ -557,7 +577,7 @@ function _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, end """ - _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, offset::Int = position(io)) + _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{$RegionAnnotation}, offset::Int = position(io)) Register new `annotations` in `io`, applying an `offset` to their regions. @@ -573,19 +593,19 @@ This is implemented so that one can say write an `AnnotatedString` to an `AnnotatedIOBuffer` one character at a time without needlessly producing a new annotation for each character. """ -function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, offset::Int = position(io)) +function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{RegionAnnotation}, offset::Int = position(io)) run = 0 - if !isempty(io.annotations) && last(first(last(io.annotations))) == offset + if !isempty(io.annotations) && last(last(io.annotations).region) == offset for i in reverse(axes(annotations, 1)) annot = annotations[i] - first(first(annot)) == 1 || continue + first(annot.region) == 1 || continue i <= length(io.annotations) || continue - if last(annot) == last(last(io.annotations)) + if annot.label == last(io.annotations).label && annot.value == last(io.annotations).value valid_run = true for runlen in 1:i - new_range, new_annot = annotations[begin+runlen-1] - old_range, old_annot = io.annotations[end-i+runlen] - if last(old_range) != offset || first(new_range) != 1 || old_annot != new_annot + new = annotations[begin+runlen-1] + old = io.annotations[end-i+runlen] + if last(old.region) != offset || first(new.region) != 1 || old.label != new.label || old.value != new.value valid_run = false break end @@ -599,14 +619,14 @@ function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{U end for runindex in 0:run-1 old_index = lastindex(io.annotations) - run + 1 + runindex - old_region, annot = io.annotations[old_index] - new_region, _ = annotations[begin+runindex] - io.annotations[old_index] = (first(old_region):last(new_region)+offset, annot) + old = io.annotations[old_index] + new = annotations[begin+runindex] + io.annotations[old_index] = setindex(old, first(old.region):last(new.region)+offset, :region) end for index in run+1:lastindex(annotations) - region, annot = annotations[index] - start, stop = first(region), last(region) - push!(io.annotations, (start+offset:stop+offset, annot)) + annot = annotations[index] + start, stop = first(annot.region), last(annot.region) + push!(io.annotations, setindex(annotations[index], start+offset:stop+offset, :region)) end end @@ -614,8 +634,8 @@ function read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{T}}) where {T <: Abs if (start = position(io)) == 0 AnnotatedString(read(io.io, T), copy(io.annotations)) else - annots = [(UnitRange{Int}(max(1, first(region) - start), last(region)-start), val) - for (region, val) in io.annotations if last(region) > start] + annots = [setindex(annot, UnitRange{Int}(max(1, first(annot.region) - start), last(annot.region)-start), :region) + for annot in io.annotations if last(annot.region) > start] AnnotatedString(read(io.io, T), annots) end end @@ -625,7 +645,7 @@ read(io::AnnotatedIOBuffer, ::Type{AnnotatedString}) = read(io, AnnotatedString{ function read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{T}}) where {T <: AbstractChar} pos = position(io) char = read(io.io, T) - annots = Pair{Symbol, Any}[annot for (range, annot) in io.annotations if pos+1 in range] + annots = [NamedTuple{(:label, :value)}(annot) for annot in io.annotations if pos+1 in annot.region] AnnotatedChar(char, annots) end read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{AbstractChar}}) = read(io, AnnotatedChar{Char}) @@ -633,8 +653,8 @@ read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar}) = read(io, AnnotatedChar{Char function truncate(io::AnnotatedIOBuffer, size::Integer) truncate(io.io, size) - filter!(((range, _),) -> first(range) <= size, io.annotations) - map!(((range, val),) -> (first(range):min(size, last(range)), val), + filter!(ann -> first(ann.region) <= size, io.annotations) + map!(ann -> setindex(ann, first(ann.region):min(size, last(ann.region)), :region), io.annotations, io.annotations) io end diff --git a/base/strings/io.jl b/base/strings/io.jl index df34712b519d5..82dd128240a92 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -816,12 +816,12 @@ function AnnotatedString(chars::AbstractVector{C}) where {C<:AbstractChar} end end end - annots = Tuple{UnitRange{Int}, Pair{Symbol, Any}}[] + annots = RegionAnnotation[] point = 1 for c in chars if c isa AnnotatedChar for annot in c.annotations - push!(annots, (point:point, annot)) + push!(annots, (point:point, annot...)) end end point += ncodeunits(c) diff --git a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 new file mode 100644 index 0000000000000..a86f3fe9c5561 --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/md5 @@ -0,0 +1 @@ +401bb32ca43a8460d6790ee80e695bb5 diff --git a/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 new file mode 100644 index 0000000000000..6e54aef5fd34f --- /dev/null +++ b/deps/checksums/JuliaSyntaxHighlighting-19bd57b89c648592155156049addf67e0638eab1.tar.gz/sha512 @@ -0,0 +1 @@ +db2c732d3343f5a8770b3516cdd900587d497feab2259a937d354fac436ab3cb099b0401fb4e05817e75744fb9877ab69b1e4879d8a710b33b69c95b7e58d961 diff --git a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 deleted file mode 100644 index cbcb8097d1673..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3dc1387ed88ba3c0df04d05a86d804d0 diff --git a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 b/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 deleted file mode 100644 index 2e58061d16058..0000000000000 --- a/deps/checksums/JuliaSyntaxHighlighting-b89dd99db56700c47434df6106b6c6afd1c9ed01.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fe30ed73b257e6928097cb7baca5b82a9a60b2f9b9f219fbcf570c5ed513447f0fda2a48da06b57e381516a69278f7f8519764d00e9e4fb5683a5411e245ef45 diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 new file mode 100644 index 0000000000000..8d78dd7b0a11b --- /dev/null +++ b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 @@ -0,0 +1 @@ +f053c84279a8920f355f202e605842af diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 new file mode 100644 index 0000000000000..5a8ca888c38f8 --- /dev/null +++ b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 @@ -0,0 +1 @@ +b6f4c1d6c0dc73a520472746c96adff506e5405154e4b93d419e07b577b01804d2fc87d4a6cac48a136777579bebf8388c2c1e54f849b51e233138d482146b4f diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 deleted file mode 100644 index 0d39747d275ba..0000000000000 --- a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bf7c157df6084942b794fbe5b768a643 diff --git a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 b/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 deleted file mode 100644 index d0a8d6cec08cf..0000000000000 --- a/deps/checksums/StyledStrings-f6035eb97b516862b16e36cab2ecc6ea8adc3d7c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ba2f6b91494662208842dec580ea9410d8d6ba4e57315c72e872227f5e2f68cc970fcf5dbd9c8a03920f93b6adabdeaab738fff04f9ca7b5da5cd6b89759e7f6 diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index c04e5e6d6760e..57431d07c0aa5 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -1230,7 +1230,7 @@ to keep the string annotations. ```jldoctest julia> str = Base.AnnotatedString("hello there", - [(1:5, :word => :greeting), (7:11, :label => 1)]) + [(1:5, :word, :greeting), (7:11, :label, 1)]) "hello there" julia> length(str) @@ -1242,7 +1242,7 @@ julia> lpad(str, 14) julia> typeof(lpad(str, 7)) Base.AnnotatedString{String} -julia> str2 = Base.AnnotatedString(" julia", [(2:6, :face => :magenta)]) +julia> str2 = Base.AnnotatedString(" julia", [(2:6, :face, :magenta)]) " julia" julia> Base.annotatedstring(str, str2) diff --git a/stdlib/JuliaSyntaxHighlighting.version b/stdlib/JuliaSyntaxHighlighting.version index 280db66afe5f9..2a409c721d32b 100644 --- a/stdlib/JuliaSyntaxHighlighting.version +++ b/stdlib/JuliaSyntaxHighlighting.version @@ -1,4 +1,4 @@ JULIASYNTAXHIGHLIGHTING_BRANCH = main -JULIASYNTAXHIGHLIGHTING_SHA1 = b89dd99db56700c47434df6106b6c6afd1c9ed01 +JULIASYNTAXHIGHLIGHTING_SHA1 = 19bd57b89c648592155156049addf67e0638eab1 JULIASYNTAXHIGHLIGHTING_GIT_URL := https://github.com/julialang/JuliaSyntaxHighlighting.jl.git JULIASYNTAXHIGHLIGHTING_TAR_URL = https://api.github.com/repos/julialang/JuliaSyntaxHighlighting.jl/tarball/$1 diff --git a/stdlib/Markdown/src/render/terminal/formatting.jl b/stdlib/Markdown/src/render/terminal/formatting.jl index 3274483801c77..c9dadfb5f3d94 100644 --- a/stdlib/Markdown/src/render/terminal/formatting.jl +++ b/stdlib/Markdown/src/render/terminal/formatting.jl @@ -19,9 +19,9 @@ function with_output_annotations(f::Function, io::AnnotIO, annots::Pair{Symbol, start = position(aio) + 1 f(io) stop = position(aio) - sortedindex = searchsortedlast(aio.annotations, (start:stop,), by=first) + sortedindex = searchsortedlast(aio.annotations, (region=start:stop,), by=a -> a.region) for (i, annot) in enumerate(annots) - insert!(aio.annotations, sortedindex + i, (start:stop, annot)) + insert!(aio.annotations, sortedindex + i, (start:stop, annot...)) end end diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index 83fbece4c8bc0..5e58a5456148a 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = f6035eb97b516862b16e36cab2ecc6ea8adc3d7c +STYLEDSTRINGS_SHA1 = 056e843b2d428bb9735b03af0cff97e738ac7e14 STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index ee53c3d5846eb..85acab74abf7b 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -2,7 +2,7 @@ @testset "AnnotatedString" begin str = Base.AnnotatedString("some string") - @test str == Base.AnnotatedString(str.string, Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]) + @test str == Base.AnnotatedString(str.string, Base.RegionAnnotation[]) @test length(str) == 11 @test ncodeunits(str) == 11 @test codeunits(str) == codeunits("some string") @@ -23,9 +23,9 @@ @test cmp("some stringy thingy", str) == 1 @test str[3:4] == SubString("me") @test SubString("me") == str[3:4] - Base.annotate!(str, 1:4, :thing => 0x01) - Base.annotate!(str, 6:11, :other => 0x02) - Base.annotate!(str, 1:11, :all => 0x03) + Base.annotate!(str, 1:4, :thing, 0x01) + Base.annotate!(str, 6:11, :other, 0x02) + Base.annotate!(str, 1:11, :all, 0x03) # :thing :other # ┌┸─┐ ┌──┸─┐ # "some string" @@ -35,21 +35,21 @@ @test str[3:4] != SubString("me") @test SubString("me") != str[3:4] @test Base.AnnotatedString(str[3:4]) == - Base.AnnotatedString("me", [(1:2, :thing => 0x01), (1:2, :all => 0x03)]) + Base.AnnotatedString("me", [(1:2, :thing, 0x01), (1:2, :all, 0x03)]) @test Base.AnnotatedString(str[3:6]) == - Base.AnnotatedString("me s", [(1:2, :thing => 0x01), (4:4, :other => 0x02), (1:4, :all => 0x03)]) - @test str == Base.AnnotatedString("some string", [(1:4, :thing => 0x01), (6:11, :other => 0x02), (1:11, :all => 0x03)]) + Base.AnnotatedString("me s", [(1:2, :thing, 0x01), (4:4, :other, 0x02), (1:4, :all, 0x03)]) + @test str == Base.AnnotatedString("some string", [(1:4, :thing, 0x01), (6:11, :other, 0x02), (1:11, :all, 0x03)]) @test str != Base.AnnotatedString("some string") - @test str != Base.AnnotatedString("some string", [(1:1, :thing => 0x01), (1:11, :all => 0x03), (6:6, :other => 0x02)]) - @test str != Base.AnnotatedString("some string", [(1:4, :thing => 0x11), (1:11, :all => 0x13), (6:11, :other => 0x12)]) - @test str != Base.AnnotatedString("some thingg", [(1:4, :thing => 0x01), (1:11, :all => 0x03), (6:11, :other => 0x02)]) - @test Base.AnnotatedString([Base.AnnotatedChar('a', [:a => 1]), Base.AnnotatedChar('b', [:b => 2])]) == - Base.AnnotatedString("ab", [(1:1, :a => 1), (2:2, :b => 2)]) + @test str != Base.AnnotatedString("some string", [(1:1, :thing, 0x01), (1:11, :all, 0x03), (6:6, :other, 0x02)]) + @test str != Base.AnnotatedString("some string", [(1:4, :thing, 0x11), (1:11, :all, 0x13), (6:11, :other, 0x12)]) + @test str != Base.AnnotatedString("some thingg", [(1:4, :thing, 0x01), (1:11, :all, 0x03), (6:11, :other, 0x02)]) + @test Base.AnnotatedString([Base.AnnotatedChar('a', [(:a, 1)]), Base.AnnotatedChar('b', [(:b, 2)])]) == + Base.AnnotatedString("ab", [(1:1, :a, 1), (2:2, :b, 2)]) let allstrings = - ['a', Base.AnnotatedChar('a'), Base.AnnotatedChar('a', [:aaa => 0x04]), + ['a', Base.AnnotatedChar('a'), Base.AnnotatedChar('a', [(:aaa, 0x04)]), "a string", Base.AnnotatedString("a string"), - Base.AnnotatedString("a string", [(1:2, :hmm => '%')]), - SubString(Base.AnnotatedString("a string", [(1:2, :hmm => '%')]), 1:1)] + Base.AnnotatedString("a string", [(1:2, :hmm, '%')]), + SubString(Base.AnnotatedString("a string", [(1:2, :hmm, '%')]), 1:1)] for str1 in repeat(allstrings, 2) for str2 in repeat(allstrings, 2) @test String(str1 * str2) == @@ -62,10 +62,10 @@ end end # @test collect(Base.eachstyle(str)) == - # [("some", [:thing => 0x01, :all => 0x03]), - # (" string", [:all => 0x03, :other => 0x02])] + # [("some", [:thing, 0x01, :all, 0x03]), + # (" string", [:all, 0x03, :other, 0x02])] @test chopprefix(sprint(show, str), "Base.") == - "AnnotatedString{String}(\"some string\", [(1:4, :thing => 0x01), (6:11, :other => 0x02), (1:11, :all => 0x03)])" + "AnnotatedString{String}(\"some string\", [(1:4, :thing, 0x01), (6:11, :other, 0x02), (1:11, :all, 0x03)])" @test eval(Meta.parse(repr(str))) == str @test sprint(show, MIME("text/plain"), str) == "\"some string\"" end @@ -78,16 +78,16 @@ end @test uppercase(chr) == Base.AnnotatedChar('C') @test titlecase(chr) == Base.AnnotatedChar('C') @test lowercase(Base.AnnotatedChar('C')) == chr - str = Base.AnnotatedString("hmm", [(1:1, :attr => "h0h0"), - (1:2, :attr => "h0m1"), - (2:3, :attr => "m1m2")]) - @test str[1] == Base.AnnotatedChar('h', Pair{Symbol, Any}[:attr => "h0h0"]) - @test str[2] == Base.AnnotatedChar('m', Pair{Symbol, Any}[:attr => "h0m1", :attr => "m1m2"]) - @test str[3] == Base.AnnotatedChar('m', Pair{Symbol, Any}[:attr => "m1m2"]) + str = Base.AnnotatedString("hmm", [(1:1, :attr, "h0h0"), + (1:2, :attr, "h0m1"), + (2:3, :attr, "m1m2")]) + @test str[1] == Base.AnnotatedChar('h', [(:attr, "h0h0")]) + @test str[2] == Base.AnnotatedChar('m', [(:attr, "h0m1"), (:attr, "m1m2")]) + @test str[3] == Base.AnnotatedChar('m', [(:attr, "m1m2")]) end @testset "Styling preservation" begin - str = Base.AnnotatedString("some string", [(1:4, :thing => 0x01), (1:11, :all => 0x03), (6:11, :other => 0x02)]) + str = Base.AnnotatedString("some string", [(1:4, :thing, 0x01), (1:11, :all, 0x03), (6:11, :other, 0x02)]) @test match(r".e", str).match == str[3:4] @test match(r"(.e)", str).captures == [str[3:4]] let m0 = match(r"(.)e", str) @@ -97,43 +97,43 @@ end end end @test lpad(str, 12) == - Base.AnnotatedString(" some string", [(2:5, :thing => 0x01), - (2:12, :all => 0x03), - (7:12, :other => 0x02)]) + Base.AnnotatedString(" some string", [(2:5, :thing, 0x01), + (2:12, :all, 0x03), + (7:12, :other, 0x02)]) @test rpad(str, 12) == - Base.AnnotatedString("some string ", [(1:4, :thing => 0x01), - (1:11, :all => 0x03), - (6:11, :other => 0x02)]) - str1 = Base.AnnotatedString("test", [(1:4, :label => 5)]) - str2 = Base.AnnotatedString("case", [(2:3, :label => "oomph")]) + Base.AnnotatedString("some string ", [(1:4, :thing, 0x01), + (1:11, :all, 0x03), + (6:11, :other, 0x02)]) + str1 = Base.AnnotatedString("test", [(1:4, :label, 5)]) + str2 = Base.AnnotatedString("case", [(2:3, :label, "oomph")]) @test join([str1, str1], ' ') == Base.AnnotatedString("test test", - [(1:4, :label => 5), - (6:9, :label => 5)]) - @test join([str1, str1], Base.AnnotatedString(" ", [(1:1, :label => 2)])) == + [(1:4, :label, 5), + (6:9, :label, 5)]) + @test join([str1, str1], Base.AnnotatedString(" ", [(1:1, :label, 2)])) == Base.AnnotatedString("test test", - [(1:4, :label => 5), - (5:5, :label => 2), - (6:9, :label => 5)]) + [(1:4, :label, 5), + (5:5, :label, 2), + (6:9, :label, 5)]) @test join((String(str1), str1), ' ') == - Base.AnnotatedString("test test", [(6:9, :label => 5)]) - @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label => 5)]) - @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label => "oomph"), - (6:7, :label => "oomph")]) - @test repeat(str1[1], 3) == Base.AnnotatedString("ttt", [(1:3, :label => 5)]) - @test reverse(str1) == Base.AnnotatedString("tset", [(1:4, :label => 5)]) - @test reverse(str2) == Base.AnnotatedString("esac", [(2:3, :label => "oomph")]) + Base.AnnotatedString("test test", [(6:9, :label, 5)]) + @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label, 5)]) + @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label, "oomph"), + (6:7, :label, "oomph")]) + @test repeat(str1[1], 3) == Base.AnnotatedString("ttt", [(1:3, :label, 5)]) + @test reverse(str1) == Base.AnnotatedString("tset", [(1:4, :label, 5)]) + @test reverse(str2) == Base.AnnotatedString("esac", [(2:3, :label, "oomph")]) end @testset "Unicode" begin for words in (["ᲃase", "cɦɒnɡeȿ", "can", "CHⱯNGE", "Сodeunıts"], ["Сodeunıts", "ᲃase", "cɦɒnɡeȿ", "can", "CHⱯNGE"]) - ann_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(words)] ann_str = join(ann_words, '-') for transform in (lowercase, uppercase, titlecase) t_words = map(transform, words) - ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(t_words)] ann_t_str = join(ann_t_words, '-') t_ann_str = transform(ann_str) @@ -142,7 +142,7 @@ end end for transform in (uppercasefirst, lowercasefirst) t_words = vcat(transform(first(words)), words[2:end]) - ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(t_words)] ann_t_str = join(ann_t_words, '-') t_ann_str = transform(ann_str) @@ -154,31 +154,32 @@ end @testset "AnnotatedIOBuffer" begin aio = Base.AnnotatedIOBuffer() + vec2ann(v::Vector{<:Tuple}) = collect(Base.RegionAnnotation, v) # Append-only writing - @test write(aio, Base.AnnotatedString("hello", [(1:5, :tag => 1)])) == 5 + @test write(aio, Base.AnnotatedString("hello", [(1:5, :tag, 1)])) == 5 @test write(aio, ' ') == 1 - @test write(aio, Base.AnnotatedString("world", [(1:5, :tag => 2)])) == 5 - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] + @test write(aio, Base.AnnotatedString("world", [(1:5, :tag, 2)])) == 5 + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Check `annotate!`, including region sorting @test truncate(aio, 0).io.size == 0 @test write(aio, "hello world") == ncodeunits("hello world") - @test Base.annotate!(aio, 1:5, :tag => 1) === aio - @test Base.annotate!(aio, 7:11, :tag => 2) === aio - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] + @test Base.annotate!(aio, 1:5, :tag, 1) === aio + @test Base.annotate!(aio, 7:11, :tag, 2) === aio + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Reading @test read(seekstart(deepcopy(aio.io)), String) == "hello world" @test read(seekstart(deepcopy(aio)), String) == "hello world" - @test read(seek(aio, 0), Base.AnnotatedString) == Base.AnnotatedString("hello world", [(1:5, :tag => 1), (7:11, :tag => 2)]) - @test read(seek(aio, 1), Base.AnnotatedString) == Base.AnnotatedString("ello world", [(1:4, :tag => 1), (6:10, :tag => 2)]) - @test read(seek(aio, 4), Base.AnnotatedString) == Base.AnnotatedString("o world", [(1:1, :tag => 1), (3:7, :tag => 2)]) - @test read(seek(aio, 5), Base.AnnotatedString) == Base.AnnotatedString(" world", [(2:6, :tag => 2)]) + @test read(seek(aio, 0), Base.AnnotatedString) == Base.AnnotatedString("hello world", [(1:5, :tag, 1), (7:11, :tag, 2)]) + @test read(seek(aio, 1), Base.AnnotatedString) == Base.AnnotatedString("ello world", [(1:4, :tag, 1), (6:10, :tag, 2)]) + @test read(seek(aio, 4), Base.AnnotatedString) == Base.AnnotatedString("o world", [(1:1, :tag, 1), (3:7, :tag, 2)]) + @test read(seek(aio, 5), Base.AnnotatedString) == Base.AnnotatedString(" world", [(2:6, :tag, 2)]) @test read(seekend(aio), Base.AnnotatedString) == Base.AnnotatedString("") - @test read(seekstart(truncate(deepcopy(aio), 5)), Base.AnnotatedString) == Base.AnnotatedString("hello", [(1:5, :tag => 1)]) - @test read(seekstart(truncate(deepcopy(aio), 6)), Base.AnnotatedString) == Base.AnnotatedString("hello ", [(1:5, :tag => 1)]) - @test read(seekstart(truncate(deepcopy(aio), 7)), Base.AnnotatedString) == Base.AnnotatedString("hello w", [(1:5, :tag => 1), (7:7, :tag => 2)]) - @test read(seek(aio, 0), Base.AnnotatedChar) == Base.AnnotatedChar('h', [:tag => 1]) - @test read(seek(aio, 5), Base.AnnotatedChar) == Base.AnnotatedChar(' ', Pair{Symbol, Any}[]) - @test read(seek(aio, 6), Base.AnnotatedChar) == Base.AnnotatedChar('w', [:tag => 2]) + @test read(seekstart(truncate(deepcopy(aio), 5)), Base.AnnotatedString) == Base.AnnotatedString("hello", [(1:5, :tag, 1)]) + @test read(seekstart(truncate(deepcopy(aio), 6)), Base.AnnotatedString) == Base.AnnotatedString("hello ", [(1:5, :tag, 1)]) + @test read(seekstart(truncate(deepcopy(aio), 7)), Base.AnnotatedString) == Base.AnnotatedString("hello w", [(1:5, :tag, 1), (7:7, :tag, 2)]) + @test read(seek(aio, 0), Base.AnnotatedChar) == Base.AnnotatedChar('h', [(:tag, 1)]) + @test read(seek(aio, 5), Base.AnnotatedChar) == Base.AnnotatedChar(' ', []) + @test read(seek(aio, 6), Base.AnnotatedChar) == Base.AnnotatedChar('w', [(:tag, 2)]) # Check method compatibility with IOBuffer @test position(aio) == 7 @test seek(aio, 4) === aio @@ -188,19 +189,19 @@ end # Writing into the middle of the buffer @test write(seek(aio, 6), "alice") == 5 # Replace 'world' with 'alice' @test read(seekstart(aio), String) == "hello alice" - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] # Should be unchanged - @test write(seek(aio, 0), Base.AnnotatedString("hey-o", [(1:5, :hey => 'o')])) == 5 + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Should be unchanged + @test write(seek(aio, 0), Base.AnnotatedString("hey-o", [(1:5, :hey, 'o')])) == 5 @test read(seekstart(aio), String) == "hey-o alice" - @test Base.annotations(aio) == [(7:11, :tag => 2), (1:5, :hey => 'o')] # First annotation should have been entirely replaced - @test write(seek(aio, 7), Base.AnnotatedString("bbi", [(1:3, :hey => 'a')])) == 3 # a[lic => bbi]e ('alice' => 'abbie') + @test Base.annotations(aio) == vec2ann([(7:11, :tag, 2), (1:5, :hey, 'o')]) # First annotation should have been entirely replaced + @test write(seek(aio, 7), Base.AnnotatedString("bbi", [(1:3, :hey, 'a')])) == 3 # a[lic, bbi]e ('alice', 'abbie') @test read(seekstart(aio), String) == "hey-o abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (1:5, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (1:5, :hey, 'o'), (8:10, :hey, 'a')]) @test write(seek(aio, 0), Base.AnnotatedString("ab")) == 2 # Check first annotation's region is adjusted correctly @test read(seekstart(aio), String) == "aby-o abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (3:5, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (3:5, :hey, 'o'), (8:10, :hey, 'a')]) @test write(seek(aio, 3), Base.AnnotatedString("ss")) == 2 @test read(seekstart(aio), String) == "abyss abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (3:3, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (3:3, :hey, 'o'), (8:10, :hey, 'a')]) # Writing one buffer to another newaio = Base.AnnotatedIOBuffer() @test write(newaio, seekstart(aio)) == 11 @@ -210,36 +211,37 @@ end @test sort(Base.annotations(newaio)) == sort(Base.annotations(aio)) @test write(newaio, seek(aio, 5)) == 6 @test read(seekstart(newaio), String) == "abyss abbie abbie" - @test sort(Base.annotations(newaio)) == sort(vcat(Base.annotations(aio), [(13:13, :tag => 2), (14:16, :hey => 'a'), (17:17, :tag => 2)])) + @test sort(Base.annotations(newaio)) == + sort(vcat(Base.annotations(aio), vec2ann([(13:13, :tag, 2), (14:16, :hey, 'a'), (17:17, :tag, 2)]))) # The `_insert_annotations!` cautious-merging optimisation aio = Base.AnnotatedIOBuffer() - @test write(aio, Base.AnnotatedChar('a', [:a => 1, :b => 2])) == 1 - @test Base.annotations(aio) == [(1:1, :a => 1), (1:1, :b => 2)] - @test write(aio, Base.AnnotatedChar('b', [:a => 1, :b => 2])) == 1 - @test Base.annotations(aio) == [(1:2, :a => 1), (1:2, :b => 2)] + @test write(aio, Base.AnnotatedChar('a', [(:a, 1), (:b, 2)])) == 1 + @test Base.annotations(aio) == vec2ann([(1:1, :a, 1), (1:1, :b, 2)]) + @test write(aio, Base.AnnotatedChar('b', [(:a, 1), (:b, 2)])) == 1 + @test Base.annotations(aio) == vec2ann([(1:2, :a, 1), (1:2, :b, 2)]) let aio2 = copy(aio) # A different start makes merging too risky to do. - @test write(aio2, Base.AnnotatedChar('c', [:a => 0, :b => 2])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:2, :b => 2), (3:3, :a => 0), (3:3, :b => 2)] + @test write(aio2, Base.AnnotatedChar('c', [(:a, 0), (:b, 2)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:2, :b, 2), (3:3, :a, 0), (3:3, :b, 2)]) end let aio2 = copy(aio) # Merging some run of the most recent annotations is fine though. - @test write(aio2, Base.AnnotatedChar('c', [:b => 2])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:3, :b => 2)] + @test write(aio2, Base.AnnotatedChar('c', [(:b, 2)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:3, :b, 2)]) end let aio2 = copy(aio) # ...and any subsequent annotations after a matching run can just be copied over. - @test write(aio2, Base.AnnotatedChar('c', [:b => 2, :c => 3, :d => 4])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:3, :b => 2), (3:3, :c => 3), (3:3, :d => 4)] + @test write(aio2, Base.AnnotatedChar('c', [(:b, 2), (:c, 3), (:d, 4)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:3, :b, 2), (3:3, :c, 3), (3:3, :d, 4)]) end let aio2 = Base.AnnotatedIOBuffer() - @test write(aio2, Base.AnnotatedChar('a', [:b => 1])) == 1 - @test write(aio2, Base.AnnotatedChar('b', [:a => 1, :b => 1])) == 1 + @test write(aio2, Base.AnnotatedChar('a', [(:b, 1)])) == 1 + @test write(aio2, Base.AnnotatedChar('b', [(:a, 1), (:b, 1)])) == 1 @test read(seekstart(aio2), Base.AnnotatedString) == - Base.AnnotatedString("ab", [(1:1, :b => 1), (2:2, :a => 1), (2:2, :b => 1)]) + Base.AnnotatedString("ab", [(1:1, :b, 1), (2:2, :a, 1), (2:2, :b, 1)]) end # Working through an IOContext aio = Base.AnnotatedIOBuffer() wrapio = IOContext(aio) - @test write(wrapio, Base.AnnotatedString("hey", [(1:3, :x => 1)])) == 3 - @test write(wrapio, Base.AnnotatedChar('a', [:y => 2])) == 1 + @test write(wrapio, Base.AnnotatedString("hey", [(1:3, :x, 1)])) == 3 + @test write(wrapio, Base.AnnotatedChar('a', [(:y, 2)])) == 1 @test read(seekstart(aio), Base.AnnotatedString) == - Base.AnnotatedString("heya", [(1:3, :x => 1), (4:4, :y => 2)]) + Base.AnnotatedString("heya", [(1:3, :x, 1), (4:4, :y, 2)]) end From 54299d941587d5f4371ba203efa5589e815bf6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Wed, 16 Oct 2024 06:32:14 +0100 Subject: [PATCH 405/548] Remove redundant `convert` in `_setindex!` (#56178) Follow up to #56034, ref: https://github.com/JuliaLang/julia/pull/56034#discussion_r1798573573. --------- Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> --- base/array.jl | 4 ++-- base/genericmemory.jl | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/base/array.jl b/base/array.jl index 9bd632f794aa5..a628c1212659d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -999,11 +999,11 @@ function setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} x = x isa T ? x : convert(T, x)::T return _setindex!(A, x, i1, i2, I...) end -function _setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} +function _setindex!(A::Array{T}, x::T, i1::Int, i2::Int, I::Int...) where {T} @inline @_noub_if_noinbounds_meta @boundscheck checkbounds(A, i1, i2, I...) # generally _to_linear_index requires bounds checking - memoryrefset!(memoryrefnew(A.ref, _to_linear_index(A, i1, i2, I...), false), x isa T ? x : convert(T,x)::T, :not_atomic, false) + memoryrefset!(memoryrefnew(A.ref, _to_linear_index(A, i1, i2, I...), false), x, :not_atomic, false) return A end diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 91b87ab14c6b1..5fe070a73628d 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -235,13 +235,18 @@ getindex(A::Memory, c::Colon) = copy(A) ## Indexing: setindex! ## -function setindex!(A::Memory{T}, x, i1::Int) where {T} - val = x isa T ? x : convert(T,x)::T +function _setindex!(A::Memory{T}, x::T, i1::Int) where {T} ref = memoryrefnew(memoryref(A), i1, @_boundscheck) - memoryrefset!(ref, val, :not_atomic, @_boundscheck) + memoryrefset!(ref, x, :not_atomic, @_boundscheck) return A end +function setindex!(A::Memory{T}, x, i1::Int) where {T} + @_propagate_inbounds_meta + val = x isa T ? x : convert(T,x)::T + return _setindex!(A, val, i1) +end + function setindex!(A::Memory{T}, x, i1::Int, i2::Int, I::Int...) where {T} @inline @boundscheck (i2 == 1 && all(==(1), I)) || throw_boundserror(A, (i1, i2, I...)) From 6ee784d919b6c97178d50fa85e0420d144f1adbe Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 16 Oct 2024 02:33:42 -0300 Subject: [PATCH 406/548] Improve type inference of Artifacts.jl (#56118) This also has some changes that move platform selection to compile time together with https://github.com/JuliaPackaging/JLLWrappers.jl/commit/45cc04963f3c99d4eb902f97528fe16fc37002cc, move the platform selection to compile time. (this helps juliac a ton) --- stdlib/Artifacts/src/Artifacts.jl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index 9bca72f6c7a14..e21db58b9445e 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -443,7 +443,7 @@ function artifact_hash(name::String, artifacts_toml::String; return nothing end - return SHA1(meta["git-tree-sha1"]) + return SHA1(meta["git-tree-sha1"]::String) end function select_downloadable_artifacts(artifact_dict::Dict, artifacts_toml::String; @@ -642,10 +642,9 @@ function artifact_slash_lookup(name::String, artifact_dict::Dict, if meta === nothing error("Cannot locate artifact '$(name)' for $(triplet(platform)) in '$(artifacts_toml)'") end - hash = SHA1(meta["git-tree-sha1"]) + hash = SHA1(meta["git-tree-sha1"]::String) return artifact_name, artifact_path_tail, hash end - """ macro artifact_str(name) @@ -707,17 +706,16 @@ macro artifact_str(name, platform=nothing) # If `name` is a constant, (and we're using the default `Platform`) we can actually load # and parse the `Artifacts.toml` file now, saving the work from runtime. - if isa(name, AbstractString) && platform === nothing - # To support slash-indexing, we need to split the artifact name from the path tail: + if platform === nothing platform = HostPlatform() + end + if isa(name, AbstractString) && isa(platform, AbstractPlatform) + # To support slash-indexing, we need to split the artifact name from the path tail: artifact_name, artifact_path_tail, hash = artifact_slash_lookup(name, artifact_dict, artifacts_toml, platform) return quote Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), $(artifact_name), $(artifact_path_tail), $(artifact_dict), $(hash), $(platform), Val($(LazyArtifacts)))::String end else - if platform === nothing - platform = :($(HostPlatform)()) - end return quote local platform = $(esc(platform)) local artifact_name, artifact_path_tail, hash = artifact_slash_lookup($(esc(name)), $(artifact_dict), $(artifacts_toml), platform) From a98f3713cb9aea3225cdb0e3ec0dd719f556a974 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 16 Oct 2024 08:06:42 +0200 Subject: [PATCH 407/548] Initial support for RISC-V (#56105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebase and extension of @alexfanqi's initial work on porting Julia to RISC-V. Requires LLVM 19. Tested on a VisionFive2, built with: ```make MARCH := rv64gc_zba_zbb MCPU := sifive-u74 USE_BINARYBUILDER:=0 DEPS_GIT = llvm override LLVM_VER=19.1.1 override LLVM_BRANCH=julia-release/19.x override LLVM_SHA1=julia-release/19.x ``` ```julia-repl ❯ ./julia _ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 1.12.0-DEV.1374 (2024-10-14) _/ |\__'_|_|_|\__'_| | riscv/25092a3982* (fork: 1 commits, 0 days) |__/ | julia> versioninfo(; verbose=true) Julia Version 1.12.0-DEV.1374 Commit 25092a3982* (2024-10-14 09:57 UTC) Platform Info: OS: Linux (riscv64-unknown-linux-gnu) uname: Linux 6.11.3-1-riscv64 #1 SMP Debian 6.11.3-1 (2024-10-10) riscv64 unknown CPU: unknown: speed user nice sys idle irq #1 1500 MHz 922 s 0 s 265 s 160953 s 0 s #2 1500 MHz 457 s 0 s 280 s 161521 s 0 s #3 1500 MHz 452 s 0 s 270 s 160911 s 0 s #4 1500 MHz 638 s 15 s 301 s 161340 s 0 s Memory: 7.760246276855469 GB (7474.08203125 MB free) Uptime: 16260.13 sec Load Avg: 0.25 0.23 0.1 WORD_SIZE: 64 LLVM: libLLVM-19.1.1 (ORCJIT, sifive-u74) Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores) Environment: HOME = /home/tim PATH = /home/tim/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/games TERM = xterm-256color julia> ccall(:jl_dump_host_cpu, Nothing, ()) CPU: sifive-u74 Features: +zbb,+d,+i,+f,+c,+a,+zba,+m,-zvbc,-zksed,-zvfhmin,-zbkc,-zkne,-zksh,-zfh,-zfhmin,-zknh,-v,-zihintpause,-zicboz,-zbs,-zvknha,-zvksed,-zfa,-ztso,-zbc,-zvknhb,-zihintntl,-zknd,-zvbb,-zbkx,-zkt,-zvkt,-zicond,-zvksh,-zvfh,-zvkg,-zvkb,-zbkb,-zvkned julia> @code_native debuginfo=:none 1+2. .text .attribute 4, 16 .attribute 5, "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zba1p0_zbb1p0" .file "+" .globl "julia_+_3003" .p2align 1 .type "julia_+_3003",@function "julia_+_3003": addi sp, sp, -16 sd ra, 8(sp) sd s0, 0(sp) addi s0, sp, 16 fcvt.d.l fa5, a0 ld ra, 8(sp) ld s0, 0(sp) fadd.d fa0, fa5, fa0 addi sp, sp, 16 ret .Lfunc_end0: .size "julia_+_3003", .Lfunc_end0-"julia_+_3003" .type ".L+Core.Float64#3005",@object .section .data.rel.ro,"aw",@progbits .p2align 3, 0x0 ".L+Core.Float64#3005": .quad ".L+Core.Float64#3005.jit" .size ".L+Core.Float64#3005", 8 .set ".L+Core.Float64#3005.jit", 272467692544 .size ".L+Core.Float64#3005.jit", 8 .section ".note.GNU-stack","",@progbits ``` Lots of bugs guaranteed, but with this we at least have a functional build and REPL for further development by whoever is interested. Also requires Linux 6.4+, since the fallback processor detection used here relies on LLVM's `sys::getHostCPUFeatures`, which for RISC-V is implemented using hwprobe introduced in 6.4. We could probably add a fallback that parses `/proc/cpuinfo`, either by building a CPU database much like how we've done for AArch64, or by parsing the actual ISA string contained there. That would probably also be a good place to add support for profiles, which are supposedly the way forward to package RISC-V binaries. That can happen in follow-up PRs though. For now, on older kernels, use the `-C` arg to Julia to specify an ISA. Co-authored-by: Alex Fan --- Make.inc | 13 +- base/binaryplatforms.jl | 5 +- base/cpuid.jl | 3 + cli/trampolines/trampolines_riscv64.S | 20 ++ contrib/generate_precompile.jl | 9 +- contrib/normalize_triplet.py | 1 + doc/src/devdocs/build/build.md | 1 + doc/src/devdocs/build/riscv.md | 103 +++++++++ src/abi_riscv.cpp | 315 ++++++++++++++++++++++++++ src/aotcompile.cpp | 3 +- src/ccall.cpp | 3 + src/codegen.cpp | 7 +- src/disasm.cpp | 2 + src/jitlayers.cpp | 26 ++- src/jitlayers.h | 17 +- src/julia_internal.h | 9 +- src/julia_threads.h | 2 +- src/llvm-ptls.cpp | 2 + src/llvm-version.h | 4 + src/runtime_intrinsics.c | 4 +- src/signal-handling.c | 19 +- src/signals-unix.c | 8 + src/stackwalk.c | 38 ++++ src/support/platform.h | 3 + src/task.c | 8 + src/threading.c | 4 +- 26 files changed, 609 insertions(+), 20 deletions(-) create mode 100644 cli/trampolines/trampolines_riscv64.S create mode 100644 doc/src/devdocs/build/riscv.md create mode 100644 src/abi_riscv.cpp diff --git a/Make.inc b/Make.inc index 53aee8a269732..cb79e3ca1b5a9 100644 --- a/Make.inc +++ b/Make.inc @@ -938,8 +938,12 @@ endif #If nothing is set default to native unless we are cross-compiling ifeq ($(MARCH)$(MCPU)$(MTUNE)$(JULIA_CPU_TARGET)$(XC_HOST),) -ifeq ($(ARCH),aarch64) #ARM recommends only setting MCPU for AArch64 +ifeq ($(ARCH),aarch64) +# ARM recommends only setting MCPU for AArch64 MCPU=native +else ifneq (,$(findstring riscv64,$(ARCH))) +# RISC-V doesn't have a native option +$(error Building for RISC-V requires a specific MARCH to be set)) else MARCH=native MTUNE=native @@ -995,6 +999,9 @@ endif ifneq (,$(findstring arm,$(ARCH))) DIST_ARCH:=arm endif +ifneq (,$(findstring riscv64,$(ARCH))) +DIST_ARCH:=riscv64 +endif JULIA_BINARYDIST_FILENAME := julia-$(JULIA_COMMIT)-$(DIST_OS)$(DIST_ARCH) endif @@ -1018,8 +1025,12 @@ ifneq ($(MARCH),) CC += -march=$(MARCH) CXX += -march=$(MARCH) FC += -march=$(MARCH) +# On RISC-V, don't forward the MARCH ISA string to JULIA_CPU_TARGET, +# as it's always incompatible with LLVM's CPU target name parser. +ifeq (,$(findstring riscv64,$(ARCH))) JULIA_CPU_TARGET ?= $(MARCH) endif +endif # Set MCPU-specific flags ifneq ($(MCPU),) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index c8a55c99a5724..a372137edeb98 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -597,7 +597,7 @@ const arch_mapping = Dict( "armv7l" => "arm(v7l)?", # if we just see `arm-linux-gnueabihf`, we assume it's `armv7l` "armv6l" => "armv6l", "powerpc64le" => "p(ower)?pc64le", - "riscv64" => "riscv64", + "riscv64" => "(rv64|riscv64)", ) # Keep this in sync with `CPUID.ISAs_by_family` # These are the CPUID side of the microarchitectures targeted by GCC flags in BinaryBuilder.jl @@ -631,6 +631,9 @@ const arch_march_isa_mapping = let "a64fx" => get_set("aarch64", "a64fx"), "apple_m1" => get_set("aarch64", "apple_m1"), ], + "riscv64" => [ + "riscv64" => get_set("riscv64", "riscv64") + ], "powerpc64le" => [ "power8" => get_set("powerpc64le", "power8"), ], diff --git a/base/cpuid.jl b/base/cpuid.jl index f653ba27b4bcd..0370bd33b83e5 100644 --- a/base/cpuid.jl +++ b/base/cpuid.jl @@ -61,6 +61,9 @@ const ISAs_by_family = Dict( "a64fx" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_sha2, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fullfp16, JL_AArch64_sve))), "apple_m1" => ISA(Set((JL_AArch64_v8_5a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_sha3, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fp16fml, JL_AArch64_fullfp16, JL_AArch64_dotprod, JL_AArch64_rcpc, JL_AArch64_altnzcv))), ], + "riscv64" => [ + "riscv64" => ISA(Set{UInt32}()), + ], "powerpc64le" => [ # We have no way to test powerpc64le features yet, so we're only going to declare the lowest ISA: "power8" => ISA(Set{UInt32}()), diff --git a/cli/trampolines/trampolines_riscv64.S b/cli/trampolines/trampolines_riscv64.S new file mode 100644 index 0000000000000..26307b7c2bb36 --- /dev/null +++ b/cli/trampolines/trampolines_riscv64.S @@ -0,0 +1,20 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include "common.h" +#include "../../src/jl_exported_funcs.inc" + +#define SEP ; + +#define XX(name) \ +.global CNAME(name) SEP \ +.cfi_startproc SEP \ +.p2align 2 SEP \ + CNAME(name)##: SEP \ + auipc t3, %pcrel_hi(CNAMEADDR(name)) SEP \ + ld t3, %pcrel_lo(CNAME(name))(t3) SEP \ + jr t3 SEP \ +.cfi_endproc SEP \ + +JL_RUNTIME_EXPORTED_FUNCS(XX) +JL_CODEGEN_EXPORTED_FUNCS(XX) +#undef XX diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 60f7290c7a0ac..04d13011d6223 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -202,12 +202,15 @@ if Artifacts !== nothing using Artifacts, Base.BinaryPlatforms, Libdl artifacts_toml = abspath(joinpath(Sys.STDLIB, "Artifacts", "test", "Artifacts.toml")) artifact_hash("HelloWorldC", artifacts_toml) - oldpwd = pwd(); cd(dirname(artifacts_toml)) - macroexpand(Main, :(@artifact_str("HelloWorldC"))) - cd(oldpwd) artifacts = Artifacts.load_artifacts_toml(artifacts_toml) platforms = [Artifacts.unpack_platform(e, "HelloWorldC", artifacts_toml) for e in artifacts["HelloWorldC"]] best_platform = select_platform(Dict(p => triplet(p) for p in platforms)) + if best_platform !== nothing + # @artifact errors for unsupported platforms + oldpwd = pwd(); cd(dirname(artifacts_toml)) + macroexpand(Main, :(@artifact_str("HelloWorldC"))) + cd(oldpwd) + end dlopen("libjulia$(Base.isdebugbuild() ? "-debug" : "")", RTLD_LAZY | RTLD_DEEPBIND) """ end diff --git a/contrib/normalize_triplet.py b/contrib/normalize_triplet.py index b1bab29487b8f..833b725480996 100755 --- a/contrib/normalize_triplet.py +++ b/contrib/normalize_triplet.py @@ -14,6 +14,7 @@ 'i686': "i\\d86", 'aarch64': "(arm|aarch)64", 'armv7l': "arm(v7l)?", + 'riscv64': "(rv64|riscv64)", 'powerpc64le': "p(ower)?pc64le", } platform_mapping = { diff --git a/doc/src/devdocs/build/build.md b/doc/src/devdocs/build/build.md index 0ef9ce4e4f071..553f7c2e815cf 100644 --- a/doc/src/devdocs/build/build.md +++ b/doc/src/devdocs/build/build.md @@ -148,6 +148,7 @@ Notes for various operating systems: Notes for various architectures: * [ARM](https://github.com/JuliaLang/julia/blob/master/doc/src/devdocs/build/arm.md) +* [RISC-V](https://github.com/JuliaLang/julia/blob/master/doc/src/devdocs/build/riscv.md) ## Required Build Tools and External Libraries diff --git a/doc/src/devdocs/build/riscv.md b/doc/src/devdocs/build/riscv.md new file mode 100644 index 0000000000000..7c0e7ab29d9f8 --- /dev/null +++ b/doc/src/devdocs/build/riscv.md @@ -0,0 +1,103 @@ +# RISC-V (Linux) + +Julia has experimental support for 64-bit RISC-V (RV64) processors running +Linux. This file provides general guidelines for compilation, in addition to +instructions for specific devices. + +A list of [known issues](https://github.com/JuliaLang/julia/labels/system:riscv) +for RISC-V is available. If you encounter difficulties, please create an issue +including the output from `cat /proc/cpuinfo`. + + +## Compiling Julia + +For now, Julia will need to be compiled entirely from source, i.e., including +all of its dependencies. This can be accomplished with the following +`Make.user`: + +```make +USE_BINARYBUILDER := 0 +``` + +Additionally, it is required to indicate what architecture, and optionally which +CPU to build for. This can be done by setting the `MARCH` and `MCPU` variables +in `Make.user` + +The `MARCH` variable needs to be set to a RISC-V ISA string, which can be found by +looking at the documentation of your device, or by inspecting `/proc/cpuinfo`. Only +use flags that your compiler supports, e.g., run `gcc -march=help` to see a list of +supported flags. A common value is `rv64gc`, which is a good starting point. + +The `MCPU` variable is optional, and can be used to further optimize the +generated code for a specific CPU. If you are unsure, it is recommended to leave +it unset. You can find a list of supported values by running `gcc --target-help`. + +For example, if you are using a StarFive VisionFive2, which contains a JH7110 +processor based on the SiFive U74, you can set these flags as follows: + +```make +MARCH := rv64gc_zba_zbb +MCPU := sifive-u74 +``` + +If you prefer a portable build, you could use: + +```make +MARCH := rv64gc + +# also set JULIA_CPU_TARGET to the expanded form of rv64gc +# (it normally copies the value of MCPU, which we don't set) +JULIA_CPU_TARGET := generic-rv64,i,m,a,f,d,zicsr,zifencei,c +``` + +### Cross-compilation + +A native build on a RISC-V device may take a very long time, so it's also +possible to cross-compile Julia on a faster machine. + +First, get a hold of a RISC-V cross-compilation toolchain that provides +support for C, C++ and Fortran. This can be done by checking-out the +[riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) +repository and building it as follows: + +```sh +sudo mkdir /opt/riscv && sudo chown $USER /opt/riscv +./configure --prefix=/opt/riscv --with-languages=c,c++,fortran +make linux -j$(nproc) +``` + +Then, install the QEMU user-mode emulator for RISC-V, along with `binfmt` +support to enable execution of RISC-V binaries on the host machine. The +exact steps depend on your distribution, e.g., on Arch Linux it involves +installing the `qemu-user-static` and `qemu-user-static-binfmt` packages. +Note that to actually execute RISC-V binaries, QEMU will need to be able to +find the RISC-V system root, which can be achieved by setting the +`QEMU_LD_PREFIX` environment variable to the path of the root filesystem. + +Finally, compile Julia with the following `Make.user` variables (in addition to +the ones from the previous section): + +```make +XC_HOST=riscv64-unknown-linux-gnu +OS=Linux +export QEMU_LD_PREFIX=/opt/riscv/sysroot +``` + +Note that you will have to execute `make` with `PATH` set to include the +cross-compilation toolchain, e.g., by running: + +```sh +PATH=/opt/riscv/bin:$PATH make -j$(nproc) +``` + +Because of the RISC-V sysroot we use being very barren, you may need to +add additional libraries that the Julia build system currently expects +to be available system-wide. For example, the build currently relies on +a system-provided `libz`, so you may need to copy this library from the +Julia build into the system root: + +```sh +make -C deps install-zlib +cp -v usr/lib/libz.* /opt/riscv/sysroot/usr/lib +cp -v usr/include/z*.h /opt/riscv/sysroot/usr/include +``` diff --git a/src/abi_riscv.cpp b/src/abi_riscv.cpp new file mode 100644 index 0000000000000..cbd85892801c8 --- /dev/null +++ b/src/abi_riscv.cpp @@ -0,0 +1,315 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +//===----------------------------------------------------------------------===// +// +// The ABI implementation used for RISC-V targets. +// +//===----------------------------------------------------------------------===// +// +// The Procedure Call Standard can be found here: +// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc +// +// This code is based on: +// - The Rust implementation: +// https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/abi/call/riscv.rs +// - The LLVM RISC-V backend: +// https://github.com/llvm/llvm-project/blob/78533528cf5ed04ac78722afff7c9f2f91aa8359/llvm/lib/Target/RISCV/RISCVISelLowering.cpp#L10865 +// +//===----------------------------------------------------------------------===// + + +struct ABI_RiscvLayout : AbiLayout { + +static const size_t XLen = 8; +static const size_t FLen = 8; +static const int NumArgGPRs = 8; +static const int NumArgFPRs = 8; + +// available register num is needed to determine if fp pair or int-fp pair in a struct should be unpacked +// WARN: with this, use_sret must only be called once before the next +// needPassByRef call, otherwise avail_gprs is wrong +int avail_gprs, avail_fprs; + +// preferred type is determined in the same time of use_sret & needPassByRef +// cache it here to avoid computing it again in preferred_llvm_type +Type *cached_llvmtype = NULL; + +ABI_RiscvLayout() : avail_gprs(NumArgGPRs), avail_fprs(NumArgFPRs) {} + +enum RegPassKind { UNKNOWN = 0, INTEGER = 1, FLOAT = 2 }; + +struct ElementType { + RegPassKind type; + jl_datatype_t *dt; + ElementType() : type(RegPassKind::UNKNOWN), dt(NULL) {}; +}; + +bool is_floattype(jl_datatype_t *dt) const +{ + return dt == jl_float16_type || dt == jl_float32_type || dt == jl_float64_type; +} + +Type *get_llvm_fptype(jl_datatype_t *dt, LLVMContext &ctx) const +{ + assert(is_floattype(dt)); + switch (jl_datatype_size(dt)) { + case 2: return Type::getHalfTy(ctx); + case 4: return Type::getFloatTy(ctx); + case 8: return Type::getDoubleTy(ctx); + case 16: return Type::getFP128Ty(ctx); + default: assert(0 && "abi_riscv: unsupported floating point type"); return NULL; + } +} + +// for primitive types that can be passed as integer +// includes integer, bittypes, pointer +Type *get_llvm_inttype(jl_datatype_t *dt, LLVMContext &ctx) const +{ + assert(jl_is_primitivetype(dt)); + // XXX: without Zfh, Float16 is passed in integer registers + if (dt == jl_float16_type) + return Type::getInt32Ty(ctx); + assert(!is_floattype(dt)); + if (dt == jl_bool_type) + return getInt8Ty(ctx); + if (dt == jl_int32_type) + return getInt32Ty(ctx); + if (dt == jl_int64_type) + return getInt64Ty(ctx); + int nb = jl_datatype_size(dt); + return Type::getIntNTy(ctx, nb * 8); +} + +bool should_use_fp_conv(jl_datatype_t *dt, ElementType &ele1, ElementType &ele2) const +{ + if (jl_is_primitivetype(dt)) { + size_t dsz = jl_datatype_size(dt); + if (dsz > FLen) { + return false; + } + if (is_floattype(dt)) { + if (ele1.type == RegPassKind::UNKNOWN) { + ele1.type = RegPassKind::FLOAT; + ele1.dt = dt; + } + else if (ele2.type == RegPassKind::UNKNOWN) { + ele2.type = RegPassKind::FLOAT; + ele2.dt = dt; + } + else { + // 3 elements not eligible, must be a pair + return false; + } + } + // integer or pointer type or bitstypes + else { + if (ele1.type == RegPassKind::UNKNOWN) { + ele1.type = RegPassKind::INTEGER; + ele1.dt = dt; + } + else if (ele1.type == RegPassKind::INTEGER) { + // two integers not eligible + return false; + } + // ele1.type == RegPassKind::FLOAT + else { + if (ele2.type == RegPassKind::UNKNOWN) { + ele2.type = RegPassKind::INTEGER; + ele2.dt = dt; + } + else { + // 3 elements not eligible, must be a pair + return false; + } + } + } + } + else { // aggregates + while (size_t nfields = jl_datatype_nfields(dt)) { + size_t i; + size_t fieldsz; + for (i = 0; i < nfields; i++) { + if ((fieldsz = jl_field_size(dt, i))) { + break; + } + } + assert(i < nfields); + // If there's only one non zero sized member, try again on this member + if (fieldsz == jl_datatype_size(dt)) { + dt = (jl_datatype_t *)jl_field_type(dt, i); + if (!jl_is_datatype(dt)) // could be inline union #46787 + return false; + continue; + } + for (; i < nfields; i++) { + size_t fieldsz = jl_field_size(dt, i); + if (fieldsz == 0) + continue; + jl_datatype_t *fieldtype = (jl_datatype_t *)jl_field_type(dt, i); + if (!jl_is_datatype(dt)) // could be inline union + return false; + // This needs to be done after the zero size member check + if (ele2.type != RegPassKind::UNKNOWN) { + // we already have a pair and can't accept more elements + return false; + } + if (!should_use_fp_conv(fieldtype, ele1, ele2)) { + return false; + } + } + break; + } + } + // Tuple{Int,} can reach here as well, but doesn't really hurt + return true; +} + +Type *get_llvm_inttype_byxlen(size_t xlen, LLVMContext &ctx) const +{ + if (xlen == 8) { + return getInt64Ty(ctx); + } + else if (xlen == 4) { + return getInt32Ty(ctx); + } + else { + assert(0 && "abi_riscv: unsupported xlen"); + return NULL; + } +} + +Type *classify_arg(jl_datatype_t *ty, int &avail_gprs, int &avail_fprs, bool &onstack, + LLVMContext &ctx) const +{ + onstack = false; + if (ty == jl_nothing_type) { + return NULL; + } + ElementType ele1, ele2; + if (should_use_fp_conv(ty, ele1, ele2)) { + if (ele1.type == RegPassKind::FLOAT) { + if (ele2.type == RegPassKind::FLOAT) { + if (avail_fprs >= 2) { + avail_fprs -= 2; + SmallVector eles; + eles.push_back(get_llvm_fptype(ele1.dt, ctx)); + eles.push_back(get_llvm_fptype(ele2.dt, ctx)); + return StructType::get(ctx, eles); + } + } + else if (ele2.type == RegPassKind::INTEGER) { + if (avail_fprs >= 1 && avail_gprs >= 1) { + avail_fprs -= 1; + avail_gprs -= 1; + SmallVector eles; + eles.push_back(get_llvm_fptype(ele1.dt, ctx)); + eles.push_back(get_llvm_inttype(ele2.dt, ctx)); + return StructType::get(ctx, eles); + } + } + else { + // A struct containing just one floating-point real is passed + // as though it were a standalone floating-point real. + if (avail_fprs >= 1) { + avail_fprs -= 1; + return get_llvm_fptype(ele1.dt, ctx); + } + } + } + else if (ele1.type == RegPassKind::INTEGER) { + if (ele2.type == RegPassKind::FLOAT) { + if (avail_fprs >= 1 && avail_gprs >= 1) { + avail_fprs -= 1; + avail_gprs -= 1; + return StructType::get(get_llvm_inttype(ele1.dt, ctx), + get_llvm_fptype(ele2.dt, ctx)); + } + } + } + } + size_t dsz = jl_datatype_size(ty); + if (dsz > 2 * XLen) { + if (!jl_is_primitivetype(ty)) { + onstack = true; + } + // else let llvm backend handle scalars + if (avail_gprs >= 1) { + avail_gprs -= 1; + } + return NULL; + } + + if (dsz > XLen) { + size_t alignment = jl_datatype_align(ty); + bool align_regs = alignment > XLen; + if (avail_gprs >= 2) { + avail_gprs -= 2; + } + // should we handle variadic as well? + // Variadic arguments with 2×XLEN-bit alignment and size at most 2×XLEN + // bits are passed in an aligned register pair + else { + avail_gprs = 0; + } + + if (!jl_is_primitivetype(ty)) { + // Aggregates or scalars passed on the stack are aligned to the + // greater of the type alignment and XLen bits, but never more than + // the stack alignment. + if (align_regs) { + if (alignment == 16) { + return Type::getInt128Ty(ctx); + } + else { + return Type::getInt64Ty(ctx); + } + } + else { + return ArrayType::get(get_llvm_inttype_byxlen(XLen, ctx), 2); + } + } + // let llvm backend handle scalars + return NULL; + } + + //else dsz <= XLen + if (avail_gprs >= 1) { + avail_gprs -= 1; + } + if (!jl_is_primitivetype(ty)) { + return get_llvm_inttype_byxlen(XLen, ctx); + } + return get_llvm_inttype(ty, ctx); +} + +bool use_sret(jl_datatype_t *ty, LLVMContext &ctx) override +{ + bool onstack = false; + int gprs = 2; + int fprs = FLen ? 2 : 0; + this->cached_llvmtype = classify_arg(ty, gprs, fprs, onstack, ctx); + if (onstack) { + this->avail_gprs -= 1; + return true; + } + else { + return false; + } +} + +bool needPassByRef(jl_datatype_t *ty, AttrBuilder &ab, LLVMContext &ctx, + Type *Ty) override +{ + bool onstack = false; + this->cached_llvmtype = + classify_arg(ty, this->avail_gprs, this->avail_fprs, onstack, ctx); + return onstack; +} + +Type *preferred_llvm_type(jl_datatype_t *ty, bool isret, + LLVMContext &ctx) const override +{ + return this->cached_llvmtype; +} + +}; diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c2f112f9c9d5c..279686c387e1b 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1664,7 +1664,8 @@ void jl_dump_native_impl(void *native_code, } CodeModel::Model CMModel = CodeModel::Small; - if (TheTriple.isPPC() || (TheTriple.isX86() && TheTriple.isArch64Bit() && TheTriple.isOSLinux())) { + if (TheTriple.isPPC() || TheTriple.isRISCV() || + (TheTriple.isX86() && TheTriple.isArch64Bit() && TheTriple.isOSLinux())) { // On PPC the small model is limited to 16bit offsets. For very large images the small code model CMModel = CodeModel::Medium; // isn't good enough on x86 so use Medium, it has no cost because only the image goes in .ldata } diff --git a/src/ccall.cpp b/src/ccall.cpp index 2de5be6906e7c..f559ddbe93a43 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -367,6 +367,7 @@ static bool is_native_simd_type(jl_datatype_t *dt) { #include "abi_arm.cpp" #include "abi_aarch64.cpp" +#include "abi_riscv.cpp" #include "abi_ppc64le.cpp" #include "abi_win32.cpp" #include "abi_win64.cpp" @@ -391,6 +392,8 @@ static bool is_native_simd_type(jl_datatype_t *dt) { typedef ABI_ARMLayout DefaultAbiState; #elif defined _CPU_AARCH64_ typedef ABI_AArch64Layout DefaultAbiState; +#elif defined _CPU_RISCV64_ + typedef ABI_RiscvLayout DefaultAbiState; #elif defined _CPU_PPC64_ typedef ABI_PPC64leLayout DefaultAbiState; #else diff --git a/src/codegen.cpp b/src/codegen.cpp index bcda527416676..3f69f4789493a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5368,7 +5368,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos } CallInst *call = ctx.builder.CreateCall(cft, TheCallee, argvals); call->setAttributes(returninfo.attrs); - if (gcstack_arg) + if (gcstack_arg && ctx.emission_context.use_swiftcc) call->setCallingConv(CallingConv::Swift); jl_cgval_t retval; @@ -8186,7 +8186,8 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value if (gcstack_arg){ AttrBuilder param(ctx.builder.getContext()); - param.addAttribute(Attribute::SwiftSelf); + if (ctx.emission_context.use_swiftcc) + param.addAttribute(Attribute::SwiftSelf); param.addAttribute(Attribute::NonNull); attrs.push_back(AttributeSet::get(ctx.builder.getContext(), param)); fsig.push_back(PointerType::get(JuliaType::get_ppjlvalue_ty(ctx.builder.getContext()), 0)); @@ -8278,7 +8279,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value fval = emit_inttoptr(ctx, fval, ftype->getPointerTo()); } if (auto F = dyn_cast(fval)) { - if (gcstack_arg) + if (gcstack_arg && ctx.emission_context.use_swiftcc) F->setCallingConv(CallingConv::Swift); assert(F->arg_size() >= argnames.size()); for (size_t i = 0; i < argnames.size(); i++) { diff --git a/src/disasm.cpp b/src/disasm.cpp index ebe8f2ac397c0..b944e48430c29 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -1058,6 +1058,8 @@ static void jl_dump_asm_internal( if (insSize == 0) // skip illegible bytes #if defined(_CPU_PPC_) || defined(_CPU_PPC64_) || defined(_CPU_ARM_) || defined(_CPU_AARCH64_) insSize = 4; // instructions are always 4 bytes +#elif defined(_CPU_RISCV64_) + insSize = 2; // instructions can be 2 bytes when compressed #else insSize = 1; // attempt to slide 1 byte forward #endif diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 4ff7400df13dd..313449dda5557 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -998,6 +998,16 @@ namespace { #if defined(MSAN_EMUTLS_WORKAROUND) options.EmulatedTLS = true; options.ExplicitEmulatedTLS = true; +#endif +#if defined(_CPU_RISCV64_) + // we set these manually to avoid LLVM defaulting to soft-float +#if defined(__riscv_float_abi_double) + options.MCOptions.ABIName = "lp64d"; +#elif defined(__riscv_float_abi_single) + options.MCOptions.ABIName = "lp64f"; +#else + options.MCOptions.ABIName = "lp64"; +#endif #endif uint32_t target_flags = 0; auto target = jl_get_llvm_target(imaging_default(), target_flags); @@ -1042,11 +1052,23 @@ namespace { #endif if (TheTriple.isAArch64()) codemodel = CodeModel::Small; + else if (TheTriple.isRISCV()) { + // RISC-V will support large code model in LLVM 21 + // https://github.com/llvm/llvm-project/pull/70308 + codemodel = CodeModel::Medium; + } + // Generate simpler code for JIT + Reloc::Model relocmodel = Reloc::Static; + if (TheTriple.isRISCV()) { + // until large code model is supported, use PIC for RISC-V + // https://github.com/llvm/llvm-project/issues/106203 + relocmodel = Reloc::PIC_; + } auto optlevel = CodeGenOptLevelFor(jl_options.opt_level); auto TM = TheTarget->createTargetMachine( TheTriple.getTriple(), TheCPU, FeaturesStr, options, - Reloc::Static, // Generate simpler code for JIT + relocmodel, codemodel, optlevel, true // JIT @@ -1067,7 +1089,7 @@ namespace { .setCPU(TM.getTargetCPU().str()) .setFeatures(TM.getTargetFeatureString()) .setOptions(TM.Options) - .setRelocationModel(Reloc::Static) + .setRelocationModel(TM.getRelocationModel()) .setCodeModel(TM.getCodeModel()) .setCodeGenOptLevel(CodeGenOptLevelFor(optlevel)); } diff --git a/src/jitlayers.h b/src/jitlayers.h index 3353a4093bd27..f4b9a6ea5395a 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -58,6 +58,10 @@ # define JL_USE_JITLINK #endif +#if defined(_CPU_RISCV64_) +# define JL_USE_JITLINK +#endif + # include # include # include @@ -257,9 +261,18 @@ struct jl_codegen_params_t { bool external_linkage = false; bool imaging_mode; int debug_level; + bool use_swiftcc = true; jl_codegen_params_t(orc::ThreadSafeContext ctx, DataLayout DL, Triple triple) - : tsctx(std::move(ctx)), tsctx_lock(tsctx.getLock()), - DL(std::move(DL)), TargetTriple(std::move(triple)), imaging_mode(imaging_default()) {} + : tsctx(std::move(ctx)), + tsctx_lock(tsctx.getLock()), + DL(std::move(DL)), + TargetTriple(std::move(triple)), + imaging_mode(imaging_default()) + { + // LLVM's RISC-V back-end currently does not support the Swift calling convention + if (TargetTriple.isRISCV()) + use_swiftcc = false; + } }; jl_llvm_functions_t jl_emit_code( diff --git a/src/julia_internal.h b/src/julia_internal.h index 20d90fede3d5e..c09bfc5c3eb42 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -105,8 +105,8 @@ JL_DLLIMPORT void __tsan_switch_to_fiber(void *fiber, unsigned flags); #ifndef _OS_WINDOWS_ #if defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_WASM_) #define MAX_ALIGN 8 - #elif defined(_CPU_AARCH64_) || (JL_LLVM_VERSION >= 180000 && (defined(_CPU_X86_64_) || defined(_CPU_X86_))) - // int128 is 16 bytes aligned on aarch64 and on x86 with LLVM >= 18 + #elif defined(_CPU_AARCH64_) || defined(_CPU_RISCV64_) || (JL_LLVM_VERSION >= 180000 && (defined(_CPU_X86_64_) || defined(_CPU_X86_))) + // int128 is 16 bytes aligned on aarch64 and riscv, and on x86 with LLVM >= 18 #define MAX_ALIGN 16 #elif defined(_P64) // Generically we assume MAX_ALIGN is sizeof(void*) @@ -259,6 +259,11 @@ static inline uint64_t cycleclock(void) JL_NOTSAFEPOINT struct timeval tv; gettimeofday(&tv, NULL); return (int64_t)(tv.tv_sec) * 1000000 + tv.tv_usec; +#elif defined(_CPU_RISCV64_) + // taken from https://github.com/google/benchmark/blob/3b3de69400164013199ea448f051d94d7fc7d81f/src/cycleclock.h#L190 + uint64_t ret; + __asm__ volatile("rdcycle %0" : "=r"(ret)); + return ret; #elif defined(_CPU_PPC64_) // This returns a time-base, which is not always precisely a cycle-count. // https://reviews.llvm.org/D78084 diff --git a/src/julia_threads.h b/src/julia_threads.h index b697a0bf030ed..17e8d7d466044 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -56,7 +56,7 @@ typedef struct { !defined(JL_HAVE_ASM) && \ !defined(JL_HAVE_UNW_CONTEXT) #if (defined(_CPU_X86_64_) || defined(_CPU_X86_) || defined(_CPU_AARCH64_) || \ - defined(_CPU_ARM_) || defined(_CPU_PPC64_)) + defined(_CPU_ARM_) || defined(_CPU_PPC64_) || defined(_CPU_RISCV64_)) #define JL_HAVE_ASM #endif #if 0 diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 488dd46cade21..614ed15f840e6 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -117,6 +117,8 @@ Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefor asm_str = "mrs $0, tpidr_el0"; } else if (TargetTriple.isARM()) { asm_str = "mrc p15, 0, $0, c13, c0, 3"; + } else if (TargetTriple.isRISCV()) { + asm_str = "mv $0, tp"; } else if (TargetTriple.getArch() == Triple::x86_64) { asm_str = "movq %fs:0, $0"; } else if (TargetTriple.getArch() == Triple::x86) { diff --git a/src/llvm-version.h b/src/llvm-version.h index 2a38bb7c488b8..984e918d480cc 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -18,6 +18,10 @@ #define JL_LLVM_OPAQUE_POINTERS 1 #endif +#if JL_LLVM_VERSION < 19000 && defined(_CPU_RISCV64_) + #error Only LLVM versions >= 19.0.0 are supported by Julia on RISC-V +#endif + #ifdef __cplusplus #if defined(__GNUC__) && (__GNUC__ >= 9) // Added in GCC 9, this warning is annoying diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index db4007d32035e..450096eef5b01 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -256,7 +256,7 @@ JL_DLLEXPORT float julia_half_to_float(uint16_t param) { #if ((defined(__GNUC__) && __GNUC__ > 11) || \ (defined(__clang__) && __clang_major__ > 14)) && \ !defined(_CPU_PPC64_) && !defined(_CPU_PPC_) && \ - !defined(_OS_WINDOWS_) + !defined(_OS_WINDOWS_) && !defined(_CPU_RISCV64_) #define FLOAT16_TYPE _Float16 #define FLOAT16_TO_UINT16(x) (*(uint16_t*)&(x)) #define FLOAT16_FROM_UINT16(x) (*(_Float16*)&(x)) @@ -355,7 +355,7 @@ float julia_bfloat_to_float(uint16_t param) { #if ((defined(__GNUC__) && __GNUC__ > 12) || \ (defined(__clang__) && __clang_major__ > 16)) && \ !defined(_CPU_PPC64_) && !defined(_CPU_PPC_) && \ - !defined(_OS_WINDOWS_) + !defined(_OS_WINDOWS_) && !defined(_CPU_RISCV64_) #define BFLOAT16_TYPE __bf16 #define BFLOAT16_TO_UINT16(x) (*(uint16_t*)&(x)) #define BFLOAT16_FROM_UINT16(x) (*(__bf16*)&(x)) diff --git a/src/signal-handling.c b/src/signal-handling.c index d7f4697a3c4f0..ce7e8ba57af19 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -256,7 +256,8 @@ static uintptr_t jl_get_pc_from_ctx(const void *_ctx); void jl_show_sigill(void *_ctx); #if defined(_CPU_X86_64_) || defined(_CPU_X86_) \ || (defined(_OS_LINUX_) && defined(_CPU_AARCH64_)) \ - || (defined(_OS_LINUX_) && defined(_CPU_ARM_)) + || (defined(_OS_LINUX_) && defined(_CPU_ARM_)) \ + || (defined(_OS_LINUX_) && defined(_CPU_RISCV64_)) static size_t jl_safe_read_mem(const volatile char *ptr, char *out, size_t len) { jl_jmp_buf *old_buf = jl_get_safe_restore(); @@ -344,6 +345,8 @@ static uintptr_t jl_get_pc_from_ctx(const void *_ctx) return ((ucontext_t*)_ctx)->uc_mcontext.mc_gpregs.gp_elr; #elif defined(_OS_LINUX_) && defined(_CPU_ARM_) return ((ucontext_t*)_ctx)->uc_mcontext.arm_pc; +#elif defined(_OS_LINUX_) && defined(_CPU_RISCV64_) + return ((ucontext_t*)_ctx)->uc_mcontext.__gregs[REG_PC]; #else // TODO for PPC return 0; @@ -421,6 +424,20 @@ void jl_show_sigill(void *_ctx) jl_safe_printf("Invalid ARM instruction at %p: 0x%08" PRIx32 "\n", (void*)pc, inst); } } +#elif defined(_OS_LINUX_) && defined(_CPU_RISCV64_) + uint32_t inst = 0; + size_t len = jl_safe_read_mem(pc, (char*)&inst, 4); + if (len < 2) + jl_safe_printf("Fault when reading instruction: %d bytes read\n", (int)len); + if (inst == 0x00100073 || // ebreak + inst == 0xc0001073 || // unimp (pseudo-instruction for illegal `csrrw x0, cycle, x0`) + (inst & ((1 << 16) - 1)) == 0x0000) { // c.unimp (compressed form) + // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. + jl_safe_printf("Unreachable reached at %p\n", pc); + } + else { + jl_safe_printf("Invalid instruction at %p: 0x%08" PRIx32 "\n", pc, inst); + } #else // TODO for PPC (void)_ctx; diff --git a/src/signals-unix.c b/src/signals-unix.c index f99eca31730b6..caf0e977929c5 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -80,6 +80,9 @@ static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx) #elif defined(_OS_LINUX_) && defined(_CPU_ARM_) const ucontext_t *ctx = (const ucontext_t*)_ctx; return ctx->uc_mcontext.arm_sp; +#elif defined(_OS_LINUX_) && (defined(_CPU_RISCV64_)) + const ucontext_t *ctx = (const ucontext_t*)_ctx; + return ctx->uc_mcontext.__gregs[REG_SP]; #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) const ucontext_t *ctx = (const ucontext_t*)_ctx; return ctx->uc_mcontext.mc_rsp; @@ -175,6 +178,11 @@ JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int si ctx->uc_mcontext.arm_sp = rsp; ctx->uc_mcontext.arm_lr = 0; // Clear link register ctx->uc_mcontext.arm_pc = target; +#elif defined(_OS_LINUX_) && (defined(_CPU_RISCV64_)) + ucontext_t *ctx = (ucontext_t*)_ctx; + ctx->uc_mcontext.__gregs[REG_SP] = rsp; + ctx->uc_mcontext.__gregs[REG_RA] = 0; // Clear return address address (ra) + ctx->uc_mcontext.__gregs[REG_PC] = (uintptr_t)fptr; #else #pragma message("julia: throw-in-context not supported on this platform") // TODO Add support for PowerPC(64)? diff --git a/src/stackwalk.c b/src/stackwalk.c index 5377d091cb780..6784e601bcfba 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1066,6 +1066,44 @@ int jl_simulate_longjmp(jl_jmp_buf mctx, bt_context_t *c) JL_NOTSAFEPOINT mc->regs[0] = 1; assert(mc->sp % 16 == 0); return 1; + #elif defined(_CPU_RISCV64_) + // https://github.com/bminor/glibc/blob/master/sysdeps/riscv/bits/setjmp.h + // https://github.com/llvm/llvm-project/blob/7714e0317520207572168388f22012dd9e152e9e/libunwind/src/Registers.hpp -> Registers_riscv + mc->__gregs[1] = (*_ctx)->__pc; // ra + mc->__gregs[8] = (*_ctx)->__regs[0]; // s0 + mc->__gregs[9] = (*_ctx)->__regs[1]; // s1 + mc->__gregs[18] = (*_ctx)->__regs[2]; // s2 + mc->__gregs[19] = (*_ctx)->__regs[3]; // s3 + mc->__gregs[20] = (*_ctx)->__regs[4]; // s4 + mc->__gregs[21] = (*_ctx)->__regs[5]; // s5 + mc->__gregs[22] = (*_ctx)->__regs[6]; // s6 + mc->__gregs[23] = (*_ctx)->__regs[7]; // s7 + mc->__gregs[24] = (*_ctx)->__regs[8]; // s8 + mc->__gregs[25] = (*_ctx)->__regs[9]; // s9 + mc->__gregs[26] = (*_ctx)->__regs[10]; // s10 + mc->__gregs[27] = (*_ctx)->__regs[11]; // s11 + mc->__gregs[2] = (*_ctx)->__sp; // sp + #ifndef __riscv_float_abi_soft + mc->__fpregs.__d.__f[8] = (unsigned long long) (*_ctx)->__fpregs[0]; // fs0 + mc->__fpregs.__d.__f[9] = (unsigned long long) (*_ctx)->__fpregs[1]; // fs1 + mc->__fpregs.__d.__f[18] = (unsigned long long) (*_ctx)->__fpregs[2]; // fs2 + mc->__fpregs.__d.__f[19] = (unsigned long long) (*_ctx)->__fpregs[3]; // fs3 + mc->__fpregs.__d.__f[20] = (unsigned long long) (*_ctx)->__fpregs[4]; // fs4 + mc->__fpregs.__d.__f[21] = (unsigned long long) (*_ctx)->__fpregs[5]; // fs5 + mc->__fpregs.__d.__f[22] = (unsigned long long) (*_ctx)->__fpregs[6]; // fs6 + mc->__fpregs.__d.__f[23] = (unsigned long long) (*_ctx)->__fpregs[7]; // fs7 + mc->__fpregs.__d.__f[24] = (unsigned long long) (*_ctx)->__fpregs[8]; // fs8 + mc->__fpregs.__d.__f[25] = (unsigned long long) (*_ctx)->__fpregs[9]; // fs9 + mc->__fpregs.__d.__f[26] = (unsigned long long) (*_ctx)->__fpregs[10]; // fs10 + mc->__fpregs.__d.__f[27] = (unsigned long long) (*_ctx)->__fpregs[11]; // fs11 + #endif + // ifdef PTR_DEMANGLE ? + mc->__gregs[REG_SP] = ptr_demangle(mc->__gregs[REG_SP]); + mc->__gregs[REG_RA] = ptr_demangle(mc->__gregs[REG_RA]); + mc->__gregs[REG_PC] = mc->__gregs[REG_RA]; + mc->__gregs[REG_A0] = 1; + assert(mc->__gregs[REG_SP] % 16 == 0); + return 1; #else #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown linux") (void)mc; diff --git a/src/support/platform.h b/src/support/platform.h index a0dd84c9c20b6..816e2090b5a08 100644 --- a/src/support/platform.h +++ b/src/support/platform.h @@ -27,6 +27,7 @@ * _CPU_X86_64_ * _CPU_AARCH64_ * _CPU_ARM_ + * _CPU_RISCV64_ * _CPU_WASM_ */ @@ -106,6 +107,8 @@ #define _CPU_AARCH64_ #elif defined(__arm__) || defined(_M_ARM) #define _CPU_ARM_ +#elif defined(__riscv) && __riscv_xlen == 64 +#define _CPU_RISCV64_ #elif defined(__PPC64__) #define _CPU_PPC64_ #elif defined(_ARCH_PPC) diff --git a/src/task.c b/src/task.c index f86e0ab3a880d..be2631347e82e 100644 --- a/src/task.c +++ b/src/task.c @@ -1491,6 +1491,14 @@ CFI_NORETURN // because all our addresses are word-aligned. " udf #0" // abort : : "r" (stk), "r"(fn) : "memory" ); +#elif defined(_CPU_RISCV64_) + asm volatile( + " mv sp, %0;\n" + " mv ra, zero;\n" // Clear return address register + " mv fp, zero;\n" // Clear frame pointer + " jr %1;\n" // call `fn` with fake stack frame + " ebreak" // abort + : : "r"(stk), "r"(fn) : "memory" ); #elif defined(_CPU_PPC64_) // N.B.: There is two iterations of the PPC64 ABI. // v2 is current and used here. Make sure you have the diff --git a/src/threading.c b/src/threading.c index c26028d2f3da2..50944a24eb29b 100644 --- a/src/threading.c +++ b/src/threading.c @@ -18,7 +18,7 @@ // For variant 1 JL_ELF_TLS_INIT_SIZE is the size of the thread control block (TCB) // For variant 2 JL_ELF_TLS_INIT_SIZE is 0 #if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) -# if defined(_CPU_X86_64_) || defined(_CPU_X86_) +# if defined(_CPU_X86_64_) || defined(_CPU_X86_) || defined(_CPU_RISCV64_) # define JL_ELF_TLS_VARIANT 2 # define JL_ELF_TLS_INIT_SIZE 0 # elif defined(_CPU_AARCH64_) @@ -638,6 +638,8 @@ static void jl_check_tls(void) asm("mrs %0, tpidr_el0" : "=r"(tp)); #elif defined(__ARM_ARCH) && __ARM_ARCH >= 7 asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(tp)); +#elif defined(_CPU_RISCV64_) + asm("mv %0, tp" : "=r"(tp)); #else # error "Cannot emit thread pointer for this architecture." #endif From 8a79822cbab84d8fe05e55cfb515b73c18db754c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:28:37 +0900 Subject: [PATCH 408/548] minor tweak on sysimg.md (#56183) --- doc/src/devdocs/sysimg.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index 64c309e1fb02a..2cbba2744d4a1 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -166,15 +166,17 @@ debug info, respectively, and so will make debugging more difficult. types are not known. All printing should use a specific IO object with a known type. The easiest substitution is to use `print(Core.stdout, x)` instead of `print(x)` or `print(stdout, x)`. -- Use tools like `JET`, `Cthulhu`, and/or `SnoopCompile` to identify failures of type-inference, and - follow our [Performance Tips](@ref) to fix them. +- Use tools like [JET.jl](https://github.com/aviatesk/JET.jl), + [Cthulhu.jl](https://github.com/JuliaDebug/Cthulhu.jl), and/or + [SnoopCompile](https://github.com/timholy/SnoopCompile.jl) + to identify failures of type-inference, and follow our [Performance Tips](@ref) to fix them. ### Compatibility concerns We have identified many small changes to Base that significantly increase the set of programs that can be reliably trimmed. Unfortunately some of those changes would be considered breaking, and so are only applied when trimming is requested (this is done by an external build script, -currently maintained inside the test suite as `test/trimming/buildscript.jl`). +currently maintained inside the test suite as `contrib/juliac-buildscript.jl`). Therefore in many cases trimming will require you to opt in to new variants of Base and some standard libraries. @@ -187,7 +189,7 @@ with trimming as you develop it. Package authors may wish to test that their package is "trimming safe", however this is impossible in general. Trimming is only expected to work given concrete entry points such as `main()` and library entry points meant to be called from outside Julia. For generic packages, existing tests -for type stability like `@inferred` and `JET` are about as close as you can get to checking +for type stability like `@inferred` and `JET.@report_call` are about as close as you can get to checking trim compatibility. Trimming also introduces new compatibility issues between minor versions of Julia. At this time, From f5937b432c51f6b8a5b28eaa5c1583f1350e12a3 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 16 Oct 2024 13:46:22 +0200 Subject: [PATCH 409/548] Remove zero arg methods of `+` and `*` from linalg tests (#56184) There are tests elsewhere that i) make sure there is no zero-arg methods of these functions and ii) tests that e.g. `+()` throws a `MethodError`. Without this patch there are test errors whenever the same test process runs both of these tests. --- stdlib/LinearAlgebra/test/matmul.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 0d1e2776d2bb3..1294e97c2a30c 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1139,8 +1139,8 @@ end Base.zero(::Thing) = Thing(0.) Base.one(::Type{Thing}) = Thing(1.) Base.one(::Thing) = Thing(1.) - Base.:+(t::Thing...) = +(getfield.(t, :data)...) - Base.:*(t::Thing...) = *(getfield.(t, :data)...) + Base.:+(t1::Thing, t::Thing...) = +(getfield.((t1, t...), :data)...) + Base.:*(t1::Thing, t::Thing...) = *(getfield.((t1, t...), :data)...) M = Float64[1 2; 3 4] A = Thing.(M) From b19a7c1721f623ae085354889b183622537543b0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:13:08 +0900 Subject: [PATCH 410/548] optimizer: allow EA-powered `finalizer` inlining (#55954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E.g. this allows `finalizer` inlining in the following case: ```julia mutable struct ForeignBuffer{T} const ptr::Ptr{T} end const foreign_buffer_finalized = Ref(false) function foreign_alloc(::Type{T}, length) where T ptr = Libc.malloc(sizeof(T) * length) ptr = Base.unsafe_convert(Ptr{T}, ptr) obj = ForeignBuffer{T}(ptr) return finalizer(obj) do obj Base.@assume_effects :notaskstate :nothrow foreign_buffer_finalized[] = true Libc.free(obj.ptr) end end function f_EA_finalizer(N::Int) workspace = foreign_alloc(Float64, N) GC.@preserve workspace begin (;ptr) = workspace Base.@assume_effects :nothrow @noinline println(devnull, "ptr = ", ptr) end end ``` ```julia julia> @code_typed f_EA_finalizer(42) CodeInfo( 1 ── %1 = Base.mul_int(8, N)::Int64 │ %2 = Core.lshr_int(%1, 63)::Int64 │ %3 = Core.trunc_int(Core.UInt8, %2)::UInt8 │ %4 = Core.eq_int(%3, 0x01)::Bool └─── goto #3 if not %4 2 ── invoke Core.throw_inexacterror(:convert::Symbol, UInt64::Type, %1::Int64)::Union{} └─── unreachable 3 ── goto #4 4 ── %9 = Core.bitcast(Core.UInt64, %1)::UInt64 └─── goto #5 5 ── goto #6 6 ── goto #7 7 ── goto #8 8 ── %14 = $(Expr(:foreigncall, :(:malloc), Ptr{Nothing}, svec(UInt64), 0, :(:ccall), :(%9), :(%9)))::Ptr{Nothing} └─── goto #9 9 ── %16 = Base.bitcast(Ptr{Float64}, %14)::Ptr{Float64} │ %17 = %new(ForeignBuffer{Float64}, %16)::ForeignBuffer{Float64} └─── goto #10 10 ─ %19 = $(Expr(:gc_preserve_begin, :(%17))) │ %20 = Base.getfield(%17, :ptr)::Ptr{Float64} │ invoke Main.println(Main.devnull::Base.DevNull, "ptr = "::String, %20::Ptr{Float64})::Nothing │ $(Expr(:gc_preserve_end, :(%19))) │ %23 = Main.foreign_buffer_finalized::Base.RefValue{Bool} │ Base.setfield!(%23, :x, true)::Bool │ %25 = Base.getfield(%17, :ptr)::Ptr{Float64} │ %26 = Base.bitcast(Ptr{Nothing}, %25)::Ptr{Nothing} │ $(Expr(:foreigncall, :(:free), Nothing, svec(Ptr{Nothing}), 0, :(:ccall), :(%26), :(%25)))::Nothing └─── return nothing ) => Nothing ``` However, this is still a WIP. Before merging, I want to improve EA's precision a bit and at least fix the test case that is currently marked as `broken`. I also need to check its impact on compiler performance. Additionally, I believe this feature is not yet practical. In particular, there is still significant room for improvement in the following areas: - EA's interprocedural capabilities: currently EA is performed ad-hoc for limited frames because of latency reasons, which significantly reduces its precision in the presence of interprocedural calls. - Relaxing the `:nothrow` check for finalizer inlining: the current algorithm requires `:nothrow`-ness on all paths from the allocation of the mutable struct to its last use, which is not practical for real-world cases. Even when `:nothrow` cannot be guaranteed, auxiliary optimizations such as inserting a `finalize` call after the last use might still be possible (JuliaLang/julia#55990). --- base/compiler/optimize.jl | 2 +- .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 59 +++++--- base/compiler/ssair/passes.jl | 86 +++++++---- base/compiler/types.jl | 2 + test/compiler/EscapeAnalysis/EAUtils.jl | 142 +++++++++++------- test/compiler/codegen.jl | 2 +- test/compiler/inline.jl | 33 ++++ 7 files changed, 225 insertions(+), 101 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 5f0c5077688f8..c5606f80468c0 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -665,7 +665,7 @@ function refine_effects!(interp::AbstractInterpreter, opt::OptimizationState, sv if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending) ir = sv.ir nargs = Int(opt.src.nargs) - estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp)) + estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), get_escape_cache(interp)) argescapes = EscapeAnalysis.ArgEscapeCache(estate) stack_analysis_result!(sv.result, argescapes) validate_mutable_arg_escapes!(estate, sv) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index a0abacb617085..1f98758cd6055 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -18,7 +18,7 @@ import ._TOP_MOD: ==, getindex, setindex! using Core: MethodMatch, SimpleVector, ifelse, sizeof using Core.IR using ._TOP_MOD: # Base definitions - @__MODULE__, @assert, @eval, @goto, @inbounds, @inline, @label, @noinline, + @__MODULE__, @assert, @eval, @goto, @inbounds, @inline, @label, @noinline, @show, @nospecialize, @specialize, BitSet, Callable, Csize_t, IdDict, IdSet, UnitRange, Vector, copy, delete!, empty!, enumerate, error, first, get, get!, haskey, in, isassigned, isempty, ismutabletype, keys, last, length, max, min, missing, pop!, push!, pushfirst!, @@ -657,11 +657,13 @@ function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_e # `escape_exception!` conservatively propagates `AllEscape` anyway, # and so escape information imposed on `:the_exception` isn't computed continue + elseif head === :gc_preserve_begin + # GC preserve is handled by `escape_gc_preserve!` + elseif head === :gc_preserve_end + escape_gc_preserve!(astate, pc, stmt.args) elseif head === :static_parameter || # this exists statically, not interested in its escape - head === :copyast || # XXX can this account for some escapes? - head === :isdefined || # just returns `Bool`, nothing accounts for any escapes - head === :gc_preserve_begin || # `GC.@preserve` expressions themselves won't be used anywhere - head === :gc_preserve_end # `GC.@preserve` expressions themselves won't be used anywhere + head === :copyast || # XXX escape something? + head === :isdefined # just returns `Bool`, nothing accounts for any escapes continue else add_conservative_changes!(astate, pc, stmt.args) @@ -1064,17 +1066,27 @@ end function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) mi = first(args)::MethodInstance first_idx, last_idx = 2, length(args) + add_liveness_changes!(astate, pc, args, first_idx, last_idx) # TODO inspect `astate.ir.stmts[pc][:info]` and use const-prop'ed `InferenceResult` if available cache = astate.get_escape_cache(mi) + ret = SSAValue(pc) if cache isa Bool if cache - return nothing # guaranteed to have no escape + # This method call is very simple and has good effects, so there's no need to + # escape its arguments. However, since the arguments might be returned, we need + # to consider the possibility of aliasing between them and the return value. + for argidx = first_idx:last_idx + arg = args[argidx] + if !is_mutation_free_argtype(argextype(arg, astate.ir)) + add_alias_change!(astate, ret, arg) + end + end + return nothing else return add_conservative_changes!(astate, pc, args, 2) end end cache = cache::ArgEscapeCache - ret = SSAValue(pc) retinfo = astate.estate[ret] # escape information imposed on the call statement method = mi.def::Method nargs = Int(method.nargs) @@ -1162,6 +1174,17 @@ function escape_foreigncall!(astate::AnalysisState, pc::Int, args::Vector{Any}) end end +function escape_gc_preserve!(astate::AnalysisState, pc::Int, args::Vector{Any}) + @assert length(args) == 1 "invalid :gc_preserve_end" + val = args[1] + @assert val isa SSAValue "invalid :gc_preserve_end" + beginstmt = astate.ir[val][:stmt] + @assert isexpr(beginstmt, :gc_preserve_begin) "invalid :gc_preserve_end" + beginargs = beginstmt.args + # COMBAK we might need to add liveness for all statements from `:gc_preserve_begin` to `:gc_preserve_end` + add_liveness_changes!(astate, pc, beginargs) +end + normalize(@nospecialize x) = isa(x, QuoteNode) ? x.value : x function escape_call!(astate::AnalysisState, pc::Int, args::Vector{Any}) @@ -1187,20 +1210,12 @@ function escape_call!(astate::AnalysisState, pc::Int, args::Vector{Any}) if result === missing # if this call hasn't been handled by any of pre-defined handlers, escape it conservatively add_conservative_changes!(astate, pc, args) - return elseif result === true add_liveness_changes!(astate, pc, args, 2) - return # ThrownEscape is already checked + elseif is_nothrow(astate.ir, pc) + add_liveness_changes!(astate, pc, args, 2) else - # we escape statements with the `ThrownEscape` property using the effect-freeness - # computed by `stmt_effect_flags` invoked within inlining - # TODO throwness ≠ "effect-free-ness" - if is_nothrow(astate.ir, pc) - add_liveness_changes!(astate, pc, args, 2) - else - add_fallback_changes!(astate, pc, args, 2) - end - return + add_fallback_changes!(astate, pc, args, 2) end end @@ -1528,4 +1543,12 @@ function escape_array_copy!(astate::AnalysisState, pc::Int, args::Vector{Any}) add_liveness_changes!(astate, pc, args, 6) end +function escape_builtin!(::typeof(Core.finalizer), astate::AnalysisState, pc::Int, args::Vector{Any}) + if length(args) ≥ 3 + obj = args[3] + add_liveness_change!(astate, obj, pc) # TODO setup a proper FinalizerEscape? + end + return false +end + end # baremodule EscapeAnalysis diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 0e2272524a0ed..e3f294c4e91fe 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1300,7 +1300,13 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) # Inlining performs legality checks on the finalizer to determine # whether or not we may inline it. If so, it appends extra arguments # at the end of the intrinsic. Detect that here. - length(stmt.args) == 5 || continue + if length(stmt.args) == 4 && stmt.args[4] === nothing + # constant case + elseif length(stmt.args) == 5 && stmt.args[4] isa Bool && stmt.args[5] isa MethodInstance + # inlining case + else + continue + end end is_finalizer = true elseif isexpr(stmt, :foreigncall) @@ -1685,18 +1691,21 @@ end function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}}, used_ssas::Vector{Int}, lazydomtree::LazyDomtree, inlining::Union{Nothing,InliningState}) 𝕃ₒ = inlining === nothing ? SimpleInferenceLattice.instance : optimizer_lattice(inlining.interp) lazypostdomtree = LazyPostDomtree(ir) - for (defidx, (intermediaries, defuse)) in defuses - # Check if there are any uses we did not account for. If so, the variable - # escapes and we cannot eliminate the allocation. This works, because we're guaranteed - # not to include any intermediaries that have dead uses. As a result, missing uses will only ever - # show up in the nuses_total count. - nleaves = length(defuse.uses) + length(defuse.defs) - nuses = 0 - for iidx in intermediaries - nuses += used_ssas[iidx] + function find_finalizer_useidx(defuse::SSADefUse) + finalizer_useidx = nothing + for (useidx, use) in enumerate(defuse.uses) + if use.kind === :finalizer + # For now: Only allow one finalizer per allocation + finalizer_useidx !== nothing && return false + finalizer_useidx = useidx + end end - nuses_total = used_ssas[defidx] + nuses - length(intermediaries) - nleaves == nuses_total || continue + if finalizer_useidx === nothing || inlining === nothing + return true + end + return finalizer_useidx + end + for (defidx, (intermediaries, defuse)) in defuses # Find the type for this allocation defexpr = ir[SSAValue(defidx)][:stmt] isexpr(defexpr, :new) || continue @@ -1706,22 +1715,47 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}} typ = widenconst(typ) ismutabletype(typ) || continue typ = typ::DataType - # First check for any finalizer calls - finalizer_useidx = nothing - for (useidx, use) in enumerate(defuse.uses) - if use.kind === :finalizer - # For now: Only allow one finalizer per allocation - finalizer_useidx !== nothing && @goto skip - finalizer_useidx = useidx - end + # Check if there are any uses we did not account for. If so, the variable + # escapes and we cannot eliminate the allocation. This works, because we're guaranteed + # not to include any intermediaries that have dead uses. As a result, missing uses will only ever + # show up in the nuses_total count. + nleaves = length(defuse.uses) + length(defuse.defs) + nuses = 0 + for iidx in intermediaries + nuses += used_ssas[iidx] end + nuses_total = used_ssas[defidx] + nuses - length(intermediaries) all_eliminated = all_forwarded = true - if finalizer_useidx !== nothing && inlining !== nothing - finalizer_idx = defuse.uses[finalizer_useidx].idx - try_resolve_finalizer!(ir, defidx, finalizer_idx, defuse, inlining, - lazydomtree, lazypostdomtree, ir[SSAValue(finalizer_idx)][:info]) - deleteat!(defuse.uses, finalizer_useidx) - all_eliminated = all_forwarded = false # can't eliminate `setfield!` calls safely + if nleaves ≠ nuses_total + finalizer_useidx = find_finalizer_useidx(defuse) + if finalizer_useidx isa Int + nargs = length(ir.argtypes) # COMBAK this might need to be `Int(opt.src.nargs)` + estate = EscapeAnalysis.analyze_escapes(ir, nargs, 𝕃ₒ, get_escape_cache(inlining.interp)) + einfo = estate[SSAValue(defidx)] + if EscapeAnalysis.has_no_escape(einfo) + already = BitSet(use.idx for use in defuse.uses) + for idx = einfo.Liveness + if idx ∉ already + push!(defuse.uses, SSAUse(:EALiveness, idx)) + end + end + finalizer_idx = defuse.uses[finalizer_useidx].idx + try_resolve_finalizer!(ir, defidx, finalizer_idx, defuse, inlining::InliningState, + lazydomtree, lazypostdomtree, ir[SSAValue(finalizer_idx)][:info]) + end + end + continue + else + finalizer_useidx = find_finalizer_useidx(defuse) + if finalizer_useidx isa Int + finalizer_idx = defuse.uses[finalizer_useidx].idx + try_resolve_finalizer!(ir, defidx, finalizer_idx, defuse, inlining::InliningState, + lazydomtree, lazypostdomtree, ir[SSAValue(finalizer_idx)][:info]) + deleteat!(defuse.uses, finalizer_useidx) + all_eliminated = all_forwarded = false # can't eliminate `setfield!` calls safely + elseif !finalizer_useidx + continue + end end # Partition defuses by field fielddefuse = SSADefUse[SSADefUse() for _ = 1:fieldcount(typ)] diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 210adf7be96b2..b6c976da48f67 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -457,6 +457,8 @@ typeinf_lattice(::AbstractInterpreter) = InferenceLattice(BaseInferenceLattice.i ipo_lattice(::AbstractInterpreter) = InferenceLattice(IPOResultLattice.instance) optimizer_lattice(::AbstractInterpreter) = SimpleInferenceLattice.instance +get_escape_cache(interp::AbstractInterpreter) = GetNativeEscapeCache(interp) + abstract type CallInfo end @nospecialize diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index c41e61e231892..1f0a84f1a8365 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -6,56 +6,6 @@ const CC = Core.Compiler using ..EscapeAnalysis const EA = EscapeAnalysis -# entries -# ------- - -using Base: IdSet, unwrap_unionall, rewrap_unionall -using InteractiveUtils: gen_call_with_extracted_types_and_kwargs - -""" - @code_escapes [options...] f(args...) - -Evaluates the arguments to the function call, determines its types, and then calls -[`code_escapes`](@ref) on the resulting expression. -As with `@code_typed` and its family, any of `code_escapes` keyword arguments can be given -as the optional arguments like `@code_escapes optimize=false myfunc(myargs...)`. -""" -macro code_escapes(ex0...) - return gen_call_with_extracted_types_and_kwargs(__module__, :code_escapes, ex0) -end - -""" - code_escapes(f, argtypes=Tuple{}; [debuginfo::Symbol = :none], [optimize::Bool = true]) -> result::EscapeResult - -Runs the escape analysis on optimized IR of a generic function call with the given type signature. - -# Keyword Arguments - -- `optimize::Bool = true`: - if `true` returns escape information of post-inlining IR (used for local optimization), - otherwise returns escape information of pre-inlining IR (used for interprocedural escape information generation) -- `debuginfo::Symbol = :none`: - controls the amount of code metadata present in the output, possible options are `:none` or `:source`. -""" -function code_escapes(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); - world::UInt = get_world_counter(), - debuginfo::Symbol = :none) - tt = Base.signature_type(f, types) - match = Base._which(tt; world, raise=true) - mi = Core.Compiler.specialize_method(match)::MethodInstance - interp = EscapeAnalyzer(world, mi) - frame = Core.Compiler.typeinf_frame(interp, mi, #=run_optimizer=#true) - isdefined(interp, :result) || error("optimization didn't happen: maybe everything has been constant folded?") - slotnames = let src = frame.src - src isa CodeInfo ? src.slotnames : nothing - end - return EscapeResult(interp.result.ir, interp.result.estate, interp.result.mi, - slotnames, debuginfo === :source, interp) -end - -# in order to run a whole analysis from ground zero (e.g. for benchmarking, etc.) -__clear_cache!() = empty!(GLOBAL_EA_CODE_CACHE) - # AbstractInterpreter # ------------------- @@ -99,10 +49,10 @@ mutable struct EscapeAnalyzer <: AbstractInterpreter const opt_params::OptimizationParams const inf_cache::Vector{InferenceResult} const escape_cache::EscapeCache - const entry_mi::MethodInstance + const entry_mi::Union{Nothing,MethodInstance} result::EscapeResultForEntry - function EscapeAnalyzer(world::UInt, entry_mi::MethodInstance, - escape_cache::EscapeCache=GLOBAL_ESCAPE_CACHE) + function EscapeAnalyzer(world::UInt, escape_cache::EscapeCache; + entry_mi::Union{Nothing,MethodInstance}=nothing) inf_params = InferenceParams() opt_params = OptimizationParams() inf_cache = InferenceResult[] @@ -115,6 +65,7 @@ CC.OptimizationParams(interp::EscapeAnalyzer) = interp.opt_params CC.get_inference_world(interp::EscapeAnalyzer) = interp.world CC.get_inference_cache(interp::EscapeAnalyzer) = interp.inf_cache CC.cache_owner(::EscapeAnalyzer) = EAToken() +CC.get_escape_cache(interp::EscapeAnalyzer) = GetEscapeCache(interp) function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::OptimizationState, ir::IRCode, caller::InferenceResult) @@ -125,8 +76,9 @@ function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::OptimizationStat estate = try analyze_escapes(ir, nargs, 𝕃ₒ, get_escape_cache) catch err - @error "error happened within EA, inspect `Main.failed_escapeanalysis`" - Main.failed_escapeanalysis = FailedAnalysis(ir, nargs, get_escape_cache) + @error "error happened within EA, inspect `Main.failedanalysis`" + failedanalysis = FailedAnalysis(caller, ir, nargs, get_escape_cache) + Core.eval(Main, :(failedanalysis = $failedanalysis)) rethrow(err) end if caller.linfo === interp.entry_mi @@ -156,6 +108,7 @@ function ((; escape_cache)::GetEscapeCache)(mi::MethodInstance) end struct FailedAnalysis + caller::InferenceResult ir::IRCode nargs::Int get_escape_cache::GetEscapeCache @@ -301,4 +254,83 @@ function print_with_info(preprint, postprint, io::IO, ir::IRCode, source::Bool) return nothing end +# entries +# ------- + +using InteractiveUtils: gen_call_with_extracted_types_and_kwargs + +""" + @code_escapes [options...] f(args...) + +Evaluates the arguments to the function call, determines its types, and then calls +[`code_escapes`](@ref) on the resulting expression. +As with `@code_typed` and its family, any of `code_escapes` keyword arguments can be given +as the optional arguments like `@code_escapes optimize=false myfunc(myargs...)`. +""" +macro code_escapes(ex0...) + return gen_call_with_extracted_types_and_kwargs(__module__, :code_escapes, ex0) +end + +""" + code_escapes(f, argtypes=Tuple{}; [world::UInt], [debuginfo::Symbol]) -> result::EscapeResult + code_escapes(mi::MethodInstance; [world::UInt], [interp::EscapeAnalyzer], [debuginfo::Symbol]) -> result::EscapeResult + +Runs the escape analysis on optimized IR of a generic function call with the given type signature, +while caching the analysis results. + +# Keyword Arguments + +- `world::UInt = Base.get_world_counter()`: + controls the world age to use when looking up methods, use current world age if not specified. +- `interp::EscapeAnalyzer = EscapeAnalyzer(world)`: + specifies the escape analyzer to use, by default a new analyzer with the global cache is created. +- `debuginfo::Symbol = :none`: + controls the amount of code metadata present in the output, possible options are `:none` or `:source`. +""" +function code_escapes(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); + world::UInt = get_world_counter(), + debuginfo::Symbol = :none) + tt = Base.signature_type(f, types) + match = Base._which(tt; world, raise=true) + mi = Core.Compiler.specialize_method(match) + return code_escapes(mi; world, debuginfo) +end + +function code_escapes(mi::MethodInstance; + world::UInt = get_world_counter(), + interp::EscapeAnalyzer=EscapeAnalyzer(world, GLOBAL_ESCAPE_CACHE; entry_mi=mi), + debuginfo::Symbol = :none) + frame = Core.Compiler.typeinf_frame(interp, mi, #=run_optimizer=#true) + isdefined(interp, :result) || error("optimization didn't happen: maybe everything has been constant folded?") + slotnames = let src = frame.src + src isa CodeInfo ? src.slotnames : nothing + end + return EscapeResult(interp.result.ir, interp.result.estate, interp.result.mi, + slotnames, debuginfo === :source, interp) +end + +""" + code_escapes(ir::IRCode, nargs::Int; [world::UInt], [interp::AbstractInterpreter]) -> result::EscapeResult + +Runs the escape analysis on `ir::IRCode`. +`ir` is supposed to be optimized already, specifically after inlining has been applied. +Note that this version does not cache the analysis results. + +# Keyword Arguments + +- `world::UInt = Base.get_world_counter()`: + controls the world age to use when looking up methods, use current world age if not specified. +- `interp::AbstractInterpreter = EscapeAnalyzer(world, EscapeCache())`: + specifies the abstract interpreter to use, by default a new `EscapeAnalyzer` with an empty cache is created. +""" +function code_escapes(ir::IRCode, nargs::Int; + world::UInt = get_world_counter(), + interp::AbstractInterpreter=EscapeAnalyzer(world, EscapeCache())) + estate = analyze_escapes(ir, nargs, CC.optimizer_lattice(interp), CC.get_escape_cache(interp)) + return EscapeResult(ir, estate) # return back the result +end + +# in order to run a whole analysis from ground zero (e.g. for benchmarking, etc.) +__clear_cache!() = empty!(GLOBAL_EA_CODE_CACHE) + end # module EAUtils diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 26ae965b35319..ae04250964554 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -266,7 +266,7 @@ if opt_level > 0 @test occursin("ret $Iptr %\"x::$(Int)\"", load_dummy_ref_ir) end -# Issue 22770 +# Issue JuliaLang/julia#22770 let was_gced = false @noinline make_tuple(x) = tuple(x) @noinline use(x) = ccall(:jl_breakpoint, Cvoid, ()) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 99fed00a7269d..53f7adc2a2a77 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -2249,3 +2249,36 @@ let src = code_typed1(bar_split_error, Tuple{}) @test count(iscall((src, foo_split)), src.code) == 0 @test count(iscall((src, Core.throw_methoderror)), src.code) > 0 end + +# finalizer inlining with EA +mutable struct ForeignBuffer{T} + const ptr::Ptr{T} +end +mutable struct ForeignBufferChecker + @atomic finalized::Bool +end +const foreign_buffer_checker = ForeignBufferChecker(false) +function foreign_alloc(::Type{T}, length) where T + ptr = Libc.malloc(sizeof(T) * length) + ptr = Base.unsafe_convert(Ptr{T}, ptr) + obj = ForeignBuffer{T}(ptr) + return finalizer(obj) do obj + Base.@assume_effects :notaskstate :nothrow + @atomic foreign_buffer_checker.finalized = true + Libc.free(obj.ptr) + end +end +function f_EA_finalizer(N::Int) + workspace = foreign_alloc(Float64, N) + GC.@preserve workspace begin + (;ptr) = workspace + Base.@assume_effects :nothrow @noinline println(devnull, "ptr = ", ptr) + end +end +let src = code_typed1(f_EA_finalizer, (Int,)) + @test count(iscall((src, Core.finalizer)), src.code) == 0 +end +let;Base.Experimental.@force_compile + f_EA_finalizer(42000) + @test foreign_buffer_checker.finalized +end From c07a40f03c67bbd92db65add4964ee7415453ea4 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 16 Oct 2024 09:31:14 -0400 Subject: [PATCH 411/548] Some small follow-ups to stackless compiler (#55972) 1. Adjust the docstring for `Future`, which had its design changed late in that PR and is now confusing. 2. Add additional assertions validating the API assumptions of the `Future` API. I found it too easy to accidentally misuse this and cause hard-to-debug failures. The biggest change is that `isready` accounts for delayed assignments again, which allows an additional invariant that incomplete tasks must always have other pending tasks, allowing for infinite loop detection in the scheduler. 3. A small fix to use the AbstractInterpreter that created the InferenceState for the callback. We haven't fully defined the semantics of mixed-interpreter inference stacks, but downstream packages were using is and this at least makes it mostly work again. --- base/compiler/abstractinterpretation.jl | 5 ++-- base/compiler/inferencestate.jl | 36 +++++++++++++++++-------- base/compiler/ssair/irinterp.jl | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 04a62700e9de7..dbe79e19bf9b4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2681,7 +2681,7 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::Infere end si = StmtInfo(!unused) call = abstract_call(interp, arginfo, si, sv)::Future - Future{Nothing}(call, interp, sv) do call, interp, sv + Future{Any}(call, interp, sv) do call, interp, sv # this only is needed for the side-effect, sequenced before any task tries to consume the return value, # which this will do even without returning this Future sv.stmt_info[sv.currpc] = call.info @@ -2833,7 +2833,7 @@ function abstract_eval_new_opaque_closure(interp::AbstractInterpreter, e::Expr, pushfirst!(argtypes, rt.env) callinfo = abstract_call_opaque_closure(interp, rt, ArgInfo(nothing, argtypes), StmtInfo(true), sv, #=check=#false)::Future - Future{Nothing}(callinfo, interp, sv) do callinfo, interp, sv + Future{Any}(callinfo, interp, sv) do callinfo, interp, sv sv.stmt_info[sv.currpc] = OpaqueClosureCreateInfo(callinfo) nothing end @@ -3775,6 +3775,7 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) takeprev = 0 while takenext >= frame.frameid callee = takenext == 0 ? frame : callstack[takenext]::InferenceState + interp = callee.interp if !isempty(callstack) if length(callstack) - frame.frameid >= minwarn topmethod = callstack[1].linfo diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 5f8fb82caaa34..a200d5ced4d93 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -1128,24 +1128,35 @@ end """ Future{T} -Delayed return value for a value of type `T`, similar to RefValue{T}, but -explicitly represents completed as a `Bool` rather than as `isdefined`. -Set once with `f[] = v` and accessed with `f[]` afterwards. +Assign-once delayed return value for a value of type `T`, similar to RefValue{T}. +Can be constructed in one of three ways: -Can also be constructed with the `completed` flag value and a closure to -produce `x`, as well as the additional arguments to avoid always capturing the -same couple of values. +1. With an immediate as `Future{T}(val)` +2. As an assign-once storage location with `Future{T}()`. Assigned (once) using `f[] = val`. +3. As a delayed computation with `Future{T}(callback, dep, interp, sv)` to have + `sv` arrange to call the `callback` with the result of `dep` when it is ready. + +Use `isready` to check if the value is ready, and `getindex` to get the value. """ struct Future{T} later::Union{Nothing,RefValue{T}} now::Union{Nothing,T} - Future{T}() where {T} = new{T}(RefValue{T}(), nothing) + function Future{T}() where {T} + later = RefValue{T}() + @assert !isassigned(later) "Future{T}() is not allowed for inlinealloc T" + new{T}(later, nothing) + end Future{T}(x) where {T} = new{T}(nothing, x) Future(x::T) where {T} = new{T}(nothing, x) end -isready(f::Future) = f.later === nothing +isready(f::Future) = f.later === nothing || isassigned(f.later) getindex(f::Future{T}) where {T} = (later = f.later; later === nothing ? f.now::T : later[]) -setindex!(f::Future, v) = something(f.later)[] = v +function setindex!(f::Future, v) + later = something(f.later) + @assert !isassigned(later) + later[] = v + return f +end convert(::Type{Future{T}}, x) where {T} = Future{T}(x) # support return type conversion convert(::Type{Future{T}}, x::Future) where {T} = x::Future{T} function Future{T}(f, immediate::Bool, interp::AbstractInterpreter, sv::AbsIntState) where {T} @@ -1176,7 +1187,6 @@ function Future{T}(f, prev::Future{S}, interp::AbstractInterpreter, sv::AbsIntSt end end - """ doworkloop(args...) @@ -1189,12 +1199,16 @@ Each task will be run repeatedly when returning `false`, until it returns `true` function doworkloop(interp::AbstractInterpreter, sv::AbsIntState) tasks = sv.tasks prev = length(tasks) + prevcallstack = length(sv.callstack) prev == 0 && return false task = pop!(tasks) completed = task(interp, sv) tasks = sv.tasks # allow dropping gc root over the previous call completed isa Bool || throw(TypeError(:return, "", Bool, task)) # print the task on failure as part of the error message, instead of just "@ workloop:line" - completed || push!(tasks, task) + if !completed + @assert (length(tasks) >= prev || length(sv.callstack) > prevcallstack) "Task did not complete, but also did not create any child tasks" + push!(tasks, task) + end # efficient post-order visitor: items pushed are executed in reverse post order such # that later items are executed before earlier ones, but are fully executed # (including any dependencies scheduled by them) before going on to the next item diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index ca8ca770df413..f9565f3971733 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -52,7 +52,7 @@ end function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, irsv::IRInterpretationState) si = StmtInfo(true) # TODO better job here? call = abstract_call(interp, arginfo, si, irsv)::Future - Future{Nothing}(call, interp, irsv) do call, interp, irsv + Future{Any}(call, interp, irsv) do call, interp, irsv irsv.ir.stmts[irsv.curridx][:info] = call.info nothing end From b7b79eb8f62a63d1c80fb3fed6cd7b539057ecce Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 16 Oct 2024 14:49:05 -0400 Subject: [PATCH 412/548] Break dependency between loading and Core.Compiler (#56186) This code was originally added in df81bf9a96c59f257a01307cd0ecf05035d8301f where Core.Compiler would keep an array of all the things it inferred, which could then be provieded to the runtime to be included in the package image. In 113efb6e0aa27879cb423ab323c0159911e4c5e7 keeping the array itself became a runtime service for locking considerations. As a result, the role of Core.Compiler here is a bit weird. It has the enable switch and the GC root, but all the actual state is being managed by the runtime. It would be desirable to remove the Core.Compiler reference, so that loading.jl can function even if `Core.Compiler` does not exist (which is in theory supposed to be possible, even though we currently never run in such a configuration; that said, post trimming one might imagine useful instances of such a setup). To do this, put the runtime fully in charge of managing this array. Core.Compiler will call the callback unconditionally for all newly inferred cis and the runtime can decide whether to save it or not. Extracted from #56128 --- base/compiler/cicache.jl | 4 ++++ base/compiler/typeinfer.jl | 10 ---------- base/loading.jl | 13 ++++++++++--- src/staticdata_utils.c | 6 +++++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/base/compiler/cicache.jl b/base/compiler/cicache.jl index a6ed18fe5105f..bf32e8f12f085 100644 --- a/base/compiler/cicache.jl +++ b/base/compiler/cicache.jl @@ -13,6 +13,10 @@ end function setindex!(cache::InternalCodeCache, ci::CodeInstance, mi::MethodInstance) @assert ci.owner === cache.owner + m = mi.def + if isa(m, Method) && m.module != Core + ccall(:jl_push_newly_inferred, Cvoid, (Any,), ci) + end ccall(:jl_mi_cache_insert, Cvoid, (Any, Any), mi, ci) return cache end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 8b85f7c6f35f1..2a3bbf3854302 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1,9 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# Tracking of newly-inferred CodeInstances during precompilation -const track_newly_inferred = RefValue{Bool}(false) -const newly_inferred = CodeInstance[] - """ The module `Core.Compiler.Timings` provides a simple implementation of nested timers that can be used to measure the exclusive time spent inferring each method instance that is @@ -264,12 +260,6 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) if cache_results code_cache(interp)[mi] = result.ci - if track_newly_inferred[] - m = mi.def - if isa(m, Method) && m.module != Core - ccall(:jl_push_newly_inferred, Cvoid, (Any,), result.ci) - end - end end return cache_results end diff --git a/base/loading.jl b/base/loading.jl index 8dff6838c27cc..b396c7897c1fd 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2900,6 +2900,9 @@ function load_path_setup_code(load_path::Bool=true) return code end +# Const global for GC root +const newly_inferred = CodeInstance[] + # this is called in the external process that generates precompiled package files function include_package_for_output(pkg::PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::typeof(_concrete_dependencies), source::Union{Nothing,String}) @@ -2919,8 +2922,7 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto task_local_storage()[:SOURCE_PATH] = source end - ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred) - Core.Compiler.track_newly_inferred.x = true + ccall(:jl_set_newly_inferred, Cvoid, (Any,), newly_inferred) try Base.include(Base.__toplevel__, input) catch ex @@ -2928,10 +2930,15 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto @debug "Aborting `create_expr_cache'" exception=(ErrorException("Declaration of __precompile__(false) not allowed"), catch_backtrace()) exit(125) # we define status = 125 means PrecompileableError finally - Core.Compiler.track_newly_inferred.x = false + ccall(:jl_set_newly_inferred, Cvoid, (Any,), nothing) end # check that the package defined the expected module so we can give a nice error message if not Base.check_package_module_loaded(pkg) + + # Re-populate the runtime's newly-inferred array, which will be included + # in the output. We removed it above to avoid including any code we may + # have compiled for error handling and validation. + ccall(:jl_set_newly_inferred, Cvoid, (Any,), newly_inferred) end function check_package_module_loaded(pkg::PkgId) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 8eb223d3cfbde..5f1095fec9168 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -91,12 +91,16 @@ extern jl_mutex_t world_counter_lock; // This gets called as the first step of Base.include_package_for_output JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t* _newly_inferred) { - assert(_newly_inferred == NULL || jl_is_array(_newly_inferred)); + assert(_newly_inferred == NULL || _newly_inferred == jl_nothing || jl_is_array(_newly_inferred)); + if (_newly_inferred == jl_nothing) + _newly_inferred = NULL; newly_inferred = (jl_array_t*) _newly_inferred; } JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t* ci) { + if (!newly_inferred) + return; JL_LOCK(&newly_inferred_mutex); size_t end = jl_array_nrows(newly_inferred); jl_array_grow_end(newly_inferred, 1); From df5f4375820dd80ba06e819954c5880b153c4d41 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 16 Oct 2024 16:52:24 -0300 Subject: [PATCH 413/548] Implement parallel sweeping of stack pools (#55643) Also use a round robin to only return stacks one thread at a time to avoid contention on munmap syscalls. Using https://github.com/gbaraldi/cilkbench_julia/blob/main/cilk5julia/nqueens.jl as a benchmark it's about 12% faster wall time. This benchmark has other weird behaviours specially single threaded. Where if calls `wait` thousandas of times per second, and if single threaded every single one does a `jl_process_events` call which is a syscall + preemption. So it looks like a hang. With threads the issue isn't there The idea behind the round robin is twofold. One we are just freeing too much and talking with vtjnash we maybe want some less agressive behaviour, the second is that munmap takes a lock in most OSs. So doing it in parallel has severe negative scaling. --- base/timing.jl | 2 ++ src/Makefile | 1 + src/gc-interface.h | 2 ++ src/gc-stacks.c | 55 ++++++++++++++++++--------------- src/gc-stock.c | 77 ++++++++++++++++++++++++++++++++++++++++------ src/gc-stock.h | 7 +++-- src/gc-tls.h | 1 + 7 files changed, 109 insertions(+), 36 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index b094aa230e1c2..4880951f0a32d 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -22,8 +22,10 @@ struct GC_Num total_time_to_safepoint ::Int64 sweep_time ::Int64 mark_time ::Int64 + stack_pool_sweep_time ::Int64 total_sweep_time ::Int64 total_mark_time ::Int64 + total_stack_pool_sweep_time::Int64 last_full_sweep ::Int64 last_incremental_sweep ::Int64 end diff --git a/src/Makefile b/src/Makefile index 4dbb094c65321..75635c2e6c062 100644 --- a/src/Makefile +++ b/src/Makefile @@ -318,6 +318,7 @@ $(BUILDDIR)/debuginfo.o $(BUILDDIR)/debuginfo.dbg.obj: $(addprefix $(SRCDIR)/,de $(BUILDDIR)/disasm.o $(BUILDDIR)/disasm.dbg.obj: $(SRCDIR)/debuginfo.h $(SRCDIR)/processor.h $(BUILDDIR)/gc-debug.o $(BUILDDIR)/gc-debug.dbg.obj: $(SRCDIR)/gc-common.h $(SRCDIR)/gc-stock.h $(BUILDDIR)/gc-pages.o $(BUILDDIR)/gc-pages.dbg.obj: $(SRCDIR)/gc-common.h $(SRCDIR)/gc-stock.h +$(BUILDDIR)/gc-stacks.o $(BUILDDIR)/gc-stacks.dbg.obj: $(SRCDIR)/gc-common.h $(SRCDIR)/gc-stock.h $(BUILDDIR)/gc-stock.o $(BUILDDIR)/gc.dbg.obj: $(SRCDIR)/gc-common.h $(SRCDIR)/gc-stock.h $(SRCDIR)/gc-heap-snapshot.h $(SRCDIR)/gc-alloc-profiler.h $(SRCDIR)/gc-page-profiler.h $(BUILDDIR)/gc-heap-snapshot.o $(BUILDDIR)/gc-heap-snapshot.dbg.obj: $(SRCDIR)/gc-heap-snapshot.h $(BUILDDIR)/gc-alloc-profiler.o $(BUILDDIR)/gc-alloc-profiler.dbg.obj: $(SRCDIR)/gc-alloc-profiler.h diff --git a/src/gc-interface.h b/src/gc-interface.h index e543b4b5879f1..bb2abbe2d36ac 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -44,8 +44,10 @@ typedef struct { uint64_t total_time_to_safepoint; uint64_t sweep_time; uint64_t mark_time; + uint64_t stack_pool_sweep_time; uint64_t total_sweep_time; uint64_t total_mark_time; + uint64_t total_stack_pool_sweep_time; uint64_t last_full_sweep; uint64_t last_incremental_sweep; } jl_gc_num_t; diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 783129ea97693..f6e787a4c1d2d 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -1,6 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include "gc-common.h" +#include "gc-stock.h" #include "threading.h" #ifndef _OS_WINDOWS_ # include @@ -202,7 +203,7 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO return stk; } -void sweep_stack_pools(void) JL_NOTSAFEPOINT +void sweep_stack_pool_loop(void) JL_NOTSAFEPOINT { // Stack sweeping algorithm: // // deallocate stacks if we have too many sitting around unused @@ -215,33 +216,38 @@ void sweep_stack_pools(void) JL_NOTSAFEPOINT // bufsz = t->bufsz // if (stkbuf) // push(free_stacks[sz], stkbuf) - assert(gc_n_threads); - for (int i = 0; i < gc_n_threads; i++) { + jl_atomic_fetch_add(&gc_n_threads_sweeping_stacks, 1); + while (1) { + int i = jl_atomic_fetch_add_relaxed(&gc_ptls_sweep_idx, -1); + if (i < 0) + break; jl_ptls_t ptls2 = gc_all_tls_states[i]; if (ptls2 == NULL) continue; - + assert(gc_n_threads); // free half of stacks that remain unused since last sweep - for (int p = 0; p < JL_N_STACK_POOLS; p++) { - small_arraylist_t *al = &ptls2->gc_tls.heap.free_stacks[p]; - size_t n_to_free; - if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { - n_to_free = al->len; // not alive yet or dead, so it does not need these anymore - } - else if (al->len > MIN_STACK_MAPPINGS_PER_POOL) { - n_to_free = al->len / 2; - if (n_to_free > (al->len - MIN_STACK_MAPPINGS_PER_POOL)) - n_to_free = al->len - MIN_STACK_MAPPINGS_PER_POOL; - } - else { - n_to_free = 0; - } - for (int n = 0; n < n_to_free; n++) { - void *stk = small_arraylist_pop(al); - free_stack(stk, pool_sizes[p]); - } - if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { - small_arraylist_free(al); + if (i == jl_atomic_load_relaxed(&gc_stack_free_idx)) { + for (int p = 0; p < JL_N_STACK_POOLS; p++) { + small_arraylist_t *al = &ptls2->gc_tls.heap.free_stacks[p]; + size_t n_to_free; + if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { + n_to_free = al->len; // not alive yet or dead, so it does not need these anymore + } + else if (al->len > MIN_STACK_MAPPINGS_PER_POOL) { + n_to_free = al->len / 2; + if (n_to_free > (al->len - MIN_STACK_MAPPINGS_PER_POOL)) + n_to_free = al->len - MIN_STACK_MAPPINGS_PER_POOL; + } + else { + n_to_free = 0; + } + for (int n = 0; n < n_to_free; n++) { + void *stk = small_arraylist_pop(al); + free_stack(stk, pool_sizes[p]); + } + if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { + small_arraylist_free(al); + } } } if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { @@ -287,6 +293,7 @@ void sweep_stack_pools(void) JL_NOTSAFEPOINT } live_tasks->len -= ndel; } + jl_atomic_fetch_add(&gc_n_threads_sweeping_stacks, -1); } JL_DLLEXPORT jl_array_t *jl_live_tasks(void) diff --git a/src/gc-stock.c b/src/gc-stock.c index 6b97881909bbd..50a3896d4f9aa 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -24,11 +24,17 @@ int jl_n_sweepthreads; // Number of threads currently running the GC mark-loop _Atomic(int) gc_n_threads_marking; // Number of threads sweeping -_Atomic(int) gc_n_threads_sweeping; +_Atomic(int) gc_n_threads_sweeping_pools; +// Number of threads sweeping stacks +_Atomic(int) gc_n_threads_sweeping_stacks; // Temporary for the `ptls->gc_tls.page_metadata_allocd` used during parallel sweeping (padded to avoid false sharing) _Atomic(jl_gc_padded_page_stack_t *) gc_allocd_scratch; // `tid` of mutator thread that triggered GC _Atomic(int) gc_master_tid; +// counter for sharing work when sweeping stacks +_Atomic(int) gc_ptls_sweep_idx; +// counter for round robin of giving back stack pages to the OS +_Atomic(int) gc_stack_free_idx = 0; // `tid` of first GC thread int gc_first_tid; // Mutex/cond used to synchronize wakeup of GC threads on parallel marking @@ -996,13 +1002,50 @@ STATIC_INLINE void gc_sweep_pool_page(gc_page_profiler_serializer_t *s, jl_gc_pa // sweep over all memory that is being used and not in a pool static void gc_sweep_other(jl_ptls_t ptls, int sweep_full) JL_NOTSAFEPOINT { - sweep_stack_pools(); gc_sweep_foreign_objs(); sweep_malloced_memory(); sweep_big(ptls); jl_engine_sweep(gc_all_tls_states); } +// wake up all threads to sweep the stacks +void gc_sweep_wake_all_stacks(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + uv_mutex_lock(&gc_threads_lock); + int first = gc_first_parallel_collector_thread_id(); + int last = gc_last_parallel_collector_thread_id(); + for (int i = first; i <= last; i++) { + jl_ptls_t ptls2 = gc_all_tls_states[i]; + gc_check_ptls_of_parallel_collector_thread(ptls2); + jl_atomic_fetch_add(&ptls2->gc_tls.gc_stack_sweep_requested, 1); + } + uv_cond_broadcast(&gc_threads_cond); + uv_mutex_unlock(&gc_threads_lock); + return; +} + +void gc_sweep_wait_for_all_stacks(void) JL_NOTSAFEPOINT +{ + while ((jl_atomic_load_acquire(&gc_ptls_sweep_idx) >= 0 ) || jl_atomic_load_acquire(&gc_n_threads_sweeping_stacks) != 0) { + jl_cpu_pause(); + } +} + +void sweep_stack_pools(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + // initialize ptls index for parallel sweeping of stack pools + assert(gc_n_threads); + int stack_free_idx = jl_atomic_load_relaxed(&gc_stack_free_idx); + if (stack_free_idx + 1 == gc_n_threads) + jl_atomic_store_relaxed(&gc_stack_free_idx, 0); + else + jl_atomic_store_relaxed(&gc_stack_free_idx, stack_free_idx + 1); + jl_atomic_store_release(&gc_ptls_sweep_idx, gc_n_threads - 1); // idx == gc_n_threads = release stacks to the OS so it's serial + gc_sweep_wake_all_stacks(ptls); + sweep_stack_pool_loop(); + gc_sweep_wait_for_all_stacks(); +} + static void gc_pool_sync_nfree(jl_gc_pagemeta_t *pg, jl_taggedvalue_t *last) JL_NOTSAFEPOINT { assert(pg->fl_begin_offset != UINT16_MAX); @@ -1078,7 +1121,7 @@ int gc_sweep_prescan(jl_ptls_t ptls, jl_gc_padded_page_stack_t *new_gc_allocd_sc } // wake up all threads to sweep the pages -void gc_sweep_wake_all(jl_ptls_t ptls, jl_gc_padded_page_stack_t *new_gc_allocd_scratch) +void gc_sweep_wake_all_pages(jl_ptls_t ptls, jl_gc_padded_page_stack_t *new_gc_allocd_scratch) { int parallel_sweep_worthwhile = gc_sweep_prescan(ptls, new_gc_allocd_scratch); if (parallel_sweep_worthwhile && !page_profile_enabled) { @@ -1114,10 +1157,10 @@ void gc_sweep_wake_all(jl_ptls_t ptls, jl_gc_padded_page_stack_t *new_gc_allocd_ } // wait for all threads to finish sweeping -void gc_sweep_wait_for_all(void) +void gc_sweep_wait_for_all_pages(void) { jl_atomic_store(&gc_allocd_scratch, NULL); - while (jl_atomic_load_acquire(&gc_n_threads_sweeping) != 0) { + while (jl_atomic_load_acquire(&gc_n_threads_sweeping_pools) != 0) { jl_cpu_pause(); } } @@ -1125,7 +1168,7 @@ void gc_sweep_wait_for_all(void) // sweep all pools void gc_sweep_pool_parallel(jl_ptls_t ptls) { - jl_atomic_fetch_add(&gc_n_threads_sweeping, 1); + jl_atomic_fetch_add(&gc_n_threads_sweeping_pools, 1); jl_gc_padded_page_stack_t *allocd_scratch = jl_atomic_load(&gc_allocd_scratch); if (allocd_scratch != NULL) { gc_page_profiler_serializer_t serializer = gc_page_serializer_create(); @@ -1170,7 +1213,7 @@ void gc_sweep_pool_parallel(jl_ptls_t ptls) } gc_page_serializer_destroy(&serializer); } - jl_atomic_fetch_add(&gc_n_threads_sweeping, -1); + jl_atomic_fetch_add(&gc_n_threads_sweeping_pools, -1); } // free all pages (i.e. through `madvise` on Linux) that were lazily freed @@ -1260,9 +1303,9 @@ static void gc_sweep_pool(void) // the actual sweeping jl_gc_padded_page_stack_t *new_gc_allocd_scratch = (jl_gc_padded_page_stack_t *) calloc_s(n_threads * sizeof(jl_gc_padded_page_stack_t)); jl_ptls_t ptls = jl_current_task->ptls; - gc_sweep_wake_all(ptls, new_gc_allocd_scratch); + gc_sweep_wake_all_pages(ptls, new_gc_allocd_scratch); gc_sweep_pool_parallel(ptls); - gc_sweep_wait_for_all(); + gc_sweep_wait_for_all_pages(); // reset half-pages pointers for (int t_i = 0; t_i < n_threads; t_i++) { @@ -3073,6 +3116,11 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) #endif current_sweep_full = sweep_full; sweep_weak_refs(); + uint64_t stack_pool_time = jl_hrtime(); + sweep_stack_pools(ptls); + stack_pool_time = jl_hrtime() - stack_pool_time; + gc_num.total_stack_pool_sweep_time += stack_pool_time; + gc_num.stack_pool_sweep_time = stack_pool_time; gc_sweep_other(ptls, sweep_full); gc_scrub(); gc_verify_tags(); @@ -3491,6 +3539,10 @@ STATIC_INLINE int may_sweep(jl_ptls_t ptls) JL_NOTSAFEPOINT return (jl_atomic_load(&ptls->gc_tls.gc_sweeps_requested) > 0); } +STATIC_INLINE int may_sweep_stack(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + return (jl_atomic_load(&ptls->gc_tls.gc_stack_sweep_requested) > 0); +} // parallel gc thread function void jl_parallel_gc_threadfun(void *arg) { @@ -3513,12 +3565,17 @@ void jl_parallel_gc_threadfun(void *arg) while (1) { uv_mutex_lock(&gc_threads_lock); - while (!may_mark() && !may_sweep(ptls)) { + while (!may_mark() && !may_sweep(ptls) && !may_sweep_stack(ptls)) { uv_cond_wait(&gc_threads_cond, &gc_threads_lock); } uv_mutex_unlock(&gc_threads_lock); assert(jl_atomic_load_relaxed(&ptls->gc_state) == JL_GC_PARALLEL_COLLECTOR_THREAD); gc_mark_loop_parallel(ptls, 0); + if (may_sweep_stack(ptls)) { + assert(jl_atomic_load_relaxed(&ptls->gc_state) == JL_GC_PARALLEL_COLLECTOR_THREAD); + sweep_stack_pool_loop(); + jl_atomic_fetch_add(&ptls->gc_tls.gc_stack_sweep_requested, -1); + } if (may_sweep(ptls)) { assert(jl_atomic_load_relaxed(&ptls->gc_state) == JL_GC_PARALLEL_COLLECTOR_THREAD); gc_sweep_pool_parallel(ptls); diff --git a/src/gc-stock.h b/src/gc-stock.h index 46f7d3e11e105..76cecf68067bf 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -524,7 +524,10 @@ extern uv_mutex_t gc_threads_lock; extern uv_cond_t gc_threads_cond; extern uv_sem_t gc_sweep_assists_needed; extern _Atomic(int) gc_n_threads_marking; -extern _Atomic(int) gc_n_threads_sweeping; +extern _Atomic(int) gc_n_threads_sweeping_pools; +extern _Atomic(int) gc_n_threads_sweeping_stacks; +extern _Atomic(int) gc_ptls_sweep_idx; +extern _Atomic(int) gc_stack_free_idx; extern _Atomic(int) n_threads_running; extern uv_barrier_t thread_init_done; void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq); @@ -535,7 +538,7 @@ void gc_mark_loop_serial(jl_ptls_t ptls); void gc_mark_loop_parallel(jl_ptls_t ptls, int master); void gc_sweep_pool_parallel(jl_ptls_t ptls); void gc_free_pages(void); -void sweep_stack_pools(void) JL_NOTSAFEPOINT; +void sweep_stack_pool_loop(void) JL_NOTSAFEPOINT; void jl_gc_debug_init(void); // GC pages diff --git a/src/gc-tls.h b/src/gc-tls.h index 9e4b09404db84..3c2cc029a6183 100644 --- a/src/gc-tls.h +++ b/src/gc-tls.h @@ -82,6 +82,7 @@ typedef struct { jl_gc_markqueue_t mark_queue; jl_gc_mark_cache_t gc_cache; _Atomic(size_t) gc_sweeps_requested; + _Atomic(size_t) gc_stack_sweep_requested; arraylist_t sweep_objs; } jl_gc_tls_states_t; From 12aa9dea03a8a6bbe2891d3ca6ce34a21ec84734 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 16 Oct 2024 13:54:03 -0700 Subject: [PATCH 414/548] add fenv cache to task struct (#51288) Fixes #51277, though we give no guarantee that it keeps working this way, or that calling `setrounding_raw` won't lead to other undefined behavior. To give some examples: ```julia julia> t = Base.Rounding.setrounding_raw(Float64, Base.Rounding.to_fenv(RoundDown)) do Task(() -> println(rounding(Float64))) end Task (runnable) @0x000000010dff04c0 julia> rounding(Float64) RoundingMode{:Nearest}() julia> wait(schedule(t)) RoundingMode{:Down}() # currently gives RoundingMode{:Nearest}() julia> rounding(Float64) RoundingMode{:Nearest}() julia> Base.Rounding.setrounding_raw(Float64, Base.Rounding.to_fenv(RoundDown)) do Threads.@threads :static for i = 1:Threads.nthreads() println(Threads.threadid() => rounding(Float64)) end end 1 => RoundingMode{:Down}() 2 => RoundingMode{:Down}() # currently gives RoundingMode{:Nearest}() 4 => RoundingMode{:Down}() # currently gives RoundingMode{:Nearest}() 3 => RoundingMode{:Down}() # currently gives RoundingMode{:Nearest}() ``` --- src/julia.h | 4 ++++ src/task.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/julia.h b/src/julia.h index ed3d9bf825658..46679da9714dc 100644 --- a/src/julia.h +++ b/src/julia.h @@ -20,6 +20,7 @@ #include "libsupport.h" #include #include +#include #include "htable.h" #include "arraylist.h" @@ -2282,6 +2283,9 @@ typedef struct _jl_task_t { uint16_t priority; // hidden state: + // cached floating point environment + // only updated at task switch + fenv_t fenv; // id of owning thread - does not need to be defined until the task runs _Atomic(int16_t) tid; diff --git a/src/task.c b/src/task.c index be2631347e82e..5e1172a96a409 100644 --- a/src/task.c +++ b/src/task.c @@ -534,6 +534,7 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) jl_set_pgcstack(&t->gcstack); jl_signal_fence(); lastt->ptls = NULL; + fegetenv(&lastt->fenv); #ifdef MIGRATE_TASKS ptls->previous_task = lastt; #endif @@ -726,6 +727,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER 0 == ptls->finalizers_inhibited); ptls->finalizers_inhibited = finalizers_inhibited; jl_timing_block_task_enter(ct, ptls, blk); (void)blk; + fesetenv(&ct->fenv); sig_atomic_t other_defer_signal = ptls->defer_signal; ptls->defer_signal = defer_signal; @@ -1138,6 +1140,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->excstack = NULL; t->ctx.started = 0; t->priority = 0; + fegetenv(&t->fenv); jl_atomic_store_relaxed(&t->tid, -1); t->threadpoolid = ct->threadpoolid; t->ptls = NULL; @@ -1239,6 +1242,7 @@ CFI_NORETURN if (!pt->sticky && !pt->ctx.copy_stack) jl_atomic_store_release(&pt->tid, -1); #endif + fesetenv(&ct->fenv); ct->ctx.started = 1; JL_PROBE_RT_START_TASK(ct); From 5c3f477fe0570760a34eb9ea91b0ee5dfabe69b5 Mon Sep 17 00:00:00 2001 From: spaette <111918424+spaette@users.noreply.github.com> Date: Thu, 17 Oct 2024 03:23:10 +0200 Subject: [PATCH 415/548] url relocation LinearAlgebra markdown (#56202) _cf_: https://github.com/JuliaLang/julia/issues/56147 .html and .pdf rendering may be resolved for this content the ticket's mentioned build.md file was left unattended to --- stdlib/LinearAlgebra/docs/src/index.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 967ef8237f03d..e3e79b7034969 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -683,11 +683,9 @@ and the complexity of the operation. ### Level 1 BLAS functions -The level 1 BLAS functions were first proposed in [(Lawson, 1979)][Lawson-1979] and +The level 1 BLAS functions were first proposed in ([Lawson, 1979](https://dl.acm.org/doi/10.1145/355841.355847)) and define operations between scalars and vectors. -[Lawson-1979]: https://dl.acm.org/doi/10.1145/355841.355847 - ```@docs # xROTG # xROTMG @@ -710,11 +708,9 @@ LinearAlgebra.BLAS.iamax ### Level 2 BLAS functions -The level 2 BLAS functions were published in [(Dongarra, 1988)][Dongarra-1988], +The level 2 BLAS functions were published in ([Dongarra, 1988](https://dl.acm.org/doi/10.1145/42288.42291)) and define matrix-vector operations. -[Dongarra-1988]: https://dl.acm.org/doi/10.1145/42288.42291 - **return a vector** ```@docs @@ -763,11 +759,9 @@ LinearAlgebra.BLAS.spr! ### Level 3 BLAS functions -The level 3 BLAS functions were published in [(Dongarra, 1990)][Dongarra-1990], +The level 3 BLAS functions were published in ([Dongarra, 1990](https://dl.acm.org/doi/10.1145/77626.79170)) and define matrix-matrix operations. -[Dongarra-1990]: https://dl.acm.org/doi/10.1145/77626.79170 - ```@docs LinearAlgebra.BLAS.gemmt! LinearAlgebra.BLAS.gemmt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any) From e25287781adb6fec59d9cecccd1a47b3edcd3d03 Mon Sep 17 00:00:00 2001 From: TacHawkes Date: Thu, 17 Oct 2024 06:51:45 +0200 Subject: [PATCH 416/548] Update stable release tag to 1.11.1 in README.md (#56205) This still points to 1.10 and should be updated. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 465adcf049922..cfa2111600f22 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.10.5 + git checkout v1.11.1 To build the `julia` executable, run `make` from within the julia directory. From 222cde9c092e143def9ae8238b529aa8e63f902d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 17 Oct 2024 01:19:46 -0400 Subject: [PATCH 417/548] Split reflection into compiler-dependent and compiler-independent pieces (#56185) The `reflection.jl` file provides a large amount of functionality covering everything from helpers for access to core runtime data structures to setting up particular inference problems. It is included by both Base and Core.Compiler, but the functions that use the compiler, don't really make sense in the latter. In preparation for #56128, and stop including the compiler-dependent pieces in Core.Compiler. While we're here, also move a few generically useful reflection functions out of Core.Compiler, so users that access them don't have to load the compiler. Split out from #56128, but doesn't make any semantic changes by itself, so should be quick/easy to merge. --- base/Base.jl | 1 + base/compiler/compiler.jl | 2 +- base/compiler/typeutils.jl | 2 - base/compiler/utilities.jl | 84 -- base/expr.jl | 14 + base/reflection.jl | 1566 ++---------------------------------- base/runtime_internals.jl | 1530 +++++++++++++++++++++++++++++++++++ test/compiler/inference.jl | 2 +- 8 files changed, 1601 insertions(+), 1600 deletions(-) create mode 100644 base/runtime_internals.jl diff --git a/base/Base.jl b/base/Base.jl index 84e10ca788ba2..1e780bb15141a 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -173,6 +173,7 @@ include("essentials.jl") include("ctypes.jl") include("gcutils.jl") include("generator.jl") +include("runtime_internals.jl") include("reflection.jl") include("options.jl") diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 5cc01391267d7..dbfc9d7d57140 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -84,7 +84,7 @@ const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h include("essentials.jl") include("ctypes.jl") include("generator.jl") -include("reflection.jl") +include("runtime_internals.jl") include("options.jl") ntuple(f, ::Val{0}) = () diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 577452a873b5e..4af8fed0e40c3 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -202,8 +202,6 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I return a # TODO: improve this bound? end -hasintersect(@nospecialize(a), @nospecialize(b)) = typeintersect(a, b) !== Bottom - _typename(@nospecialize a) = Union{} _typename(a::TypeVar) = Core.TypeName function _typename(a::Union) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index b3dfd73d53452..f9202788b6360 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -48,24 +48,6 @@ anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i in 1:length(a) ] _topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module -####### -# AST # -####### - -# Meta expression head, these generally can't be deleted even when they are -# in a dead branch but can be ignored when analyzing uses/liveness. -is_meta_expr_head(head::Symbol) = head === :boundscheck || head === :meta || head === :loopinfo -is_meta_expr(@nospecialize x) = isa(x, Expr) && is_meta_expr_head(x.head) - -function is_self_quoting(@nospecialize(x)) - return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) || - isa(x,Char) || x === nothing || isa(x,Function) -end - -function quoted(@nospecialize(x)) - return is_self_quoting(x) ? x : QuoteNode(x) -end - ############ # inlining # ############ @@ -116,10 +98,6 @@ function is_inlineable_constant(@nospecialize(x)) return count_const_size(x) <= MAX_INLINE_CONST_SIZE end -is_nospecialized(method::Method) = method.nospecialize ≠ 0 - -is_nospecializeinfer(method::Method) = method.nospecializeinfer && is_nospecialized(method) - ########################### # MethodInstance/CodeInfo # ########################### @@ -192,74 +170,12 @@ function get_compileable_sig(method::Method, @nospecialize(atype), sparams::Simp mt, atype, sparams, method, #=int return_if_compileable=#1) end -function get_nospecializeinfer_sig(method::Method, @nospecialize(atype), sparams::SimpleVector) - isa(atype, DataType) || return method.sig - mt = ccall(:jl_method_get_table, Any, (Any,), method) - mt === nothing && return method.sig - return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any, Cint), - mt, atype, sparams, method, #=int return_if_compileable=#0) -end isa_compileable_sig(@nospecialize(atype), sparams::SimpleVector, method::Method) = !iszero(ccall(:jl_isa_compileable_sig, Int32, (Any, Any, Any), atype, sparams, method)) -# eliminate UnionAll vars that might be degenerate due to having identical bounds, -# or a concrete upper bound and appearing covariantly. -function subst_trivial_bounds(@nospecialize(atype)) - if !isa(atype, UnionAll) - return atype - end - v = atype.var - if isconcretetype(v.ub) || v.lb === v.ub - subst = try - atype{v.ub} - catch - # Note in rare cases a var bound might not be valid to substitute. - nothing - end - if subst !== nothing - return subst_trivial_bounds(subst) - end - end - return UnionAll(v, subst_trivial_bounds(atype.body)) -end - has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v) != 0 -# If removing trivial vars from atype results in an equivalent type, use that -# instead. Otherwise we can get a case like issue #38888, where a signature like -# f(x::S) where S<:Int -# gets cached and matches a concrete dispatch case. -function normalize_typevars(method::Method, @nospecialize(atype), sparams::SimpleVector) - at2 = subst_trivial_bounds(atype) - if at2 !== atype && at2 == atype - atype = at2 - sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), at2, method.sig)::SimpleVector - sparams = sp_[2]::SimpleVector - end - return Pair{Any,SimpleVector}(atype, sparams) -end - -# get a handle to the unique specialization object representing a particular instantiation of a call -@inline function specialize_method(method::Method, @nospecialize(atype), sparams::SimpleVector; preexisting::Bool=false) - if isa(atype, UnionAll) - atype, sparams = normalize_typevars(method, atype, sparams) - end - if is_nospecializeinfer(method) - atype = get_nospecializeinfer_sig(method, atype, sparams) - end - if preexisting - # check cached specializations - # for an existing result stored there - return ccall(:jl_specializations_lookup, Any, (Any, Any), method, atype)::Union{Nothing,MethodInstance} - end - return ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), method, atype, sparams) -end - -function specialize_method(match::MethodMatch; kwargs...) - return specialize_method(match.method, match.spec_types, match.sparams; kwargs...) -end - """ is_declared_inline(method::Method) -> Bool diff --git a/base/expr.jl b/base/expr.jl index 478ccd7d7cc20..723b6b5b636c8 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -1560,3 +1560,17 @@ function make_atomiconce(success_order, fail_order, ex) end error("@atomiconce expression missing field access or indexing") end + +# Meta expression head, these generally can't be deleted even when they are +# in a dead branch but can be ignored when analyzing uses/liveness. +is_meta_expr_head(head::Symbol) = head === :boundscheck || head === :meta || head === :loopinfo +is_meta_expr(@nospecialize x) = isa(x, Expr) && is_meta_expr_head(x.head) + +function is_self_quoting(@nospecialize(x)) + return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) || + isa(x,Char) || x === nothing || isa(x,Function) +end + +function quoted(@nospecialize(x)) + return is_self_quoting(x) ? x : QuoteNode(x) +end diff --git a/base/reflection.jl b/base/reflection.jl index 49d640ea40bab..8fe8d324eb792 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1,1186 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# name and module reflection - -""" - parentmodule(m::Module) -> Module - -Get a module's enclosing `Module`. `Main` is its own parent. - -See also: [`names`](@ref), [`nameof`](@ref), [`fullname`](@ref), [`@__MODULE__`](@ref). - -# Examples -```jldoctest -julia> parentmodule(Main) -Main - -julia> parentmodule(Base.Broadcast) -Base -``` -""" -parentmodule(m::Module) = (@_total_meta; ccall(:jl_module_parent, Ref{Module}, (Any,), m)) - -is_root_module(m::Module) = parentmodule(m) === m || (isdefined(Main, :Base) && m === Main.Base) - -""" - moduleroot(m::Module) -> Module - -Find the root module of a given module. This is the first module in the chain of -parent modules of `m` which is either a registered root module or which is its -own parent module. -""" -function moduleroot(m::Module) - @_total_meta - while true - is_root_module(m) && return m - p = parentmodule(m) - p === m && return m - m = p - end -end - -""" - @__MODULE__ -> Module - -Get the `Module` of the toplevel eval, -which is the `Module` code is currently being read from. -""" -macro __MODULE__() - return __module__ -end - -""" - fullname(m::Module) - -Get the fully-qualified name of a module as a tuple of symbols. For example, - -# Examples -```jldoctest -julia> fullname(Base.Iterators) -(:Base, :Iterators) - -julia> fullname(Main) -(:Main,) -``` -""" -function fullname(m::Module) - @_total_meta - mn = nameof(m) - if m === Main || m === Base || m === Core - return (mn,) - end - mp = parentmodule(m) - if mp === m - return (mn,) - end - return (fullname(mp)..., mn) -end - -""" - moduleloc(m::Module) -> LineNumberNode - -Get the location of the `module` definition. -""" -function moduleloc(m::Module) - line = Ref{Int32}(0) - file = ccall(:jl_module_getloc, Ref{Symbol}, (Any, Ref{Int32}), m, line) - return LineNumberNode(Int(line[]), file) -end - -""" - names(x::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) -> Vector{Symbol} - -Get a vector of the public names of a `Module`, excluding deprecated names. -If `all` is true, then the list also includes non-public names defined in the module, -deprecated names, and compiler-generated names. -If `imported` is true, then names explicitly imported from other modules -are also included. -If `usings` is true, then names explicitly imported via `using` are also included. -Names are returned in sorted order. - -As a special case, all names defined in `Main` are considered \"public\", -since it is not idiomatic to explicitly mark names from `Main` as public. - -!!! note - `sym ∈ names(SomeModule)` does *not* imply `isdefined(SomeModule, sym)`. - `names` may return symbols marked with `public` or `export`, even if - they are not defined in the module. - -!!! warning - `names` may return duplicate names. The duplication happens, e.g. if an `import`ed name - conflicts with an already existing identifier. - -See also: [`Base.isexported`](@ref), [`Base.ispublic`](@ref), [`Base.@locals`](@ref), [`@__MODULE__`](@ref). -""" -names(m::Module; kwargs...) = sort!(unsorted_names(m; kwargs...)) -unsorted_names(m::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) = - ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint, Cint), m, all, imported, usings) - -""" - isexported(m::Module, s::Symbol) -> Bool - -Returns whether a symbol is exported from a module. - -See also: [`ispublic`](@ref), [`names`](@ref) - -```jldoctest -julia> module Mod - export foo - public bar - end -Mod - -julia> Base.isexported(Mod, :foo) -true - -julia> Base.isexported(Mod, :bar) -false - -julia> Base.isexported(Mod, :baz) -false -``` -""" -isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0 - -""" - ispublic(m::Module, s::Symbol) -> Bool - -Returns whether a symbol is marked as public in a module. - -Exported symbols are considered public. - -!!! compat "Julia 1.11" - This function and the notion of publicity were added in Julia 1.11. - -See also: [`isexported`](@ref), [`names`](@ref) - -```jldoctest -julia> module Mod - export foo - public bar - end -Mod - -julia> Base.ispublic(Mod, :foo) -true - -julia> Base.ispublic(Mod, :bar) -true - -julia> Base.ispublic(Mod, :baz) -false -``` -""" -ispublic(m::Module, s::Symbol) = ccall(:jl_module_public_p, Cint, (Any, Any), m, s) != 0 - -# TODO: this is vaguely broken because it only works for explicit calls to -# `Base.deprecate`, not the @deprecated macro: -isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 - -""" - isbindingresolved(m::Module, s::Symbol) -> Bool - -Returns whether the binding of a symbol in a module is resolved. - -See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref) - -```jldoctest -julia> module Mod - foo() = 17 - end -Mod - -julia> Base.isbindingresolved(Mod, :foo) -true - -julia> Base.isbindingresolved(Mod, :bar) -false -``` -""" -isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0 - -function binding_module(m::Module, s::Symbol) - p = ccall(:jl_get_module_of_binding, Ptr{Cvoid}, (Any, Any), m, s) - p == C_NULL && return m - return unsafe_pointer_to_objref(p)::Module -end - -const _NAMEDTUPLE_NAME = NamedTuple.body.body.name - -function _fieldnames(@nospecialize t) - if t.name === _NAMEDTUPLE_NAME - if t.parameters[1] isa Tuple - return t.parameters[1] - else - throw(ArgumentError("type does not have definite field names")) - end - end - return t.name.names -end - -const BINDING_KIND_GLOBAL = 0x0 -const BINDING_KIND_CONST = 0x1 -const BINDING_KIND_CONST_IMPORT = 0x2 -const BINDING_KIND_IMPLICIT = 0x3 -const BINDING_KIND_EXPLICIT = 0x4 -const BINDING_KIND_IMPORTED = 0x5 -const BINDING_KIND_FAILED = 0x6 -const BINDING_KIND_DECLARED = 0x7 -const BINDING_KIND_GUARD = 0x8 - -function lookup_binding_partition(world::UInt, b::Core.Binding) - ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) -end - -function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) - ccall(:jl_get_globalref_partition, Ref{Core.BindingPartition}, (Any, UInt), gr, world) -end - -binding_kind(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_kind, UInt8, (Any,), bpart) -binding_kind(m::Module, s::Symbol) = binding_kind(lookup_binding_partition(tls_world_age(), GlobalRef(m, s))) - -""" - fieldname(x::DataType, i::Integer) - -Get the name of field `i` of a `DataType`. - -The return type is `Symbol`, except when `x <: Tuple`, in which case the index of the field is returned, of type `Int`. - -# Examples -```jldoctest -julia> fieldname(Rational, 1) -:num - -julia> fieldname(Rational, 2) -:den - -julia> fieldname(Tuple{String,Int}, 2) -2 -``` -""" -function fieldname(t::DataType, i::Integer) - throw_not_def_field() = throw(ArgumentError("type does not have definite field names")) - function throw_field_access(t, i, n_fields) - field_label = n_fields == 1 ? "field" : "fields" - throw(ArgumentError("Cannot access field $i since type $t only has $n_fields $field_label.")) - end - throw_need_pos_int(i) = throw(ArgumentError("Field numbers must be positive integers. $i is invalid.")) - - isabstracttype(t) && throw_not_def_field() - names = _fieldnames(t) - n_fields = length(names)::Int - i > n_fields && throw_field_access(t, i, n_fields) - i < 1 && throw_need_pos_int(i) - return @inbounds names[i]::Symbol -end - -fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) -fieldname(t::Type{<:Tuple}, i::Integer) = - i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i) - -""" - fieldnames(x::DataType) - -Get a tuple with the names of the fields of a `DataType`. - -Each name is a `Symbol`, except when `x <: Tuple`, in which case each name (actually the -index of the field) is an `Int`. - -See also [`propertynames`](@ref), [`hasfield`](@ref). - -# Examples -```jldoctest -julia> fieldnames(Rational) -(:num, :den) - -julia> fieldnames(typeof(1+im)) -(:re, :im) - -julia> fieldnames(Tuple{String,Int}) -(1, 2) -``` -""" -fieldnames(t::DataType) = (fieldcount(t); # error check to make sure type is specific enough - (_fieldnames(t)...,))::Tuple{Vararg{Symbol}} -fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t)) -fieldnames(::Core.TypeofBottom) = - throw(ArgumentError("The empty type does not have field names since it does not have instances.")) -fieldnames(t::Type{<:Tuple}) = ntuple(identity, fieldcount(t)) - -""" - hasfield(T::Type, name::Symbol) - -Return a boolean indicating whether `T` has `name` as one of its own fields. - -See also [`fieldnames`](@ref), [`fieldcount`](@ref), [`hasproperty`](@ref). - -!!! compat "Julia 1.2" - This function requires at least Julia 1.2. - -# Examples -```jldoctest -julia> struct Foo - bar::Int - end - -julia> hasfield(Foo, :bar) -true - -julia> hasfield(Foo, :x) -false -``` -""" -hasfield(T::Type, name::Symbol) = fieldindex(T, name, false) > 0 - -""" - nameof(t::DataType) -> Symbol - -Get the name of a (potentially `UnionAll`-wrapped) `DataType` (without its parent module) -as a symbol. - -# Examples -```jldoctest -julia> module Foo - struct S{T} - end - end -Foo - -julia> nameof(Foo.S{T} where T) -:S -``` -""" -nameof(t::DataType) = t.name.name -nameof(t::UnionAll) = nameof(unwrap_unionall(t))::Symbol - -""" - parentmodule(t::DataType) -> Module - -Determine the module containing the definition of a (potentially `UnionAll`-wrapped) `DataType`. - -# Examples -```jldoctest -julia> module Foo - struct Int end - end -Foo - -julia> parentmodule(Int) -Core - -julia> parentmodule(Foo.Int) -Foo -``` -""" -parentmodule(t::DataType) = t.name.module -parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) - -""" - isconst(m::Module, s::Symbol) -> Bool - -Determine whether a global is declared `const` in a given module `m`. -""" -isconst(m::Module, s::Symbol) = - ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 - -function isconst(g::GlobalRef) - return ccall(:jl_globalref_is_const, Cint, (Any,), g) != 0 -end - -""" - isconst(t::DataType, s::Union{Int,Symbol}) -> Bool - -Determine whether a field `s` is declared `const` in a given type `t`. -""" -function isconst(@nospecialize(t::Type), s::Symbol) - @_foldable_meta - t = unwrap_unionall(t) - isa(t, DataType) || return false - return isconst(t, fieldindex(t, s, false)) -end -function isconst(@nospecialize(t::Type), s::Int) - @_foldable_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - isa(t, DataType) || return false # uncertain - ismutabletype(t) || return true # immutable structs are always const - 1 <= s <= length(t.name.names) || return true # OOB reads are "const" since they always throw - constfields = t.name.constfields - constfields === C_NULL && return false - s -= 1 - return unsafe_load(Ptr{UInt32}(constfields), 1 + s÷32) & (1 << (s%32)) != 0 -end - -""" - isfieldatomic(t::DataType, s::Union{Int,Symbol}) -> Bool - -Determine whether a field `s` is declared `@atomic` in a given type `t`. -""" -function isfieldatomic(@nospecialize(t::Type), s::Symbol) - @_foldable_meta - t = unwrap_unionall(t) - isa(t, DataType) || return false - return isfieldatomic(t, fieldindex(t, s, false)) -end -function isfieldatomic(@nospecialize(t::Type), s::Int) - @_foldable_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - isa(t, DataType) || return false # uncertain - ismutabletype(t) || return false # immutable structs are never atomic - 1 <= s <= length(t.name.names) || return false # OOB reads are not atomic (they always throw) - atomicfields = t.name.atomicfields - atomicfields === C_NULL && return false - s -= 1 - return unsafe_load(Ptr{UInt32}(atomicfields), 1 + s÷32) & (1 << (s%32)) != 0 -end - -""" - @locals() - -Construct a dictionary of the names (as symbols) and values of all local -variables defined as of the call site. - -!!! compat "Julia 1.1" - This macro requires at least Julia 1.1. - -# Examples -```jldoctest -julia> let x = 1, y = 2 - Base.@locals - end -Dict{Symbol, Any} with 2 entries: - :y => 2 - :x => 1 - -julia> function f(x) - local y - show(Base.@locals); println() - for i = 1:1 - show(Base.@locals); println() - end - y = 2 - show(Base.@locals); println() - nothing - end; - -julia> f(42) -Dict{Symbol, Any}(:x => 42) -Dict{Symbol, Any}(:i => 1, :x => 42) -Dict{Symbol, Any}(:y => 2, :x => 42) -``` -""" -macro locals() - return Expr(:locals) -end - -# concrete datatype predicates - -datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Core.SimpleVector, (Any,), x) - -struct DataTypeLayout - size::UInt32 - nfields::UInt32 - npointers::UInt32 - firstptr::Int32 - alignment::UInt16 - flags::UInt16 - # haspadding : 1; - # fielddesc_type : 2; - # arrayelem_isboxed : 1; - # arrayelem_isunion : 1; -end - -""" - Base.datatype_alignment(dt::DataType) -> Int - -Memory allocation minimum alignment for instances of this type. -Can be called on any `isconcretetype`, although for Memory it will give the -alignment of the elements, not the whole object. -""" -function datatype_alignment(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment - return Int(alignment) -end - -function uniontype_layout(@nospecialize T::Type) - sz = RefValue{Csize_t}(0) - algn = RefValue{Csize_t}(0) - isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0 - (isinline, Int(sz[]), Int(algn[])) -end - -LLT_ALIGN(x, sz) = (x + sz - 1) & -sz - -# amount of total space taken by T when stored in a container -function aligned_sizeof(@nospecialize T::Type) - @_foldable_meta - if isa(T, Union) - if allocatedinline(T) - # NOTE this check is equivalent to `isbitsunion(T)`, we can improve type - # inference in the second branch with the outer `isa(T, Union)` check - _, sz, al = uniontype_layout(T) - return LLT_ALIGN(sz, al) - end - elseif allocatedinline(T) - al = datatype_alignment(T) - return LLT_ALIGN(Core.sizeof(T), al) - end - return Core.sizeof(Ptr{Cvoid}) -end - -gc_alignment(sz::Integer) = Int(ccall(:jl_alignment, Cint, (Csize_t,), sz)) -gc_alignment(T::Type) = gc_alignment(Core.sizeof(T)) - -""" - Base.datatype_haspadding(dt::DataType) -> Bool - -Return whether the fields of instances of this type are packed in memory, -with no intervening padding bits (defined as bits whose value does not impact -the semantic value of the instance itself). -Can be called on any `isconcretetype`. -""" -function datatype_haspadding(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags - return flags & 1 == 1 -end - -""" - Base.datatype_isbitsegal(dt::DataType) -> Bool - -Return whether egality of the (non-padding bits of the) in-memory representation -of an instance of this type implies semantic egality of the instance itself. -This may not be the case if the type contains to other values whose egality is -independent of their identity (e.g. immutable structs, some types, etc.). -""" -function datatype_isbitsegal(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags - return (flags & (1<<5)) != 0 -end - -""" - Base.datatype_nfields(dt::DataType) -> UInt32 - -Return the number of fields known to this datatype's layout. This may be -different from the number of actual fields of the type for opaque types. -Can be called on any `isconcretetype`. -""" -function datatype_nfields(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - 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 - -Return whether instances of this type can contain references to gc-managed memory. -Can be called on any `isconcretetype`. -""" -function datatype_pointerfree(dt::DataType) - @_foldable_meta - return datatype_npointers(dt) == 0 -end - -""" - Base.datatype_fielddesc_type(dt::DataType) -> Int - -Return the size in bytes of each field-description entry in the layout array, -located at `(dt.layout + sizeof(DataTypeLayout))`. -Can be called on any `isconcretetype`. - -See also [`fieldoffset`](@ref). -""" -function datatype_fielddesc_type(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags - return (flags >> 1) & 3 -end - -""" - Base.datatype_arrayelem(dt::DataType) -> Int - -Return the behavior of the trailing array types allocations. -Can be called on any `isconcretetype`, but only meaningful on `Memory`. - -0 = inlinealloc -1 = isboxed -2 = isbitsunion -""" -function datatype_arrayelem(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags - return (flags >> 3) & 3 -end - -function datatype_layoutsize(dt::DataType) - @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - size = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).size - return size % Int -end - - -# For type stability, we only expose a single struct that describes everything -struct FieldDesc - isforeign::Bool - isptr::Bool - size::UInt32 - offset::UInt32 -end - -struct FieldDescStorage{T} - ptrsize::T - offset::T -end -FieldDesc(fd::FieldDescStorage{T}) where {T} = - FieldDesc(false, fd.ptrsize & 1 != 0, - fd.ptrsize >> 1, fd.offset) - -struct DataTypeFieldDesc - dt::DataType - function DataTypeFieldDesc(dt::DataType) - dt.layout == C_NULL && throw(UndefRefError()) - new(dt) - end -end - -function getindex(dtfd::DataTypeFieldDesc, i::Int) - layout_ptr = convert(Ptr{DataTypeLayout}, dtfd.dt.layout) - fd_ptr = layout_ptr + Core.sizeof(DataTypeLayout) - layout = unsafe_load(layout_ptr) - fielddesc_type = (layout.flags >> 1) & 3 - nfields = layout.nfields - @boundscheck ((1 <= i <= nfields) || throw(BoundsError(dtfd, i))) - if fielddesc_type == 0 - return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt8}}(fd_ptr), i)) - elseif fielddesc_type == 1 - return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt16}}(fd_ptr), i)) - elseif fielddesc_type == 2 - return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt32}}(fd_ptr), i)) - else - # fielddesc_type == 3 - return FieldDesc(true, true, 0, 0) - end -end - -""" - ismutable(v) -> Bool - -Return `true` if and only if value `v` is mutable. See [Mutable Composite Types](@ref) -for a discussion of immutability. Note that this function works on values, so if you -give it a `DataType`, it will tell you that a value of the type is mutable. - -!!! note - For technical reasons, `ismutable` returns `true` for values of certain special types - (for example `String` and `Symbol`) even though they cannot be mutated in a permissible way. - -See also [`isbits`](@ref), [`isstructtype`](@ref). - -# Examples -```jldoctest -julia> ismutable(1) -false - -julia> ismutable([1,2]) -true -``` - -!!! compat "Julia 1.5" - This function requires at least Julia 1.5. -""" -ismutable(@nospecialize(x)) = (@_total_meta; (typeof(x).name::Core.TypeName).flags & 0x2 == 0x2) -# The type assertion above is required to fix some invalidations. -# See also https://github.com/JuliaLang/julia/issues/52134 - -""" - ismutabletype(T) -> Bool - -Determine whether type `T` was declared as a mutable type -(i.e. using `mutable struct` keyword). -If `T` is not a type, then return `false`. - -!!! compat "Julia 1.7" - This function requires at least Julia 1.7. -""" -function ismutabletype(@nospecialize t) - @_total_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - return isa(t, DataType) && ismutabletypename(t.name) -end - -ismutabletypename(tn::Core.TypeName) = tn.flags & 0x2 == 0x2 - -""" - isstructtype(T) -> Bool - -Determine whether type `T` was declared as a struct type -(i.e. using the `struct` or `mutable struct` keyword). -If `T` is not a type, then return `false`. -""" -function isstructtype(@nospecialize t) - @_total_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - isa(t, DataType) || return false - return !isprimitivetype(t) && !isabstracttype(t) -end - -""" - isprimitivetype(T) -> Bool - -Determine whether type `T` was declared as a primitive type -(i.e. using the `primitive type` syntax). -If `T` is not a type, then return `false`. -""" -function isprimitivetype(@nospecialize t) - @_total_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - isa(t, DataType) || return false - return (t.flags & 0x0080) == 0x0080 -end - -""" - isbitstype(T) - -Return `true` if type `T` is a "plain data" type, -meaning it is immutable and contains no references to other values, -only `primitive` types and other `isbitstype` types. -Typical examples are numeric types such as [`UInt8`](@ref), -[`Float64`](@ref), and [`Complex{Float64}`](@ref). -This category of types is significant since they are valid as type parameters, -may not track [`isdefined`](@ref) / [`isassigned`](@ref) status, -and have a defined layout that is compatible with C. -If `T` is not a type, then return `false`. - -See also [`isbits`](@ref), [`isprimitivetype`](@ref), [`ismutable`](@ref). - -# Examples -```jldoctest -julia> isbitstype(Complex{Float64}) -true - -julia> isbitstype(Complex) -false -``` -""" -isbitstype(@nospecialize t) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0008) == 0x0008) - -""" - isbits(x) - -Return `true` if `x` is an instance of an [`isbitstype`](@ref) type. -""" -isbits(@nospecialize x) = isbitstype(typeof(x)) - -""" - objectid(x) -> UInt - -Get a hash value for `x` based on object identity. - -If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. - -See also [`hash`](@ref), [`IdDict`](@ref). -""" -function objectid(@nospecialize(x)) - @_total_meta - return ccall(:jl_object_id, UInt, (Any,), x) -end - -""" - isdispatchtuple(T) - -Determine whether type `T` is a tuple "leaf type", -meaning it could appear as a type signature in dispatch -and has no subtypes (or supertypes) which could appear in a call. -If `T` is not a type, then return `false`. -""" -isdispatchtuple(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0004) == 0x0004) - -datatype_ismutationfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0100) == 0x0100) - -""" - Base.ismutationfree(T) - -Determine whether type `T` is mutation free in the sense that no mutable memory -is reachable from this type (either in the type itself) or through any fields. -Note that the type itself need not be immutable. For example, an empty mutable -type is `ismutabletype`, but also `ismutationfree`. -If `T` is not a type, then return `false`. -""" -function ismutationfree(@nospecialize(t)) - t = unwrap_unionall(t) - if isa(t, DataType) - return datatype_ismutationfree(t) - elseif isa(t, Union) - return ismutationfree(t.a) && ismutationfree(t.b) - end - # TypeVar, etc. - return false -end - -datatype_isidentityfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0200) == 0x0200) - -""" - Base.isidentityfree(T) - -Determine whether type `T` is identity free in the sense that this type or any -reachable through its fields has non-content-based identity. -If `T` is not a type, then return `false`. -""" -function isidentityfree(@nospecialize(t)) - t = unwrap_unionall(t) - if isa(t, DataType) - return datatype_isidentityfree(t) - elseif isa(t, Union) - return isidentityfree(t.a) && isidentityfree(t.b) - end - # TypeVar, etc. - return false -end - -iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) -isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) - -using Core: has_free_typevars - -# equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} -# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query -function isdispatchelem(@nospecialize v) - return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) || - (isType(v) && !has_free_typevars(v)) -end - -const _TYPE_NAME = Type.body.name -isType(@nospecialize t) = isa(t, DataType) && t.name === _TYPE_NAME - -""" - isconcretetype(T) - -Determine whether type `T` is a concrete type, meaning it could have direct instances -(values `x` such that `typeof(x) === T`). -Note that this is not the negation of `isabstracttype(T)`. -If `T` is not a type, then return `false`. - -See also: [`isbits`](@ref), [`isabstracttype`](@ref), [`issingletontype`](@ref). - -# Examples -```jldoctest -julia> isconcretetype(Complex) -false - -julia> isconcretetype(Complex{Float32}) -true - -julia> isconcretetype(Vector{Complex}) -true - -julia> isconcretetype(Vector{Complex{Float32}}) -true - -julia> isconcretetype(Union{}) -false - -julia> isconcretetype(Union{Int,String}) -false -``` -""" -isconcretetype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0002) == 0x0002) - -""" - isabstracttype(T) - -Determine whether type `T` was declared as an abstract type -(i.e. using the `abstract type` syntax). -Note that this is not the negation of `isconcretetype(T)`. -If `T` is not a type, then return `false`. - -# Examples -```jldoctest -julia> isabstracttype(AbstractArray) -true - -julia> isabstracttype(Vector) -false -``` -""" -function isabstracttype(@nospecialize(t)) - @_total_meta - t = unwrap_unionall(t) - # TODO: what to do for `Union`? - return isa(t, DataType) && (t.name.flags & 0x1) == 0x1 -end - -function is_datatype_layoutopaque(dt::DataType) - datatype_nfields(dt) == 0 && !datatype_pointerfree(dt) -end - -function is_valid_intrinsic_elptr(@nospecialize(ety)) - ety === Any && return true - isconcretetype(ety) || return false - ety <: Array && return false - return !is_datatype_layoutopaque(ety) -end - -""" - Base.issingletontype(T) - -Determine whether type `T` has exactly one possible instance; for example, a -struct type with no fields except other singleton values. -If `T` is not a concrete type, then return `false`. -""" -issingletontype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && isdefined(t, :instance) && datatype_layoutsize(t) == 0 && datatype_pointerfree(t)) - -""" - typeintersect(T::Type, S::Type) - -Compute a type that contains the intersection of `T` and `S`. Usually this will be the -smallest such type or one close to it. - -A special case where exact behavior is guaranteed: when `T <: S`, -`typeintersect(S, T) == T == typeintersect(T, S)`. -""" -typeintersect(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)) - -morespecific(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_morespecific, Cint, (Any, Any), a::Type, b::Type) != 0) -morespecific(a::Method, b::Method) = ccall(:jl_method_morespecific, Cint, (Any, Any), a, b) != 0 - -""" - fieldoffset(type, i) - -The byte offset of field `i` of a type relative to the data start. For example, we could -use it in the following manner to summarize information about a struct: - -```jldoctest -julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)]; - -julia> structinfo(Base.Filesystem.StatStruct) -14-element Vector{Tuple{UInt64, Symbol, Type}}: - (0x0000000000000000, :desc, Union{RawFD, String}) - (0x0000000000000008, :device, UInt64) - (0x0000000000000010, :inode, UInt64) - (0x0000000000000018, :mode, UInt64) - (0x0000000000000020, :nlink, Int64) - (0x0000000000000028, :uid, UInt64) - (0x0000000000000030, :gid, UInt64) - (0x0000000000000038, :rdev, UInt64) - (0x0000000000000040, :size, Int64) - (0x0000000000000048, :blksize, Int64) - (0x0000000000000050, :blocks, Int64) - (0x0000000000000058, :mtime, Float64) - (0x0000000000000060, :ctime, Float64) - (0x0000000000000068, :ioerrno, Int32) -``` -""" -fieldoffset(x::DataType, idx::Integer) = (@_foldable_meta; ccall(:jl_get_field_offset, Csize_t, (Any, Cint), x, idx)) - -""" - fieldtype(T, name::Symbol | index::Int) - -Determine the declared type of a field (specified by name or index) in a composite DataType `T`. - -# Examples -```jldoctest -julia> struct Foo - x::Int64 - y::String - end - -julia> fieldtype(Foo, :x) -Int64 - -julia> fieldtype(Foo, 2) -String -``` -""" -fieldtype - -""" - Base.fieldindex(T, name::Symbol, err:Bool=true) - -Get the index of a named field, throwing an error if the field does not exist (when err==true) -or returning 0 (when err==false). - -# Examples -```jldoctest -julia> struct Foo - x::Int64 - y::String - end - -julia> Base.fieldindex(Foo, :z) -ERROR: FieldError: type Foo has no field `z`, available fields: `x`, `y` -Stacktrace: -[...] - -julia> Base.fieldindex(Foo, :z, false) -0 -``` -""" -function fieldindex(T::DataType, name::Symbol, err::Bool=true) - return err ? _fieldindex_maythrow(T, name) : _fieldindex_nothrow(T, name) -end - -function _fieldindex_maythrow(T::DataType, name::Symbol) - @_foldable_meta - @noinline - return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, true)+1) -end - -function _fieldindex_nothrow(T::DataType, name::Symbol) - @_total_meta - @noinline - return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, false)+1) -end - -function fieldindex(t::UnionAll, name::Symbol, err::Bool=true) - t = argument_datatype(t) - if t === nothing - err && throw(ArgumentError("type does not have definite fields")) - return 0 - end - return fieldindex(t, name, err) -end - -function argument_datatype(@nospecialize t) - @_total_meta - @noinline - return ccall(:jl_argument_datatype, Any, (Any,), t)::Union{Nothing,DataType} -end - -function datatype_fieldcount(t::DataType) - if t.name === _NAMEDTUPLE_NAME - names, types = t.parameters[1], t.parameters[2] - if names isa Tuple - return length(names) - end - if types isa DataType && types <: Tuple - return fieldcount(types) - end - return nothing - elseif isabstracttype(t) - return nothing - end - if t.name === Tuple.name - isvatuple(t) && return nothing - return length(t.types) - end - # Equivalent to length(t.types), but `t.types` is lazy and we do not want - # to be forced to compute it. - return length(t.name.names) -end - -""" - fieldcount(t::Type) - -Get the number of fields that an instance of the given type would have. -An error is thrown if the type is too abstract to determine this. -""" -function fieldcount(@nospecialize t) - @_foldable_meta - if t isa UnionAll || t isa Union - t = argument_datatype(t) - if t === nothing - throw(ArgumentError("type does not have a definite number of fields")) - end - elseif t === Union{} - throw(ArgumentError("The empty type does not have a well-defined number of fields since it does not have instances.")) - end - if !(t isa DataType) - throw(TypeError(:fieldcount, DataType, t)) - end - fcount = datatype_fieldcount(t) - if fcount === nothing - throw(ArgumentError("type does not have a definite number of fields")) - end - return fcount -end - -""" - fieldtypes(T::Type) - -The declared types of all fields in a composite DataType `T` as a tuple. - -!!! compat "Julia 1.1" - This function requires at least Julia 1.1. - -# Examples -```jldoctest -julia> struct Foo - x::Int64 - y::String - end - -julia> fieldtypes(Foo) -(Int64, String) -``` -""" -fieldtypes(T::Type) = (@_foldable_meta; ntupleany(i -> fieldtype(T, i), fieldcount(T))) - -# return all instances, for types that can be enumerated - -""" - instances(T::Type) - -Return a collection of all instances of the given type, if applicable. Mostly used for -enumerated types (see `@enum`). - -# Examples -```jldoctest -julia> @enum Color red blue green - -julia> instances(Color) -(red, blue, green) -``` -""" -function instances end - -function to_tuple_type(@nospecialize(t)) - if isa(t, Tuple) || isa(t, AbstractArray) || isa(t, SimpleVector) - t = Tuple{t...} - end - if isa(t, Type) && t <: Tuple - for p in (unwrap_unionall(t)::DataType).parameters - if isa(p, Core.TypeofVararg) - p = unwrapva(p) - end - if !(isa(p, Type) || isa(p, TypeVar)) - error("argument tuple type must contain only types") - end - end - else - error("expected tuple type") - end - t -end - -function signature_type(@nospecialize(f), @nospecialize(argtypes)) - argtypes = to_tuple_type(argtypes) - ft = Core.Typeof(f) - u = unwrap_unionall(argtypes)::DataType - return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) -end +const Compiler = Core.Compiler """ code_lowered(f, types; generated=true, debuginfo=:default) @@ -1228,102 +48,8 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= return ret end -hasgenerator(m::Method) = isdefined(m, :generator) -hasgenerator(m::Core.MethodInstance) = hasgenerator(m.def::Method) - -# low-level method lookup functions used by the compiler - -unionlen(@nospecialize(x)) = x isa Union ? unionlen(x.a) + unionlen(x.b) : 1 - -function _uniontypes(@nospecialize(x), ts::Array{Any,1}) - if x isa Union - _uniontypes(x.a, ts) - _uniontypes(x.b, ts) - else - push!(ts, x) - end - return ts -end -uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) - -function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) - tt = signature_type(f, t) - return _methods_by_ftype(tt, lim, world) -end - -function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt) - return _methods_by_ftype(t, nothing, lim, world) -end -function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt) - return _methods_by_ftype(t, mt, lim, world, false, RefValue{UInt}(typemin(UInt)), RefValue{UInt}(typemax(UInt)), Ptr{Int32}(C_NULL)) -end -function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt, ambig::Bool, min::Ref{UInt}, max::Ref{UInt}, has_ambig::Ref{Int32}) - return ccall(:jl_matching_methods, Any, (Any, Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}, Ptr{Int32}), t, mt, lim, ambig, world, min, max, has_ambig)::Union{Vector{Any},Nothing} -end - # high-level, more convenient method lookup functions -# type for reflecting and pretty-printing a subset of methods -mutable struct MethodList <: AbstractArray{Method,1} - ms::Array{Method,1} - mt::Core.MethodTable -end - -size(m::MethodList) = size(m.ms) -getindex(m::MethodList, i::Integer) = m.ms[i] - -function MethodList(mt::Core.MethodTable) - ms = Method[] - visit(mt) do m - push!(ms, m) - end - return MethodList(ms, mt) -end - -""" - methods(f, [types], [module]) - -Return the method table for `f`. - -If `types` is specified, return an array of methods whose types match. -If `module` is specified, return an array of methods defined in that module. -A list of modules can also be specified as an array. - -!!! compat "Julia 1.4" - At least Julia 1.4 is required for specifying a module. - -See also: [`which`](@ref), [`@which`](@ref Main.InteractiveUtils.@which) and [`methodswith`](@ref Main.InteractiveUtils.methodswith). -""" -function methods(@nospecialize(f), @nospecialize(t), - mod::Union{Tuple{Module},AbstractArray{Module},Nothing}=nothing) - world = get_world_counter() - world == typemax(UInt) && error("code reflection cannot be used from generated functions") - # Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually - ms = Method[] - for m in _methods(f, t, -1, world)::Vector - m = m::Core.MethodMatch - (mod === nothing || parentmodule(m.method) ∈ mod) && push!(ms, m.method) - end - MethodList(ms, typeof(f).name.mt) -end -methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) - -function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) - tt = signature_type(f, t) - world = get_world_counter() - world == typemax(UInt) && error("code reflection cannot be used from generated functions") - min = RefValue{UInt}(typemin(UInt)) - max = RefValue{UInt}(typemax(UInt)) - ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector - return MethodList(Method[(m::Core.MethodMatch).method for m in ms], typeof(f).name.mt) -end - -function methods(@nospecialize(f), - mod::Union{Module,AbstractArray{Module},Nothing}=nothing) - # return all matches - return methods(f, Tuple{Vararg{Any}}, mod) -end - function visit(f, mt::Core.MethodTable) mt.defs !== nothing && visit(f, mt.defs) nothing @@ -1560,84 +286,6 @@ struct EmissionParams end end -const SLOT_USED = 0x8 -ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_slotflag, UInt8, (Any, Csize_t), code, i - 1) - -""" - may_invoke_generator(method, atype, sparams) -> Bool - -Computes whether or not we may invoke the generator for the given `method` on -the given `atype` and `sparams`. For correctness, all generated function are -required to return monotonic answers. However, since we don't expect users to -be able to successfully implement this criterion, we only call generated -functions on concrete types. The one exception to this is that we allow calling -generators with abstract types if the generator does not use said abstract type -(and thus cannot incorrectly use it to break monotonicity). This function -computes whether we are in either of these cases. - -Unlike normal functions, the compilation heuristics still can't generate good dispatch -in some cases, but this may still allow inference not to fall over in some limited cases. -""" -function may_invoke_generator(mi::MethodInstance) - return may_invoke_generator(mi.def::Method, mi.specTypes, mi.sparam_vals) -end -function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector) - # If we have complete information, we may always call the generator - isdispatchtuple(atype) && return true - - # We don't have complete information, but it is possible that the generator - # syntactically doesn't make use of the information we don't have. Check - # for that. - - # For now, only handle the (common, generated by the frontend case) that the - # generator only has one method - generator = method.generator - isa(generator, Core.GeneratedFunctionStub) || return false - tt = Tuple{typeof(generator.gen), Vararg{Any}} - gen_mthds = _methods_by_ftype(tt, #=lim=#1, method.primary_world) - gen_mthds isa Vector || return false - length(gen_mthds) == 1 || return false - - generator_method = (first(gen_mthds)::Core.MethodMatch).method - nsparams = length(sparams) - isdefined(generator_method, :source) || return false - code = generator_method.source - nslots = ccall(:jl_ir_nslots, Int, (Any,), code) - at = unwrap_unionall(atype) - at isa DataType || return false - (nslots >= 1 + length(sparams) + length(at.parameters)) || return false - - firstarg = 1 - for i = 1:nsparams - if isa(sparams[i], TypeVar) - if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 - return false - end - end - end - nargs = Int(method.nargs) - non_va_args = method.isva ? nargs - 1 : nargs - for i = 1:non_va_args - if !isdispatchelem(at.parameters[i]) - if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 - return false - end - end - end - if method.isva - # If the va argument is used, we need to ensure that all arguments that - # contribute to the va tuple are dispatchelemes - if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 - for i = (non_va_args+1):length(at.parameters) - if !isdispatchelem(at.parameters[i]) - return false - end - end - end - end - return true -end - """ code_typed(f, types; kw...) @@ -1710,7 +358,7 @@ function code_typed_by_type(@nospecialize(tt::Type); optimize::Bool=true, debuginfo::Symbol=:default, world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") if @isdefined(IRShow) @@ -1722,12 +370,12 @@ function code_typed_by_type(@nospecialize(tt::Type); throw(ArgumentError("'debuginfo' must be either :source or :none")) end tt = to_tuple_type(tt) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:code_typed, tt) asts = [] for match in matches.matches match = match::Core.MethodMatch - code = Core.Compiler.typeinf_code(interp, match, optimize) + code = Compiler.typeinf_code(interp, match, optimize) if code === nothing push!(asts, match.method => Any) else @@ -1747,9 +395,9 @@ function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool) if isdefined(m, :source) if optimize tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...} - mi = Core.Compiler.specialize_method(m, tt, Core.svec()) - interp = Core.Compiler.NativeInterpreter(m.primary_world) - code = Core.Compiler.typeinf_code(interp, mi, optimize) + mi = Compiler.specialize_method(m, tt, Core.svec()) + interp = Compiler.NativeInterpreter(m.primary_world) + code = Compiler.typeinf_code(interp, mi, optimize) if code isa CodeInfo return Pair{CodeInfo, Any}(code, code.rettype) end @@ -1839,18 +487,18 @@ a full signature to query. function code_ircode_by_type( @nospecialize(tt::Type); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world), + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing}=nothing, ) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:code_ircode, tt) asts = [] for match in matches.matches match = match::Core.MethodMatch - (code, ty) = Core.Compiler.typeinf_ircode(interp, match, optimize_until) + (code, ty) = Compiler.typeinf_ircode(interp, match, optimize_until) if code === nothing push!(asts, match.method => Any) else @@ -1860,24 +508,24 @@ function code_ircode_by_type( return asts end -function _builtin_return_type(interp::Core.Compiler.AbstractInterpreter, +function _builtin_return_type(interp::Compiler.AbstractInterpreter, @nospecialize(f::Core.Builtin), @nospecialize(types)) argtypes = Any[to_tuple_type(types).parameters...] - rt = Core.Compiler.builtin_tfunction(interp, f, argtypes, nothing) - return Core.Compiler.widenconst(rt) + rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing) + return Compiler.widenconst(rt) end -function _builtin_effects(interp::Core.Compiler.AbstractInterpreter, +function _builtin_effects(interp::Compiler.AbstractInterpreter, @nospecialize(f::Core.Builtin), @nospecialize(types)) argtypes = Any[to_tuple_type(types).parameters...] - rt = Core.Compiler.builtin_tfunction(interp, f, argtypes, nothing) - return Core.Compiler.builtin_effects(Core.Compiler.typeinf_lattice(interp), f, argtypes, rt) + rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing) + return Compiler.builtin_effects(Compiler.typeinf_lattice(interp), f, argtypes, rt) end -function _builtin_exception_type(interp::Core.Compiler.AbstractInterpreter, +function _builtin_exception_type(interp::Compiler.AbstractInterpreter, @nospecialize(f::Core.Builtin), @nospecialize(types)) effects = _builtin_effects(interp, f, types) - return Core.Compiler.is_nothrow(effects) ? Union{} : Any + return Compiler.is_nothrow(effects) ? Union{} : Any end check_generated_context(world::UInt) = @@ -1933,7 +581,7 @@ julia> Base.return_types(sum, (Union{Vector{Int},UnitRange{Int}},)) """ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) check_generated_context(world) if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f, types)) @@ -1942,11 +590,11 @@ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); return Any[_builtin_return_type(interp, f, types)] end tt = signature_type(f, types) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:return_types, tt) rts = Any[] for match in matches.matches - ty = Core.Compiler.typeinf_type(interp, match::Core.MethodMatch) + ty = Compiler.typeinf_type(interp, match::Core.MethodMatch) push!(rts, something(ty, Any)) end return rts @@ -2001,7 +649,7 @@ On the other hand `Base.infer_return_type` returns one collective result that su """ function infer_return_type(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) check_generated_context(world) if isa(f, Core.OpaqueClosure) return last(only(code_typed_opaque_closure(f, types))) @@ -2009,12 +657,12 @@ function infer_return_type(@nospecialize(f), @nospecialize(types=default_tt(f)); return _builtin_return_type(interp, f, types) end tt = signature_type(f, types) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:infer_return_type, tt) rt = Union{} for match in matches.matches - ty = Core.Compiler.typeinf_type(interp, match::Core.MethodMatch) - rt = Core.Compiler.tmerge(rt, something(ty, Any)) + ty = Compiler.typeinf_type(interp, match::Core.MethodMatch) + rt = Compiler.tmerge(rt, something(ty, Any)) end return rt end @@ -2071,7 +719,7 @@ julia> Base.infer_exception_types(throw_if_number, (Any,)) """ function infer_exception_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) check_generated_context(world) if isa(f, Core.OpaqueClosure) return Any[Any] # TODO @@ -2079,15 +727,15 @@ function infer_exception_types(@nospecialize(f), @nospecialize(types=default_tt( return Any[_builtin_exception_type(interp, f, types)] end tt = signature_type(f, types) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:infer_exception_types, tt) excts = Any[] for match in matches.matches - frame = Core.Compiler.typeinf_frame(interp, match::Core.MethodMatch, #=run_optimizer=#false) + frame = Compiler.typeinf_frame(interp, match::Core.MethodMatch, #=run_optimizer=#false) if frame === nothing exct = Any else - exct = Core.Compiler.widenconst(frame.result.exc_result) + exct = Compiler.widenconst(frame.result.exc_result) end push!(excts, exct) end @@ -2150,7 +798,7 @@ signature, the exception type is widened to `MethodError`. """ function infer_exception_type(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) check_generated_context(world) if isa(f, Core.OpaqueClosure) return Any # TODO @@ -2158,18 +806,18 @@ function infer_exception_type(@nospecialize(f), @nospecialize(types=default_tt(f return _builtin_exception_type(interp, f, types) end tt = signature_type(f, types) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:infer_exception_type, tt) exct = Union{} if _may_throw_methoderror(matches) # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - exct = Core.Compiler.tmerge(exct, MethodError) + exct = Compiler.tmerge(exct, MethodError) end for match in matches.matches match = match::Core.MethodMatch - frame = Core.Compiler.typeinf_frame(interp, match, #=run_optimizer=#false) + frame = Compiler.typeinf_frame(interp, match, #=run_optimizer=#false) frame === nothing && return Any - exct = Core.Compiler.tmerge(exct, Core.Compiler.widenconst(frame.result.exc_result)) + exct = Compiler.tmerge(exct, Compiler.widenconst(frame.result.exc_result)) end return exct end @@ -2236,24 +884,24 @@ signature, the `:nothrow` bit gets tainted. function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); optimize::Bool=true, world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) check_generated_context(world) if isa(f, Core.Builtin) return _builtin_effects(interp, f, types) end tt = signature_type(f, types) - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:infer_effects, tt) - effects = Core.Compiler.EFFECTS_TOTAL + effects = Compiler.EFFECTS_TOTAL if _may_throw_methoderror(matches) # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - effects = Core.Compiler.Effects(effects; nothrow=false) + effects = Compiler.Effects(effects; nothrow=false) end for match in matches.matches match = match::Core.MethodMatch - frame = Core.Compiler.typeinf_frame(interp, match, #=run_optimizer=#optimize) - frame === nothing && return Core.Compiler.Effects() - effects = Core.Compiler.merge_effects(effects, frame.result.ipo_effects) + frame = Compiler.typeinf_frame(interp, match, #=run_optimizer=#optimize) + frame === nothing && return Compiler.Effects() + effects = Compiler.merge_effects(effects, frame.result.ipo_effects) end return effects end @@ -2271,24 +919,24 @@ end function print_statement_costs(io::IO, @nospecialize(tt::Type); world::UInt=get_world_counter(), - interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) + interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) tt = to_tuple_type(tt) world == typemax(UInt) && error("code reflection cannot be used from generated functions") - matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) + matches = Compiler.findall(tt, Compiler.method_table(interp)) matches === nothing && raise_match_failure(:print_statement_costs, tt) - params = Core.Compiler.OptimizationParams(interp) + params = Compiler.OptimizationParams(interp) cst = Int[] for match in matches.matches match = match::Core.MethodMatch println(io, match.method) - code = Core.Compiler.typeinf_code(interp, match, true) + code = Compiler.typeinf_code(interp, match, true) if code === nothing println(io, " inference not successful") else empty!(cst) resize!(cst, length(code.code)) - sptypes = Core.Compiler.VarState[Core.Compiler.VarState(sp, false) for sp in match.sparams] - maxcost = Core.Compiler.statement_costs!(cst, code.code, code, sptypes, params) + sptypes = Compiler.VarState[Compiler.VarState(sp, false) for sp in match.sparams] + maxcost = Compiler.statement_costs!(cst, code.code, code, sptypes, params) nd = ndigits(maxcost) irshow_config = IRShow.IRShowConfig() do io, linestart, idx print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ") @@ -2303,18 +951,18 @@ end print_statement_costs(args...; kwargs...) = print_statement_costs(stdout, args...; kwargs...) function _which(@nospecialize(tt::Type); - method_table::Union{Nothing,Core.MethodTable,Core.Compiler.MethodTableView}=nothing, + method_table::Union{Nothing,Core.MethodTable,Compiler.MethodTableView}=nothing, world::UInt=get_world_counter(), raise::Bool=true) world == typemax(UInt) && error("code reflection cannot be used from generated functions") if method_table === nothing - table = Core.Compiler.InternalMethodTable(world) + table = Compiler.InternalMethodTable(world) elseif method_table isa Core.MethodTable - table = Core.Compiler.OverlayMethodTable(world, method_table) + table = Compiler.OverlayMethodTable(world, method_table) else table = method_table end - match, = Core.Compiler.findsup(tt, table) + match, = Compiler.findsup(tt, table) if match === nothing raise && error("no unique matching method found for the specified argument types") return nothing @@ -2334,7 +982,7 @@ See also: [`parentmodule`](@ref), [`@which`](@ref Main.InteractiveUtils.@which), function which(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() - match, _ = Core.Compiler._findsup(tt, nothing, world) + match, _ = Compiler._findsup(tt, nothing, world) if match === nothing me = MethodError(f, t, world) ee = ErrorException(sprint(io -> begin @@ -2653,92 +1301,6 @@ function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false) return true end -""" - delete_method(m::Method) - -Make method `m` uncallable and force recompilation of any methods that use(d) it. -""" -function delete_method(m::Method) - ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) -end - -function get_methodtable(m::Method) - mt = ccall(:jl_method_get_table, Any, (Any,), m) - if mt === nothing - return nothing - end - return mt::Core.MethodTable -end - -""" - has_bottom_parameter(t) -> Bool - -Determine whether `t` is a Type for which one or more of its parameters is `Union{}`. -""" -function has_bottom_parameter(t::DataType) - for p in t.parameters - has_bottom_parameter(p) && return true - end - return false -end -has_bottom_parameter(t::typeof(Bottom)) = true -has_bottom_parameter(t::UnionAll) = has_bottom_parameter(unwrap_unionall(t)) -has_bottom_parameter(t::Union) = has_bottom_parameter(t.a) & has_bottom_parameter(t.b) -has_bottom_parameter(t::TypeVar) = has_bottom_parameter(t.ub) -has_bottom_parameter(::Any) = false - -min_world(m::Core.CodeInstance) = m.min_world -max_world(m::Core.CodeInstance) = m.max_world -min_world(m::Core.CodeInfo) = m.min_world -max_world(m::Core.CodeInfo) = m.max_world - -""" - get_world_counter() - -Returns the current maximum world-age counter. This counter is global and monotonically -increasing. -""" -get_world_counter() = ccall(:jl_get_world_counter, UInt, ()) - -""" - tls_world_age() - -Returns the world the [current_task()](@ref) is executing within. -""" -tls_world_age() = ccall(:jl_get_tls_world_age, UInt, ()) - -""" - propertynames(x, private=false) - -Get a tuple or a vector of the properties (`x.property`) of an object `x`. -This is typically the same as [`fieldnames(typeof(x))`](@ref), but types -that overload [`getproperty`](@ref) should generally overload `propertynames` -as well to get the properties of an instance of the type. - -`propertynames(x)` may return only "public" property names that are part -of the documented interface of `x`. If you want it to also return "private" -property names intended for internal use, pass `true` for the optional second argument. -REPL tab completion on `x.` shows only the `private=false` properties. - -See also: [`hasproperty`](@ref), [`hasfield`](@ref). -""" -propertynames(x) = fieldnames(typeof(x)) -propertynames(m::Module) = names(m) -propertynames(x, private::Bool) = propertynames(x) # ignore private flag by default -propertynames(x::Array) = () # hide the fields from tab completion to discourage calling `x.size` instead of `size(x)`, even though they are equivalent - -""" - hasproperty(x, s::Symbol) - -Return a boolean indicating whether the object `x` has `s` as one of its own properties. - -!!! compat "Julia 1.2" - This function requires at least Julia 1.2. - -See also: [`propertynames`](@ref), [`hasfield`](@ref). -""" -hasproperty(x, s::Symbol) = s in propertynames(x) - """ @invoke f(arg::T, ...; kwargs...) @@ -2786,7 +1348,7 @@ julia> @macroexpand @invoke (xs::Xs)[i::I] = v::V The additional syntax is supported as of Julia 1.10. """ macro invoke(ex) - topmod = Core.Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally + topmod = Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally f, args, kwargs = destructure_callex(topmod, ex) types = Expr(:curly, :Tuple) out = Expr(:call, GlobalRef(Core, :invoke)) @@ -2845,7 +1407,7 @@ julia> @macroexpand @invokelatest xs[i] = v The additional `x.f` and `xs[i]` syntax requires Julia 1.10. """ macro invokelatest(ex) - topmod = Core.Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally + topmod = Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally f, args, kwargs = destructure_callex(topmod, ex) out = Expr(:call, GlobalRef(Base, :invokelatest)) isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) @@ -2902,23 +1464,3 @@ function destructure_callex(topmod::Module, @nospecialize(ex)) end return f, args, kwargs end - -""" - Base.generating_output([incremental::Bool])::Bool - -Return `true` if the current process is being used to pre-generate a -code cache via any of the `--output-*` command line arguments. The optional -`incremental` argument further specifies the precompilation mode: when set -to `true`, the function will return `true` only for package precompilation; -when set to `false`, it will return `true` only for system image generation. - -!!! compat "Julia 1.11" - This function requires at least Julia 1.11. -""" -function generating_output(incremental::Union{Bool,Nothing}=nothing) - ccall(:jl_generating_output, Cint, ()) == 0 && return false - if incremental !== nothing - JLOptions().incremental == incremental || return false - end - return true -end diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl new file mode 100644 index 0000000000000..645aa55c538b4 --- /dev/null +++ b/base/runtime_internals.jl @@ -0,0 +1,1530 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# name and module reflection + +""" + parentmodule(m::Module) -> Module + +Get a module's enclosing `Module`. `Main` is its own parent. + +See also: [`names`](@ref), [`nameof`](@ref), [`fullname`](@ref), [`@__MODULE__`](@ref). + +# Examples +```jldoctest +julia> parentmodule(Main) +Main + +julia> parentmodule(Base.Broadcast) +Base +``` +""" +parentmodule(m::Module) = (@_total_meta; ccall(:jl_module_parent, Ref{Module}, (Any,), m)) + +is_root_module(m::Module) = parentmodule(m) === m || (isdefined(Main, :Base) && m === Main.Base) + +""" + moduleroot(m::Module) -> Module + +Find the root module of a given module. This is the first module in the chain of +parent modules of `m` which is either a registered root module or which is its +own parent module. +""" +function moduleroot(m::Module) + @_total_meta + while true + is_root_module(m) && return m + p = parentmodule(m) + p === m && return m + m = p + end +end + +""" + @__MODULE__ -> Module + +Get the `Module` of the toplevel eval, +which is the `Module` code is currently being read from. +""" +macro __MODULE__() + return __module__ +end + +""" + fullname(m::Module) + +Get the fully-qualified name of a module as a tuple of symbols. For example, + +# Examples +```jldoctest +julia> fullname(Base.Iterators) +(:Base, :Iterators) + +julia> fullname(Main) +(:Main,) +``` +""" +function fullname(m::Module) + @_total_meta + mn = nameof(m) + if m === Main || m === Base || m === Core + return (mn,) + end + mp = parentmodule(m) + if mp === m + return (mn,) + end + return (fullname(mp)..., mn) +end + +""" + moduleloc(m::Module) -> LineNumberNode + +Get the location of the `module` definition. +""" +function moduleloc(m::Module) + line = Ref{Int32}(0) + file = ccall(:jl_module_getloc, Ref{Symbol}, (Any, Ref{Int32}), m, line) + return LineNumberNode(Int(line[]), file) +end + +""" + names(x::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) -> Vector{Symbol} + +Get a vector of the public names of a `Module`, excluding deprecated names. +If `all` is true, then the list also includes non-public names defined in the module, +deprecated names, and compiler-generated names. +If `imported` is true, then names explicitly imported from other modules +are also included. +If `usings` is true, then names explicitly imported via `using` are also included. +Names are returned in sorted order. + +As a special case, all names defined in `Main` are considered \"public\", +since it is not idiomatic to explicitly mark names from `Main` as public. + +!!! note + `sym ∈ names(SomeModule)` does *not* imply `isdefined(SomeModule, sym)`. + `names` may return symbols marked with `public` or `export`, even if + they are not defined in the module. + +!!! warning + `names` may return duplicate names. The duplication happens, e.g. if an `import`ed name + conflicts with an already existing identifier. + +See also: [`Base.isexported`](@ref), [`Base.ispublic`](@ref), [`Base.@locals`](@ref), [`@__MODULE__`](@ref). +""" +names(m::Module; kwargs...) = sort!(unsorted_names(m; kwargs...)) +unsorted_names(m::Module; all::Bool=false, imported::Bool=false, usings::Bool=false) = + ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint, Cint), m, all, imported, usings) + +""" + isexported(m::Module, s::Symbol) -> Bool + +Returns whether a symbol is exported from a module. + +See also: [`ispublic`](@ref), [`names`](@ref) + +```jldoctest +julia> module Mod + export foo + public bar + end +Mod + +julia> Base.isexported(Mod, :foo) +true + +julia> Base.isexported(Mod, :bar) +false + +julia> Base.isexported(Mod, :baz) +false +``` +""" +isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0 + +""" + ispublic(m::Module, s::Symbol) -> Bool + +Returns whether a symbol is marked as public in a module. + +Exported symbols are considered public. + +!!! compat "Julia 1.11" + This function and the notion of publicity were added in Julia 1.11. + +See also: [`isexported`](@ref), [`names`](@ref) + +```jldoctest +julia> module Mod + export foo + public bar + end +Mod + +julia> Base.ispublic(Mod, :foo) +true + +julia> Base.ispublic(Mod, :bar) +true + +julia> Base.ispublic(Mod, :baz) +false +``` +""" +ispublic(m::Module, s::Symbol) = ccall(:jl_module_public_p, Cint, (Any, Any), m, s) != 0 + +# TODO: this is vaguely broken because it only works for explicit calls to +# `Base.deprecate`, not the @deprecated macro: +isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 + +""" + isbindingresolved(m::Module, s::Symbol) -> Bool + +Returns whether the binding of a symbol in a module is resolved. + +See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref) + +```jldoctest +julia> module Mod + foo() = 17 + end +Mod + +julia> Base.isbindingresolved(Mod, :foo) +true + +julia> Base.isbindingresolved(Mod, :bar) +false +``` +""" +isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0 + +function binding_module(m::Module, s::Symbol) + p = ccall(:jl_get_module_of_binding, Ptr{Cvoid}, (Any, Any), m, s) + p == C_NULL && return m + return unsafe_pointer_to_objref(p)::Module +end + +const _NAMEDTUPLE_NAME = NamedTuple.body.body.name + +function _fieldnames(@nospecialize t) + if t.name === _NAMEDTUPLE_NAME + if t.parameters[1] isa Tuple + return t.parameters[1] + else + throw(ArgumentError("type does not have definite field names")) + end + end + return t.name.names +end + +const BINDING_KIND_GLOBAL = 0x0 +const BINDING_KIND_CONST = 0x1 +const BINDING_KIND_CONST_IMPORT = 0x2 +const BINDING_KIND_IMPLICIT = 0x3 +const BINDING_KIND_EXPLICIT = 0x4 +const BINDING_KIND_IMPORTED = 0x5 +const BINDING_KIND_FAILED = 0x6 +const BINDING_KIND_DECLARED = 0x7 +const BINDING_KIND_GUARD = 0x8 + +function lookup_binding_partition(world::UInt, b::Core.Binding) + ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) +end + +function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) + ccall(:jl_get_globalref_partition, Ref{Core.BindingPartition}, (Any, UInt), gr, world) +end + +binding_kind(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_kind, UInt8, (Any,), bpart) +binding_kind(m::Module, s::Symbol) = binding_kind(lookup_binding_partition(tls_world_age(), GlobalRef(m, s))) + +""" + fieldname(x::DataType, i::Integer) + +Get the name of field `i` of a `DataType`. + +The return type is `Symbol`, except when `x <: Tuple`, in which case the index of the field is returned, of type `Int`. + +# Examples +```jldoctest +julia> fieldname(Rational, 1) +:num + +julia> fieldname(Rational, 2) +:den + +julia> fieldname(Tuple{String,Int}, 2) +2 +``` +""" +function fieldname(t::DataType, i::Integer) + throw_not_def_field() = throw(ArgumentError("type does not have definite field names")) + function throw_field_access(t, i, n_fields) + field_label = n_fields == 1 ? "field" : "fields" + throw(ArgumentError("Cannot access field $i since type $t only has $n_fields $field_label.")) + end + throw_need_pos_int(i) = throw(ArgumentError("Field numbers must be positive integers. $i is invalid.")) + + isabstracttype(t) && throw_not_def_field() + names = _fieldnames(t) + n_fields = length(names)::Int + i > n_fields && throw_field_access(t, i, n_fields) + i < 1 && throw_need_pos_int(i) + return @inbounds names[i]::Symbol +end + +fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) +fieldname(t::Type{<:Tuple}, i::Integer) = + i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i) + +""" + fieldnames(x::DataType) + +Get a tuple with the names of the fields of a `DataType`. + +Each name is a `Symbol`, except when `x <: Tuple`, in which case each name (actually the +index of the field) is an `Int`. + +See also [`propertynames`](@ref), [`hasfield`](@ref). + +# Examples +```jldoctest +julia> fieldnames(Rational) +(:num, :den) + +julia> fieldnames(typeof(1+im)) +(:re, :im) + +julia> fieldnames(Tuple{String,Int}) +(1, 2) +``` +""" +fieldnames(t::DataType) = (fieldcount(t); # error check to make sure type is specific enough + (_fieldnames(t)...,))::Tuple{Vararg{Symbol}} +fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t)) +fieldnames(::Core.TypeofBottom) = + throw(ArgumentError("The empty type does not have field names since it does not have instances.")) +fieldnames(t::Type{<:Tuple}) = ntuple(identity, fieldcount(t)) + +""" + hasfield(T::Type, name::Symbol) + +Return a boolean indicating whether `T` has `name` as one of its own fields. + +See also [`fieldnames`](@ref), [`fieldcount`](@ref), [`hasproperty`](@ref). + +!!! compat "Julia 1.2" + This function requires at least Julia 1.2. + +# Examples +```jldoctest +julia> struct Foo + bar::Int + end + +julia> hasfield(Foo, :bar) +true + +julia> hasfield(Foo, :x) +false +``` +""" +hasfield(T::Type, name::Symbol) = fieldindex(T, name, false) > 0 + +""" + nameof(t::DataType) -> Symbol + +Get the name of a (potentially `UnionAll`-wrapped) `DataType` (without its parent module) +as a symbol. + +# Examples +```jldoctest +julia> module Foo + struct S{T} + end + end +Foo + +julia> nameof(Foo.S{T} where T) +:S +``` +""" +nameof(t::DataType) = t.name.name +nameof(t::UnionAll) = nameof(unwrap_unionall(t))::Symbol + +""" + parentmodule(t::DataType) -> Module + +Determine the module containing the definition of a (potentially `UnionAll`-wrapped) `DataType`. + +# Examples +```jldoctest +julia> module Foo + struct Int end + end +Foo + +julia> parentmodule(Int) +Core + +julia> parentmodule(Foo.Int) +Foo +``` +""" +parentmodule(t::DataType) = t.name.module +parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) + +""" + isconst(m::Module, s::Symbol) -> Bool + +Determine whether a global is declared `const` in a given module `m`. +""" +isconst(m::Module, s::Symbol) = + ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 + +function isconst(g::GlobalRef) + return ccall(:jl_globalref_is_const, Cint, (Any,), g) != 0 +end + +""" + isconst(t::DataType, s::Union{Int,Symbol}) -> Bool + +Determine whether a field `s` is declared `const` in a given type `t`. +""" +function isconst(@nospecialize(t::Type), s::Symbol) + @_foldable_meta + t = unwrap_unionall(t) + isa(t, DataType) || return false + return isconst(t, fieldindex(t, s, false)) +end +function isconst(@nospecialize(t::Type), s::Int) + @_foldable_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false # uncertain + ismutabletype(t) || return true # immutable structs are always const + 1 <= s <= length(t.name.names) || return true # OOB reads are "const" since they always throw + constfields = t.name.constfields + constfields === C_NULL && return false + s -= 1 + return unsafe_load(Ptr{UInt32}(constfields), 1 + s÷32) & (1 << (s%32)) != 0 +end + +""" + isfieldatomic(t::DataType, s::Union{Int,Symbol}) -> Bool + +Determine whether a field `s` is declared `@atomic` in a given type `t`. +""" +function isfieldatomic(@nospecialize(t::Type), s::Symbol) + @_foldable_meta + t = unwrap_unionall(t) + isa(t, DataType) || return false + return isfieldatomic(t, fieldindex(t, s, false)) +end +function isfieldatomic(@nospecialize(t::Type), s::Int) + @_foldable_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false # uncertain + ismutabletype(t) || return false # immutable structs are never atomic + 1 <= s <= length(t.name.names) || return false # OOB reads are not atomic (they always throw) + atomicfields = t.name.atomicfields + atomicfields === C_NULL && return false + s -= 1 + return unsafe_load(Ptr{UInt32}(atomicfields), 1 + s÷32) & (1 << (s%32)) != 0 +end + +""" + @locals() + +Construct a dictionary of the names (as symbols) and values of all local +variables defined as of the call site. + +!!! compat "Julia 1.1" + This macro requires at least Julia 1.1. + +# Examples +```jldoctest +julia> let x = 1, y = 2 + Base.@locals + end +Dict{Symbol, Any} with 2 entries: + :y => 2 + :x => 1 + +julia> function f(x) + local y + show(Base.@locals); println() + for i = 1:1 + show(Base.@locals); println() + end + y = 2 + show(Base.@locals); println() + nothing + end; + +julia> f(42) +Dict{Symbol, Any}(:x => 42) +Dict{Symbol, Any}(:i => 1, :x => 42) +Dict{Symbol, Any}(:y => 2, :x => 42) +``` +""" +macro locals() + return Expr(:locals) +end + +# concrete datatype predicates + +datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Core.SimpleVector, (Any,), x) + +struct DataTypeLayout + size::UInt32 + nfields::UInt32 + npointers::UInt32 + firstptr::Int32 + alignment::UInt16 + flags::UInt16 + # haspadding : 1; + # fielddesc_type : 2; + # arrayelem_isboxed : 1; + # arrayelem_isunion : 1; +end + +""" + Base.datatype_alignment(dt::DataType) -> Int + +Memory allocation minimum alignment for instances of this type. +Can be called on any `isconcretetype`, although for Memory it will give the +alignment of the elements, not the whole object. +""" +function datatype_alignment(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return Int(alignment) +end + +function uniontype_layout(@nospecialize T::Type) + sz = RefValue{Csize_t}(0) + algn = RefValue{Csize_t}(0) + isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0 + (isinline, Int(sz[]), Int(algn[])) +end + +LLT_ALIGN(x, sz) = (x + sz - 1) & -sz + +# amount of total space taken by T when stored in a container +function aligned_sizeof(@nospecialize T::Type) + @_foldable_meta + if isa(T, Union) + if allocatedinline(T) + # NOTE this check is equivalent to `isbitsunion(T)`, we can improve type + # inference in the second branch with the outer `isa(T, Union)` check + _, sz, al = uniontype_layout(T) + return LLT_ALIGN(sz, al) + end + elseif allocatedinline(T) + al = datatype_alignment(T) + return LLT_ALIGN(Core.sizeof(T), al) + end + return Core.sizeof(Ptr{Cvoid}) +end + +gc_alignment(sz::Integer) = Int(ccall(:jl_alignment, Cint, (Csize_t,), sz)) +gc_alignment(T::Type) = gc_alignment(Core.sizeof(T)) + +""" + Base.datatype_haspadding(dt::DataType) -> Bool + +Return whether the fields of instances of this type are packed in memory, +with no intervening padding bits (defined as bits whose value does not impact +the semantic value of the instance itself). +Can be called on any `isconcretetype`. +""" +function datatype_haspadding(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return flags & 1 == 1 +end + +""" + Base.datatype_isbitsegal(dt::DataType) -> Bool + +Return whether egality of the (non-padding bits of the) in-memory representation +of an instance of this type implies semantic egality of the instance itself. +This may not be the case if the type contains to other values whose egality is +independent of their identity (e.g. immutable structs, some types, etc.). +""" +function datatype_isbitsegal(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return (flags & (1<<5)) != 0 +end + +""" + Base.datatype_nfields(dt::DataType) -> UInt32 + +Return the number of fields known to this datatype's layout. This may be +different from the number of actual fields of the type for opaque types. +Can be called on any `isconcretetype`. +""" +function datatype_nfields(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + 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 + +Return whether instances of this type can contain references to gc-managed memory. +Can be called on any `isconcretetype`. +""" +function datatype_pointerfree(dt::DataType) + @_foldable_meta + return datatype_npointers(dt) == 0 +end + +""" + Base.datatype_fielddesc_type(dt::DataType) -> Int + +Return the size in bytes of each field-description entry in the layout array, +located at `(dt.layout + sizeof(DataTypeLayout))`. +Can be called on any `isconcretetype`. + +See also [`fieldoffset`](@ref). +""" +function datatype_fielddesc_type(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return (flags >> 1) & 3 +end + +""" + Base.datatype_arrayelem(dt::DataType) -> Int + +Return the behavior of the trailing array types allocations. +Can be called on any `isconcretetype`, but only meaningful on `Memory`. + +0 = inlinealloc +1 = isboxed +2 = isbitsunion +""" +function datatype_arrayelem(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return (flags >> 3) & 3 +end + +function datatype_layoutsize(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + size = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).size + return size % Int +end + + +# For type stability, we only expose a single struct that describes everything +struct FieldDesc + isforeign::Bool + isptr::Bool + size::UInt32 + offset::UInt32 +end + +struct FieldDescStorage{T} + ptrsize::T + offset::T +end +FieldDesc(fd::FieldDescStorage{T}) where {T} = + FieldDesc(false, fd.ptrsize & 1 != 0, + fd.ptrsize >> 1, fd.offset) + +struct DataTypeFieldDesc + dt::DataType + function DataTypeFieldDesc(dt::DataType) + dt.layout == C_NULL && throw(UndefRefError()) + new(dt) + end +end + +function getindex(dtfd::DataTypeFieldDesc, i::Int) + layout_ptr = convert(Ptr{DataTypeLayout}, dtfd.dt.layout) + fd_ptr = layout_ptr + Core.sizeof(DataTypeLayout) + layout = unsafe_load(layout_ptr) + fielddesc_type = (layout.flags >> 1) & 3 + nfields = layout.nfields + @boundscheck ((1 <= i <= nfields) || throw(BoundsError(dtfd, i))) + if fielddesc_type == 0 + return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt8}}(fd_ptr), i)) + elseif fielddesc_type == 1 + return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt16}}(fd_ptr), i)) + elseif fielddesc_type == 2 + return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt32}}(fd_ptr), i)) + else + # fielddesc_type == 3 + return FieldDesc(true, true, 0, 0) + end +end + +""" + ismutable(v) -> Bool + +Return `true` if and only if value `v` is mutable. See [Mutable Composite Types](@ref) +for a discussion of immutability. Note that this function works on values, so if you +give it a `DataType`, it will tell you that a value of the type is mutable. + +!!! note + For technical reasons, `ismutable` returns `true` for values of certain special types + (for example `String` and `Symbol`) even though they cannot be mutated in a permissible way. + +See also [`isbits`](@ref), [`isstructtype`](@ref). + +# Examples +```jldoctest +julia> ismutable(1) +false + +julia> ismutable([1,2]) +true +``` + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. +""" +ismutable(@nospecialize(x)) = (@_total_meta; (typeof(x).name::Core.TypeName).flags & 0x2 == 0x2) +# The type assertion above is required to fix some invalidations. +# See also https://github.com/JuliaLang/julia/issues/52134 + +""" + ismutabletype(T) -> Bool + +Determine whether type `T` was declared as a mutable type +(i.e. using `mutable struct` keyword). +If `T` is not a type, then return `false`. + +!!! compat "Julia 1.7" + This function requires at least Julia 1.7. +""" +function ismutabletype(@nospecialize t) + @_total_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + return isa(t, DataType) && ismutabletypename(t.name) +end + +ismutabletypename(tn::Core.TypeName) = tn.flags & 0x2 == 0x2 + +""" + isstructtype(T) -> Bool + +Determine whether type `T` was declared as a struct type +(i.e. using the `struct` or `mutable struct` keyword). +If `T` is not a type, then return `false`. +""" +function isstructtype(@nospecialize t) + @_total_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + return !isprimitivetype(t) && !isabstracttype(t) +end + +""" + isprimitivetype(T) -> Bool + +Determine whether type `T` was declared as a primitive type +(i.e. using the `primitive type` syntax). +If `T` is not a type, then return `false`. +""" +function isprimitivetype(@nospecialize t) + @_total_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + return (t.flags & 0x0080) == 0x0080 +end + +""" + isbitstype(T) + +Return `true` if type `T` is a "plain data" type, +meaning it is immutable and contains no references to other values, +only `primitive` types and other `isbitstype` types. +Typical examples are numeric types such as [`UInt8`](@ref), +[`Float64`](@ref), and [`Complex{Float64}`](@ref). +This category of types is significant since they are valid as type parameters, +may not track [`isdefined`](@ref) / [`isassigned`](@ref) status, +and have a defined layout that is compatible with C. +If `T` is not a type, then return `false`. + +See also [`isbits`](@ref), [`isprimitivetype`](@ref), [`ismutable`](@ref). + +# Examples +```jldoctest +julia> isbitstype(Complex{Float64}) +true + +julia> isbitstype(Complex) +false +``` +""" +isbitstype(@nospecialize t) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0008) == 0x0008) + +""" + isbits(x) + +Return `true` if `x` is an instance of an [`isbitstype`](@ref) type. +""" +isbits(@nospecialize x) = isbitstype(typeof(x)) + +""" + objectid(x) -> UInt + +Get a hash value for `x` based on object identity. + +If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. + +See also [`hash`](@ref), [`IdDict`](@ref). +""" +function objectid(@nospecialize(x)) + @_total_meta + return ccall(:jl_object_id, UInt, (Any,), x) +end + +""" + isdispatchtuple(T) + +Determine whether type `T` is a tuple "leaf type", +meaning it could appear as a type signature in dispatch +and has no subtypes (or supertypes) which could appear in a call. +If `T` is not a type, then return `false`. +""" +isdispatchtuple(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0004) == 0x0004) + +datatype_ismutationfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0100) == 0x0100) + +""" + Base.ismutationfree(T) + +Determine whether type `T` is mutation free in the sense that no mutable memory +is reachable from this type (either in the type itself) or through any fields. +Note that the type itself need not be immutable. For example, an empty mutable +type is `ismutabletype`, but also `ismutationfree`. +If `T` is not a type, then return `false`. +""" +function ismutationfree(@nospecialize(t)) + t = unwrap_unionall(t) + if isa(t, DataType) + return datatype_ismutationfree(t) + elseif isa(t, Union) + return ismutationfree(t.a) && ismutationfree(t.b) + end + # TypeVar, etc. + return false +end + +datatype_isidentityfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0200) == 0x0200) + +""" + Base.isidentityfree(T) + +Determine whether type `T` is identity free in the sense that this type or any +reachable through its fields has non-content-based identity. +If `T` is not a type, then return `false`. +""" +function isidentityfree(@nospecialize(t)) + t = unwrap_unionall(t) + if isa(t, DataType) + return datatype_isidentityfree(t) + elseif isa(t, Union) + return isidentityfree(t.a) && isidentityfree(t.b) + end + # TypeVar, etc. + return false +end + +iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) +isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) + +using Core: has_free_typevars + +# equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} +# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query +function isdispatchelem(@nospecialize v) + return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) || + (isType(v) && !has_free_typevars(v)) +end + +const _TYPE_NAME = Type.body.name +isType(@nospecialize t) = isa(t, DataType) && t.name === _TYPE_NAME + +""" + isconcretetype(T) + +Determine whether type `T` is a concrete type, meaning it could have direct instances +(values `x` such that `typeof(x) === T`). +Note that this is not the negation of `isabstracttype(T)`. +If `T` is not a type, then return `false`. + +See also: [`isbits`](@ref), [`isabstracttype`](@ref), [`issingletontype`](@ref). + +# Examples +```jldoctest +julia> isconcretetype(Complex) +false + +julia> isconcretetype(Complex{Float32}) +true + +julia> isconcretetype(Vector{Complex}) +true + +julia> isconcretetype(Vector{Complex{Float32}}) +true + +julia> isconcretetype(Union{}) +false + +julia> isconcretetype(Union{Int,String}) +false +``` +""" +isconcretetype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0002) == 0x0002) + +""" + isabstracttype(T) + +Determine whether type `T` was declared as an abstract type +(i.e. using the `abstract type` syntax). +Note that this is not the negation of `isconcretetype(T)`. +If `T` is not a type, then return `false`. + +# Examples +```jldoctest +julia> isabstracttype(AbstractArray) +true + +julia> isabstracttype(Vector) +false +``` +""" +function isabstracttype(@nospecialize(t)) + @_total_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + return isa(t, DataType) && (t.name.flags & 0x1) == 0x1 +end + +function is_datatype_layoutopaque(dt::DataType) + datatype_nfields(dt) == 0 && !datatype_pointerfree(dt) +end + +function is_valid_intrinsic_elptr(@nospecialize(ety)) + ety === Any && return true + isconcretetype(ety) || return false + ety <: Array && return false + return !is_datatype_layoutopaque(ety) +end + +""" + Base.issingletontype(T) + +Determine whether type `T` has exactly one possible instance; for example, a +struct type with no fields except other singleton values. +If `T` is not a concrete type, then return `false`. +""" +issingletontype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && isdefined(t, :instance) && datatype_layoutsize(t) == 0 && datatype_pointerfree(t)) + +""" + typeintersect(T::Type, S::Type) + +Compute a type that contains the intersection of `T` and `S`. Usually this will be the +smallest such type or one close to it. + +A special case where exact behavior is guaranteed: when `T <: S`, +`typeintersect(S, T) == T == typeintersect(T, S)`. +""" +typeintersect(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)) + +morespecific(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_morespecific, Cint, (Any, Any), a::Type, b::Type) != 0) +morespecific(a::Method, b::Method) = ccall(:jl_method_morespecific, Cint, (Any, Any), a, b) != 0 + +""" + fieldoffset(type, i) + +The byte offset of field `i` of a type relative to the data start. For example, we could +use it in the following manner to summarize information about a struct: + +```jldoctest +julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)]; + +julia> structinfo(Base.Filesystem.StatStruct) +14-element Vector{Tuple{UInt64, Symbol, Type}}: + (0x0000000000000000, :desc, Union{RawFD, String}) + (0x0000000000000008, :device, UInt64) + (0x0000000000000010, :inode, UInt64) + (0x0000000000000018, :mode, UInt64) + (0x0000000000000020, :nlink, Int64) + (0x0000000000000028, :uid, UInt64) + (0x0000000000000030, :gid, UInt64) + (0x0000000000000038, :rdev, UInt64) + (0x0000000000000040, :size, Int64) + (0x0000000000000048, :blksize, Int64) + (0x0000000000000050, :blocks, Int64) + (0x0000000000000058, :mtime, Float64) + (0x0000000000000060, :ctime, Float64) + (0x0000000000000068, :ioerrno, Int32) +``` +""" +fieldoffset(x::DataType, idx::Integer) = (@_foldable_meta; ccall(:jl_get_field_offset, Csize_t, (Any, Cint), x, idx)) + +""" + fieldtype(T, name::Symbol | index::Int) + +Determine the declared type of a field (specified by name or index) in a composite DataType `T`. + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> fieldtype(Foo, :x) +Int64 + +julia> fieldtype(Foo, 2) +String +``` +""" +fieldtype + +""" + Base.fieldindex(T, name::Symbol, err:Bool=true) + +Get the index of a named field, throwing an error if the field does not exist (when err==true) +or returning 0 (when err==false). + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> Base.fieldindex(Foo, :z) +ERROR: FieldError: type Foo has no field `z`, available fields: `x`, `y` +Stacktrace: +[...] + +julia> Base.fieldindex(Foo, :z, false) +0 +``` +""" +function fieldindex(T::DataType, name::Symbol, err::Bool=true) + return err ? _fieldindex_maythrow(T, name) : _fieldindex_nothrow(T, name) +end + +function _fieldindex_maythrow(T::DataType, name::Symbol) + @_foldable_meta + @noinline + return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, true)+1) +end + +function _fieldindex_nothrow(T::DataType, name::Symbol) + @_total_meta + @noinline + return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, false)+1) +end + +function fieldindex(t::UnionAll, name::Symbol, err::Bool=true) + t = argument_datatype(t) + if t === nothing + err && throw(ArgumentError("type does not have definite fields")) + return 0 + end + return fieldindex(t, name, err) +end + +function argument_datatype(@nospecialize t) + @_total_meta + @noinline + return ccall(:jl_argument_datatype, Any, (Any,), t)::Union{Nothing,DataType} +end + +function datatype_fieldcount(t::DataType) + if t.name === _NAMEDTUPLE_NAME + names, types = t.parameters[1], t.parameters[2] + if names isa Tuple + return length(names) + end + if types isa DataType && types <: Tuple + return fieldcount(types) + end + return nothing + elseif isabstracttype(t) + return nothing + end + if t.name === Tuple.name + isvatuple(t) && return nothing + return length(t.types) + end + # Equivalent to length(t.types), but `t.types` is lazy and we do not want + # to be forced to compute it. + return length(t.name.names) +end + +""" + fieldcount(t::Type) + +Get the number of fields that an instance of the given type would have. +An error is thrown if the type is too abstract to determine this. +""" +function fieldcount(@nospecialize t) + @_foldable_meta + if t isa UnionAll || t isa Union + t = argument_datatype(t) + if t === nothing + throw(ArgumentError("type does not have a definite number of fields")) + end + elseif t === Union{} + throw(ArgumentError("The empty type does not have a well-defined number of fields since it does not have instances.")) + end + if !(t isa DataType) + throw(TypeError(:fieldcount, DataType, t)) + end + fcount = datatype_fieldcount(t) + if fcount === nothing + throw(ArgumentError("type does not have a definite number of fields")) + end + return fcount +end + +""" + fieldtypes(T::Type) + +The declared types of all fields in a composite DataType `T` as a tuple. + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> fieldtypes(Foo) +(Int64, String) +``` +""" +fieldtypes(T::Type) = (@_foldable_meta; ntupleany(i -> fieldtype(T, i), fieldcount(T))) + +# return all instances, for types that can be enumerated + +""" + instances(T::Type) + +Return a collection of all instances of the given type, if applicable. Mostly used for +enumerated types (see `@enum`). + +# Examples +```jldoctest +julia> @enum Color red blue green + +julia> instances(Color) +(red, blue, green) +``` +""" +function instances end + +function to_tuple_type(@nospecialize(t)) + if isa(t, Tuple) || isa(t, AbstractArray) || isa(t, SimpleVector) + t = Tuple{t...} + end + if isa(t, Type) && t <: Tuple + for p in (unwrap_unionall(t)::DataType).parameters + if isa(p, Core.TypeofVararg) + p = unwrapva(p) + end + if !(isa(p, Type) || isa(p, TypeVar)) + error("argument tuple type must contain only types") + end + end + else + error("expected tuple type") + end + t +end + +function signature_type(@nospecialize(f), @nospecialize(argtypes)) + argtypes = to_tuple_type(argtypes) + ft = Core.Typeof(f) + u = unwrap_unionall(argtypes)::DataType + return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) +end + +function get_methodtable(m::Method) + mt = ccall(:jl_method_get_table, Any, (Any,), m) + if mt === nothing + return nothing + end + return mt::Core.MethodTable +end + +""" + has_bottom_parameter(t) -> Bool + +Determine whether `t` is a Type for which one or more of its parameters is `Union{}`. +""" +function has_bottom_parameter(t::DataType) + for p in t.parameters + has_bottom_parameter(p) && return true + end + return false +end +has_bottom_parameter(t::typeof(Bottom)) = true +has_bottom_parameter(t::UnionAll) = has_bottom_parameter(unwrap_unionall(t)) +has_bottom_parameter(t::Union) = has_bottom_parameter(t.a) & has_bottom_parameter(t.b) +has_bottom_parameter(t::TypeVar) = has_bottom_parameter(t.ub) +has_bottom_parameter(::Any) = false + +min_world(m::Core.CodeInstance) = m.min_world +max_world(m::Core.CodeInstance) = m.max_world +min_world(m::Core.CodeInfo) = m.min_world +max_world(m::Core.CodeInfo) = m.max_world + +""" + get_world_counter() + +Returns the current maximum world-age counter. This counter is global and monotonically +increasing. +""" +get_world_counter() = ccall(:jl_get_world_counter, UInt, ()) + +""" + tls_world_age() + +Returns the world the [current_task()](@ref) is executing within. +""" +tls_world_age() = ccall(:jl_get_tls_world_age, UInt, ()) + +""" + propertynames(x, private=false) + +Get a tuple or a vector of the properties (`x.property`) of an object `x`. +This is typically the same as [`fieldnames(typeof(x))`](@ref), but types +that overload [`getproperty`](@ref) should generally overload `propertynames` +as well to get the properties of an instance of the type. + +`propertynames(x)` may return only "public" property names that are part +of the documented interface of `x`. If you want it to also return "private" +property names intended for internal use, pass `true` for the optional second argument. +REPL tab completion on `x.` shows only the `private=false` properties. + +See also: [`hasproperty`](@ref), [`hasfield`](@ref). +""" +propertynames(x) = fieldnames(typeof(x)) +propertynames(m::Module) = names(m) +propertynames(x, private::Bool) = propertynames(x) # ignore private flag by default +propertynames(x::Array) = () # hide the fields from tab completion to discourage calling `x.size` instead of `size(x)`, even though they are equivalent + +""" + hasproperty(x, s::Symbol) + +Return a boolean indicating whether the object `x` has `s` as one of its own properties. + +!!! compat "Julia 1.2" + This function requires at least Julia 1.2. + +See also: [`propertynames`](@ref), [`hasfield`](@ref). +""" +hasproperty(x, s::Symbol) = s in propertynames(x) + +""" + delete_method(m::Method) + +Make method `m` uncallable and force recompilation of any methods that use(d) it. +""" +function delete_method(m::Method) + ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) +end + + +# type for reflecting and pretty-printing a subset of methods +mutable struct MethodList <: AbstractArray{Method,1} + ms::Array{Method,1} + mt::Core.MethodTable +end + +size(m::MethodList) = size(m.ms) +getindex(m::MethodList, i::Integer) = m.ms[i] + +function MethodList(mt::Core.MethodTable) + ms = Method[] + visit(mt) do m + push!(ms, m) + end + return MethodList(ms, mt) +end + +""" + methods(f, [types], [module]) + +Return the method table for `f`. + +If `types` is specified, return an array of methods whose types match. +If `module` is specified, return an array of methods defined in that module. +A list of modules can also be specified as an array. + +!!! compat "Julia 1.4" + At least Julia 1.4 is required for specifying a module. + +See also: [`which`](@ref), [`@which`](@ref Main.InteractiveUtils.@which) and [`methodswith`](@ref Main.InteractiveUtils.methodswith). +""" +function methods(@nospecialize(f), @nospecialize(t), + mod::Union{Tuple{Module},AbstractArray{Module},Nothing}=nothing) + world = get_world_counter() + world == typemax(UInt) && error("code reflection cannot be used from generated functions") + # Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually + ms = Method[] + for m in _methods(f, t, -1, world)::Vector + m = m::Core.MethodMatch + (mod === nothing || parentmodule(m.method) ∈ mod) && push!(ms, m.method) + end + MethodList(ms, typeof(f).name.mt) +end +methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) + +function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) + tt = signature_type(f, t) + world = get_world_counter() + world == typemax(UInt) && error("code reflection cannot be used from generated functions") + min = RefValue{UInt}(typemin(UInt)) + max = RefValue{UInt}(typemax(UInt)) + ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector + return MethodList(Method[(m::Core.MethodMatch).method for m in ms], typeof(f).name.mt) +end + +function methods(@nospecialize(f), + mod::Union{Module,AbstractArray{Module},Nothing}=nothing) + # return all matches + return methods(f, Tuple{Vararg{Any}}, mod) +end + +# low-level method lookup functions used by the compiler + +unionlen(@nospecialize(x)) = x isa Union ? unionlen(x.a) + unionlen(x.b) : 1 + +function _uniontypes(@nospecialize(x), ts::Array{Any,1}) + if x isa Union + _uniontypes(x.a, ts) + _uniontypes(x.b, ts) + else + push!(ts, x) + end + return ts +end +uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) + +function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) + tt = signature_type(f, t) + return _methods_by_ftype(tt, lim, world) +end + +function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt) + return _methods_by_ftype(t, nothing, lim, world) +end +function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt) + return _methods_by_ftype(t, mt, lim, world, false, RefValue{UInt}(typemin(UInt)), RefValue{UInt}(typemax(UInt)), Ptr{Int32}(C_NULL)) +end +function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt, ambig::Bool, min::Ref{UInt}, max::Ref{UInt}, has_ambig::Ref{Int32}) + return ccall(:jl_matching_methods, Any, (Any, Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}, Ptr{Int32}), t, mt, lim, ambig, world, min, max, has_ambig)::Union{Vector{Any},Nothing} +end + +hasgenerator(m::Method) = isdefined(m, :generator) +hasgenerator(m::Core.MethodInstance) = hasgenerator(m.def::Method) + +""" + Base.generating_output([incremental::Bool])::Bool + +Return `true` if the current process is being used to pre-generate a +code cache via any of the `--output-*` command line arguments. The optional +`incremental` argument further specifies the precompilation mode: when set +to `true`, the function will return `true` only for package precompilation; +when set to `false`, it will return `true` only for system image generation. + +!!! compat "Julia 1.11" + This function requires at least Julia 1.11. +""" +function generating_output(incremental::Union{Bool,Nothing}=nothing) + ccall(:jl_generating_output, Cint, ()) == 0 && return false + if incremental !== nothing + JLOptions().incremental == incremental || return false + end + return true +end + +const SLOT_USED = 0x8 +ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_slotflag, UInt8, (Any, Csize_t), code, i - 1) + +""" + may_invoke_generator(method, atype, sparams) -> Bool + +Computes whether or not we may invoke the generator for the given `method` on +the given `atype` and `sparams`. For correctness, all generated function are +required to return monotonic answers. However, since we don't expect users to +be able to successfully implement this criterion, we only call generated +functions on concrete types. The one exception to this is that we allow calling +generators with abstract types if the generator does not use said abstract type +(and thus cannot incorrectly use it to break monotonicity). This function +computes whether we are in either of these cases. + +Unlike normal functions, the compilation heuristics still can't generate good dispatch +in some cases, but this may still allow inference not to fall over in some limited cases. +""" +function may_invoke_generator(mi::MethodInstance) + return may_invoke_generator(mi.def::Method, mi.specTypes, mi.sparam_vals) +end +function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector) + # If we have complete information, we may always call the generator + isdispatchtuple(atype) && return true + + # We don't have complete information, but it is possible that the generator + # syntactically doesn't make use of the information we don't have. Check + # for that. + + # For now, only handle the (common, generated by the frontend case) that the + # generator only has one method + generator = method.generator + isa(generator, Core.GeneratedFunctionStub) || return false + tt = Tuple{typeof(generator.gen), Vararg{Any}} + gen_mthds = _methods_by_ftype(tt, #=lim=#1, method.primary_world) + gen_mthds isa Vector || return false + length(gen_mthds) == 1 || return false + + generator_method = (first(gen_mthds)::Core.MethodMatch).method + nsparams = length(sparams) + isdefined(generator_method, :source) || return false + code = generator_method.source + nslots = ccall(:jl_ir_nslots, Int, (Any,), code) + at = unwrap_unionall(atype) + at isa DataType || return false + (nslots >= 1 + length(sparams) + length(at.parameters)) || return false + + firstarg = 1 + for i = 1:nsparams + if isa(sparams[i], TypeVar) + if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 + return false + end + end + end + nargs = Int(method.nargs) + non_va_args = method.isva ? nargs - 1 : nargs + for i = 1:non_va_args + if !isdispatchelem(at.parameters[i]) + if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 + return false + end + end + end + if method.isva + # If the va argument is used, we need to ensure that all arguments that + # contribute to the va tuple are dispatchelemes + if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 + for i = (non_va_args+1):length(at.parameters) + if !isdispatchelem(at.parameters[i]) + return false + end + end + end + end + return true +end + +# get a handle to the unique specialization object representing a particular instantiation of a call +# eliminate UnionAll vars that might be degenerate due to having identical bounds, +# or a concrete upper bound and appearing covariantly. +function subst_trivial_bounds(@nospecialize(atype)) + if !isa(atype, UnionAll) + return atype + end + v = atype.var + if isconcretetype(v.ub) || v.lb === v.ub + subst = try + atype{v.ub} + catch + # Note in rare cases a var bound might not be valid to substitute. + nothing + end + if subst !== nothing + return subst_trivial_bounds(subst) + end + end + return UnionAll(v, subst_trivial_bounds(atype.body)) +end + +# If removing trivial vars from atype results in an equivalent type, use that +# instead. Otherwise we can get a case like issue #38888, where a signature like +# f(x::S) where S<:Int +# gets cached and matches a concrete dispatch case. +function normalize_typevars(method::Method, @nospecialize(atype), sparams::SimpleVector) + at2 = subst_trivial_bounds(atype) + if at2 !== atype && at2 == atype + atype = at2 + sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), at2, method.sig)::SimpleVector + sparams = sp_[2]::SimpleVector + end + return Pair{Any,SimpleVector}(atype, sparams) +end + +function get_nospecializeinfer_sig(method::Method, @nospecialize(atype), sparams::SimpleVector) + isa(atype, DataType) || return method.sig + mt = ccall(:jl_method_get_table, Any, (Any,), method) + mt === nothing && return method.sig + return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any, Cint), + mt, atype, sparams, method, #=int return_if_compileable=#0) +end + +is_nospecialized(method::Method) = method.nospecialize ≠ 0 +is_nospecializeinfer(method::Method) = method.nospecializeinfer && is_nospecialized(method) +function specialize_method(method::Method, @nospecialize(atype), sparams::SimpleVector; preexisting::Bool=false) + @inline + if isa(atype, UnionAll) + atype, sparams = normalize_typevars(method, atype, sparams) + end + if is_nospecializeinfer(method) + atype = get_nospecializeinfer_sig(method, atype, sparams) + end + if preexisting + # check cached specializations + # for an existing result stored there + return ccall(:jl_specializations_lookup, Any, (Any, Any), method, atype)::Union{Nothing,MethodInstance} + end + return ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), method, atype, sparams) +end + +function specialize_method(match::Core.MethodMatch; kwargs...) + return specialize_method(match.method, match.spec_types, match.sparams; kwargs...) +end + +hasintersect(@nospecialize(a), @nospecialize(b)) = typeintersect(a, b) !== Bottom diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e3b1ac499e986..71f9da04baa4a 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2647,7 +2647,7 @@ g26826(x) = getfield26826(x, :a, :b) # If this test is broken (especially if inference is getting a correct, but loose result, # like a Union) then it's potentially an indication that the optimizer isn't hitting the # InferenceResult cache properly for varargs methods. -let ct = Core.Compiler.code_typed(f26826, (Float64,))[1] +let ct = code_typed(f26826, (Float64,))[1] typed_code, retty = ct.first, ct.second found_poorly_typed_getfield_call = false for i = 1:length(typed_code.code) From 7ee3ba912e5232b2e73186c9c28597d4db3550b6 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 17 Oct 2024 01:20:32 -0400 Subject: [PATCH 418/548] Move EffectsOverride to expr.jl (#56187) It makes sense that we originally added this to the compiler, but these annotations are really a runtime feature that the compiler simply reads to allow it to make additional assumptions. The runtime should not semantically depend on the compiler for this, so move these definitions to expr.jl. The practical effect of this right now is that Base gains a second copy of this code. Post #56128, the compiler will use the Base copy of this. Split out from #56128. --- base/compiler/compiler.jl | 41 -------------------- base/compiler/effects.jl | 31 --------------- base/experimental.jl | 2 +- base/expr.jl | 79 +++++++++++++++++++++++++++++++++++++-- src/julia.h | 2 +- test/strings/basic.jl | 2 +- 6 files changed, 79 insertions(+), 78 deletions(-) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index dbfc9d7d57140..7d1dba88c9011 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -38,47 +38,6 @@ convert(::Type{T}, x::T) where {T} = x # Note that `@assume_effects` is available only after loading namedtuple.jl. abstract type MethodTableView end abstract type AbstractInterpreter end -struct EffectsOverride - consistent::Bool - effect_free::Bool - nothrow::Bool - terminates_globally::Bool - terminates_locally::Bool - notaskstate::Bool - inaccessiblememonly::Bool - noub::Bool - noub_if_noinbounds::Bool - consistent_overlay::Bool - nortcall::Bool -end -function EffectsOverride( - override::EffectsOverride = - EffectsOverride(false, false, false, false, false, false, false, false, false, false, false); - consistent::Bool = override.consistent, - effect_free::Bool = override.effect_free, - nothrow::Bool = override.nothrow, - terminates_globally::Bool = override.terminates_globally, - terminates_locally::Bool = override.terminates_locally, - notaskstate::Bool = override.notaskstate, - inaccessiblememonly::Bool = override.inaccessiblememonly, - noub::Bool = override.noub, - noub_if_noinbounds::Bool = override.noub_if_noinbounds, - consistent_overlay::Bool = override.consistent_overlay, - nortcall::Bool = override.nortcall) - return EffectsOverride( - consistent, - effect_free, - nothrow, - terminates_globally, - terminates_locally, - notaskstate, - inaccessiblememonly, - noub, - noub_if_noinbounds, - consistent_overlay, - nortcall) -end -const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h # essential files and libraries include("essentials.jl") diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index fb35162134ffa..3d9b69360b317 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -355,36 +355,5 @@ function decode_effects(e::UInt32) _Bool((e >> 14) & 0x01)) end -function encode_effects_override(eo::EffectsOverride) - e = 0x0000 - eo.consistent && (e |= (0x0001 << 0)) - eo.effect_free && (e |= (0x0001 << 1)) - eo.nothrow && (e |= (0x0001 << 2)) - eo.terminates_globally && (e |= (0x0001 << 3)) - eo.terminates_locally && (e |= (0x0001 << 4)) - eo.notaskstate && (e |= (0x0001 << 5)) - eo.inaccessiblememonly && (e |= (0x0001 << 6)) - eo.noub && (e |= (0x0001 << 7)) - eo.noub_if_noinbounds && (e |= (0x0001 << 8)) - eo.consistent_overlay && (e |= (0x0001 << 9)) - eo.nortcall && (e |= (0x0001 << 10)) - return e -end - -function decode_effects_override(e::UInt16) - return EffectsOverride( - !iszero(e & (0x0001 << 0)), - !iszero(e & (0x0001 << 1)), - !iszero(e & (0x0001 << 2)), - !iszero(e & (0x0001 << 3)), - !iszero(e & (0x0001 << 4)), - !iszero(e & (0x0001 << 5)), - !iszero(e & (0x0001 << 6)), - !iszero(e & (0x0001 << 7)), - !iszero(e & (0x0001 << 8)), - !iszero(e & (0x0001 << 9)), - !iszero(e & (0x0001 << 10))) -end - decode_statement_effects_override(ssaflag::UInt32) = decode_effects_override(UInt16((ssaflag >> NUM_IR_FLAGS) & (1 << NUM_EFFECTS_OVERRIDES - 1))) diff --git a/base/experimental.jl b/base/experimental.jl index 648b5da0ed9a1..982ed5e78aa8c 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -420,7 +420,7 @@ 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) + override = Base.EffectsOverride(; consistent_overlay=true) Base.pushmeta!(def::Expr, Base.form_purity_expr(override)) return esc(def) end diff --git a/base/expr.jl b/base/expr.jl index 723b6b5b636c8..e281d9b677297 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -757,7 +757,7 @@ macro assume_effects(args...) 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)) + insert!(lastex.args, 3, encode_effects_override(override)) return esc(lastex) end override′ = compute_assumed_setting(override, lastex) @@ -784,7 +784,49 @@ function compute_assumed_settings(settings) return override end -using Core.Compiler: EffectsOverride +struct EffectsOverride + consistent::Bool + effect_free::Bool + nothrow::Bool + terminates_globally::Bool + terminates_locally::Bool + notaskstate::Bool + inaccessiblememonly::Bool + noub::Bool + noub_if_noinbounds::Bool + consistent_overlay::Bool + nortcall::Bool +end + +function EffectsOverride( + override::EffectsOverride = + EffectsOverride(false, false, false, false, false, false, false, false, false, false, false); + consistent::Bool = override.consistent, + effect_free::Bool = override.effect_free, + nothrow::Bool = override.nothrow, + terminates_globally::Bool = override.terminates_globally, + terminates_locally::Bool = override.terminates_locally, + notaskstate::Bool = override.notaskstate, + inaccessiblememonly::Bool = override.inaccessiblememonly, + noub::Bool = override.noub, + noub_if_noinbounds::Bool = override.noub_if_noinbounds, + consistent_overlay::Bool = override.consistent_overlay, + nortcall::Bool = override.nortcall) + return EffectsOverride( + consistent, + effect_free, + nothrow, + terminates_globally, + terminates_locally, + notaskstate, + inaccessiblememonly, + noub, + noub_if_noinbounds, + consistent_overlay, + nortcall) +end + +const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h function compute_assumed_setting(override::EffectsOverride, @nospecialize(setting), val::Bool=true) if isexpr(setting, :call) && setting.args[1] === :(!) @@ -826,9 +868,40 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin return nothing end +function encode_effects_override(eo::EffectsOverride) + e = 0x0000 + eo.consistent && (e |= (0x0001 << 0)) + eo.effect_free && (e |= (0x0001 << 1)) + eo.nothrow && (e |= (0x0001 << 2)) + eo.terminates_globally && (e |= (0x0001 << 3)) + eo.terminates_locally && (e |= (0x0001 << 4)) + eo.notaskstate && (e |= (0x0001 << 5)) + eo.inaccessiblememonly && (e |= (0x0001 << 6)) + eo.noub && (e |= (0x0001 << 7)) + eo.noub_if_noinbounds && (e |= (0x0001 << 8)) + eo.consistent_overlay && (e |= (0x0001 << 9)) + eo.nortcall && (e |= (0x0001 << 10)) + return e +end + +function decode_effects_override(e::UInt16) + return EffectsOverride( + !iszero(e & (0x0001 << 0)), + !iszero(e & (0x0001 << 1)), + !iszero(e & (0x0001 << 2)), + !iszero(e & (0x0001 << 3)), + !iszero(e & (0x0001 << 4)), + !iszero(e & (0x0001 << 5)), + !iszero(e & (0x0001 << 6)), + !iszero(e & (0x0001 << 7)), + !iszero(e & (0x0001 << 8)), + !iszero(e & (0x0001 << 9)), + !iszero(e & (0x0001 << 10))) +end + function form_purity_expr(override::EffectsOverride) ex = Expr(:purity) - for i = 1:Core.Compiler.NUM_EFFECTS_OVERRIDES + for i = 1:NUM_EFFECTS_OVERRIDES push!(ex.args, getfield(override, i)) end return ex diff --git a/src/julia.h b/src/julia.h index 46679da9714dc..f42ac2a23aaeb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -256,7 +256,7 @@ typedef struct _jl_debuginfo_t { jl_value_t *codelocs; // String // Memory{UInt8} // compressed info } jl_debuginfo_t; -// the following mirrors `struct EffectsOverride` in `base/compiler/effects.jl` +// the following mirrors `struct EffectsOverride` in `base/expr.jl` typedef union __jl_purity_overrides_t { struct { uint16_t ipo_consistent : 1; diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 874607f3c1b20..de04055d047af 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1236,7 +1236,7 @@ end end @test_throws ArgumentError Symbol("a\0a") - @test Base._string_n_override == Core.Compiler.encode_effects_override(Base.compute_assumed_settings((:total, :(!:consistent)))) + @test Base._string_n_override == Base.encode_effects_override(Base.compute_assumed_settings((:total, :(!:consistent)))) end @testset "Ensure UTF-8 DFA can never leave invalid state" begin From bbd81580cdcd059176792982308fbf908771847e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20S=C3=A1nchez=20Ram=C3=ADrez?= <15837247+mofeing@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:07:25 +0200 Subject: [PATCH 419/548] Fix some grammatical errors on docstring of `GenericMemory` (#56197) --- base/genericmemory.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 5fe070a73628d..89861444d9652 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -9,8 +9,7 @@ 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: +`addrspace` can currently only be set to `Core.CPU`. It is designed to permit extension by other systems such as GPUs, which might define values such as: ``` module CUDA const Generic = bitcast(Core.AddrSpace{CUDA}, 0) From 727a57ed4fcbf99916678d719e131de2c59cf054 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 17 Oct 2024 12:00:26 +0530 Subject: [PATCH 420/548] Read views of destination in adjoint * adjoint (#56138) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, add an aggressive constprop annotation to `generic_matvecmul!`. Together, these improve performance: ```julia julia> A = rand(Int,100,100); julia> @btime $A' * $A'; 290.203 μs (405 allocations: 175.98 KiB) # v"1.12.0-DEV.1364" 270.008 μs (5 allocations: 79.11 KiB) # This PR ``` --- stdlib/LinearAlgebra/src/matmul.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 02ecd74152531..f64422fd9cb8a 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -825,7 +825,7 @@ end # NOTE: the generic version is also called as fallback for # strides != 1 cases -generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, alpha::Number, beta::Number) = +Base.@constprop :aggressive generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, alpha::Number, beta::Number) = @stable_muladdmul generic_matvecmul!(C, tA, A, B, MulAddMul(alpha, beta)) @inline function generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) @@ -957,7 +957,7 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A ta = t(_add.alpha) for i in AxM mul!(tmp, pB, view(pA, :, i)) - C[ci,:] .+= t.(ta .* tmp) + @views C[ci,:] .+= t.(ta .* tmp) ci += 1 end else From c4effc384fed9c910cd390758f2f3066ddceaa83 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 17 Oct 2024 16:43:22 +0530 Subject: [PATCH 421/548] Add context to `errorshow` `except_str` tests (#56199) With this, the error message in https://buildkite.com/julialang/julia-master/builds/41054#019294ca-e2c5-41f2-a897-e2959715f154 would become ```julia Error in testset errorshow: Test Failed at /home/jishnu/juliaPR/usr/share/julia/test/errorshow.jl:226 Expression: typeof(err) === $(Expr(:escape, :MethodError)) Evaluated: StackOverflowError === MethodError Context: expr = :(+()) ``` Having the failing expression displayed makes it easier to locate the source of the error. --- test/errorshow.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/test/errorshow.jl b/test/errorshow.jl index db22fea1131d1..7a3d50d599f2e 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -215,6 +215,7 @@ Base.show_method_candidates(buf, try bad_vararg_decl("hello", 3) catch e e end) @test occursin("bad_vararg_decl(!Matched::$Int, ::Any...)", String(take!(buf))) macro except_str(expr, err_type) + source_info = __source__ return quote let err = nothing try @@ -222,7 +223,9 @@ macro except_str(expr, err_type) catch err end err === nothing && error("expected failure, but no exception thrown") - @test typeof(err) === $(esc(err_type)) + @testset let expr=$(repr(expr)) + $(Expr(:macrocall, Symbol("@test"), source_info, :(typeof(err) === $(esc(err_type))))) + end buf = IOBuffer() showerror(buf, err) String(take!(buf)) @@ -231,6 +234,7 @@ macro except_str(expr, err_type) end macro except_strbt(expr, err_type) + source_info = __source__ errmsg = "expected failure, but no exception thrown for $expr" return quote let err = nothing @@ -239,7 +243,9 @@ macro except_strbt(expr, err_type) catch err end err === nothing && error($errmsg) - @test typeof(err) === $(esc(err_type)) + @testset let expr=$(repr(expr)) + $(Expr(:macrocall, Symbol("@test"), source_info, :(typeof(err) === $(esc(err_type))))) + end buf = IOBuffer() showerror(buf, err, catch_backtrace()) String(take!(buf)) @@ -248,6 +254,7 @@ macro except_strbt(expr, err_type) end macro except_stackframe(expr, err_type) + source_info = __source__ return quote let err = nothing local st @@ -257,7 +264,9 @@ macro except_stackframe(expr, err_type) st = stacktrace(catch_backtrace()) end err === nothing && error("expected failure, but no exception thrown") - @test typeof(err) === $(esc(err_type)) + @testset let expr=$(repr(expr)) + $(Expr(:macrocall, Symbol("@test"), source_info, :(typeof(err) === $(esc(err_type))))) + end sprint(show, st[1]) end end From af51bcc563e15023d9dacca099e323b32b8a266f Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 17 Oct 2024 07:27:24 -0400 Subject: [PATCH 422/548] Include default user depot when JULIA_DEPOT_PATH has leading empty entry (#56195) --- base/initdefs.jl | 11 +++++++---- doc/src/manual/environment-variables.md | 26 ++++++++++++++++--------- test/loading.jl | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/base/initdefs.jl b/base/initdefs.jl index 707c96a2444d6..85b708433c0ef 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -112,20 +112,23 @@ function init_depot_path() # otherwise, populate the depot path with the entries in JULIA_DEPOT_PATH, # expanding empty strings to the bundled depot - populated = false - for path in eachsplit(str, Sys.iswindows() ? ';' : ':') + pushfirst_default = true + for (i, path) in enumerate(eachsplit(str, Sys.iswindows() ? ';' : ':')) if isempty(path) append_bundled_depot_path!(DEPOT_PATH) else path = expanduser(path) path in DEPOT_PATH || push!(DEPOT_PATH, path) - populated = true + if i == 1 + # if a first entry is given, don't add the default depot at the start + pushfirst_default = false + end end end # backwards compatibility: if JULIA_DEPOT_PATH only contains empty entries # (e.g., JULIA_DEPOT_PATH=':'), make sure to use the default depot - if !populated + if pushfirst_default pushfirst!(DEPOT_PATH, joinpath(homedir(), ".julia")) end else diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index b86822e0be4b7..5aa0701c9aafe 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -130,17 +130,19 @@ environment variable or if it must have a value, set it to the string `:`. ### [`JULIA_DEPOT_PATH`](@id JULIA_DEPOT_PATH) -The [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) environment variable is used to populate the global Julia -[`DEPOT_PATH`](@ref) variable, which controls where the package manager, as well -as Julia's code loading mechanisms, look for package registries, installed -packages, named environments, repo clones, cached compiled package images, -configuration files, and the default location of the REPL's history file. +The [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) environment variable is used to populate the +global Julia [`DEPOT_PATH`](@ref) variable, which controls where the package manager, as well +as Julia's code loading mechanisms, look for package registries, installed packages, named +environments, repo clones, cached compiled package images, configuration files, and the default +location of the REPL's history file. Unlike the shell `PATH` variable but similar to [`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH), -empty entries in [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) are expanded to the default -value of `DEPOT_PATH`, excluding the user depot. This allows easy overriding of the user -depot, while still retaining access to resources that are bundled with Julia, like cache -files, artifacts, etc. For example, to switch the user depot to `/foo/bar` just do +empty entries in [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) have special behavior: +- At the end, it is expanded to the default value of `DEPOT_PATH`, *excluding* the user depot. +- At the start, it is expanded to the default value of `DEPOT_PATH`, *including* the user depot. +This allows easy overriding of the user depot, while still retaining access to resources that +are bundled with Julia, like cache files, artifacts, etc. For example, to switch the user depot +to `/foo/bar` use a trailing `:` ```sh export JULIA_DEPOT_PATH="/foo/bar:" ``` @@ -150,6 +152,12 @@ resources will still be available. If you really only want to use the depot at ` and not load any bundled resources, simply set the environment variable to `/foo/bar` without the trailing colon. +To append a depot at the end of the full default list, including the default user depot, use a +leading `:` +```sh +export JULIA_DEPOT_PATH=":/foo/bar" +``` + There are two exceptions to the above rule. First, if [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) is set to the empty string, it expands to an empty `DEPOT_PATH` array. In other words, the empty string is interpreted as a zero-element array, not a one-element diff --git a/test/loading.jl b/test/loading.jl index 4877b256a6ad9..9e7e40ff3b50a 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -731,7 +731,7 @@ end "" => [], "$s" => [default; bundled], "$tmp$s" => [tmp; bundled], - "$s$tmp" => [bundled; tmp], + "$s$tmp" => [default; bundled; tmp], ) for (env, result) in pairs(cases) script = "DEPOT_PATH == $(repr(result)) || error(\"actual depot \" * join(DEPOT_PATH,':') * \" does not match expected depot \" * join($(repr(result)), ':'))" From 4329422fac14ae4daf0a2fd3c71fe4e0df169899 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 17 Oct 2024 17:56:11 +0530 Subject: [PATCH 423/548] Add news entry for `matprod_dest` (#56160) This was missed out in https://github.com/JuliaLang/julia/pull/55537 --- NEWS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 9aebf5d42d954..b33f75063eb93 100644 --- a/NEWS.md +++ b/NEWS.md @@ -139,7 +139,9 @@ Standard library changes * The number of default BLAS threads now respects process affinity, instead of using total number of logical threads available on the system ([#55574]). * A new function `zeroslike` is added that is used to generate the zero elements for matrix-valued banded matrices. - Custom array types may specialize this function to return an appropriate result. ([#55252]) + Custom array types may specialize this function to return an appropriate result ([#55252]). +* The matrix multiplication `A * B` calls `matprod_dest(A, B, T::Type)` to generate the destination. + This function is now public ([#55537]). #### Logging From d32cc269c100cc8f26972c6cd622dc7d5d4243b2 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 17 Oct 2024 10:09:45 -0300 Subject: [PATCH 424/548] Improve type inference of nonmissingtype, nonnothingtype and of Ryu (#56120) Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> --- base/missing.jl | 2 +- base/ryu/Ryu.jl | 2 +- base/some.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/missing.jl b/base/missing.jl index 1f34195efed88..6a8c09dc02aff 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -36,7 +36,7 @@ Any !!! compat "Julia 1.3" This function is exported as of Julia 1.3. """ -nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing) +nonmissingtype(@nospecialize(T::Type)) = typesplit(T, Missing) function nonmissingtype_checked(T::Type) R = nonmissingtype(T) diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl index 89589aa4ab668..e44e240baafda 100644 --- a/base/ryu/Ryu.jl +++ b/base/ryu/Ryu.jl @@ -112,7 +112,7 @@ end function Base.show(io::IO, x::T, forceuntyped::Bool=false, fromprint::Bool=false) where {T <: Base.IEEEFloat} compact = get(io, :compact, false)::Bool buf = Base.StringVector(neededdigits(T)) - typed = !forceuntyped && !compact && Base.nonnothing_nonmissing_typeinfo(io) != typeof(x) + typed = !forceuntyped && !compact && Base.nonnothing_nonmissing_typeinfo(io) !== typeof(x) pos = writeshortest(buf, 1, x, false, false, true, -1, (x isa Float32 && !fromprint) ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) write(io, resize!(buf, pos - 1)) diff --git a/base/some.jl b/base/some.jl index 7d7089bf76655..4269b2d78aedd 100644 --- a/base/some.jl +++ b/base/some.jl @@ -16,7 +16,7 @@ Some(::Type{T}) where {T} = Some{Type{T}}(T) promote_rule(::Type{Some{T}}, ::Type{Some{S}}) where {T, S<:T} = Some{T} -nonnothingtype(::Type{T}) where {T} = typesplit(T, Nothing) +nonnothingtype(@nospecialize(T::Type)) = typesplit(T, Nothing) promote_rule(T::Type{Nothing}, S::Type) = Union{S, Nothing} function promote_rule(T::Type{>:Nothing}, S::Type) R = nonnothingtype(T) From 6b95ac0163e6fa77e5167d33e6d23198a381a630 Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:10:50 +0200 Subject: [PATCH 425/548] Limit implicit `show` in REPL to printing 20 KiB by default (#53959) closes https://github.com/JuliaLang/julia/issues/40735 --------- Co-authored-by: Jameson Nash --- NEWS.md | 2 ++ stdlib/REPL/src/REPL.jl | 62 +++++++++++++++++++++++++++++++++++++++- stdlib/REPL/test/repl.jl | 40 ++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b33f75063eb93..05f21c5092643 100644 --- a/NEWS.md +++ b/NEWS.md @@ -167,6 +167,8 @@ Standard library changes - the REPL will now warn if it detects a name is being accessed from a module which does not define it (nor has a submodule which defines it), and for which the name is not public in that module. For example, `map` is defined in Base, and executing `LinearAlgebra.map` in the REPL will now issue a warning the first time occurs. ([#54872]) +- When an object is printed automatically (by being returned in the REPL), its display is now truncated after printing 20 KiB. + This does not affect manual calls to `show`, `print`, and so forth. ([#53959]) #### SuiteSparse diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 272b907165341..88458f7de4666 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -484,10 +484,70 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) return nothing end +SHOW_MAXIMUM_BYTES::Int = 20480 + +# Limit printing during REPL display +mutable struct LimitIO{IO_t <: IO} <: IO + io::IO_t + maxbytes::Int + n::Int # max bytes to write +end +LimitIO(io::IO, maxbytes) = LimitIO(io, maxbytes, 0) + +struct LimitIOException <: Exception + maxbytes::Int +end + +function Base.showerror(io::IO, e::LimitIOException) + print(io, "$LimitIOException: aborted printing after attempting to print more than $(Base.format_bytes(e.maxbytes)) within a `LimitIO`.") +end + +function Base.write(io::LimitIO, v::UInt8) + io.n > io.maxbytes && throw(LimitIOException(io.maxbytes)) + n_bytes = write(io.io, v) + io.n += n_bytes + return n_bytes +end + +# Semantically, we only need to override `Base.write`, but we also +# override `unsafe_write` for performance. +function Base.unsafe_write(limiter::LimitIO, p::Ptr{UInt8}, nb::UInt) + # already exceeded? throw + limiter.n > limiter.maxbytes && throw(LimitIOException(limiter.maxbytes)) + remaining = limiter.maxbytes - limiter.n # >= 0 + + # Not enough bytes left; we will print up to the limit, then throw + if remaining < nb + if remaining > 0 + Base.unsafe_write(limiter.io, p, remaining) + end + throw(LimitIOException(limiter.maxbytes)) + end + + # We won't hit the limit so we'll write the full `nb` bytes + bytes_written = Base.unsafe_write(limiter.io, p, nb) + limiter.n += bytes_written + return bytes_written +end + struct REPLDisplay{Repl<:AbstractREPL} <: AbstractDisplay repl::Repl end +function show_limited(io::IO, mime::MIME, x) + try + # We wrap in a LimitIO to limit the amount of printing. + # We unpack `IOContext`s, since we will pass the properties on the outside. + inner = io isa IOContext ? io.io : io + wrapped_limiter = IOContext(LimitIO(inner, SHOW_MAXIMUM_BYTES), io) + # `show_repl` to allow the hook with special syntax highlighting + show_repl(wrapped_limiter, mime, x) + catch e + e isa LimitIOException || rethrow() + printstyled(io, """…[printing stopped after displaying $(Base.format_bytes(e.maxbytes)); call `show(stdout, MIME"text/plain"(), ans)` to print without truncation]"""; color=:light_yellow, bold=true) + end +end + function display(d::REPLDisplay, mime::MIME"text/plain", x) x = Ref{Any}(x) with_repl_linfo(d.repl) do io @@ -504,7 +564,7 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) # this can override the :limit property set initially io = foldl(IOContext, d.repl.options.iocontext, init=io) end - show_repl(io, mime, x[]) + show_limited(io, mime, x[]) println(io) end return nothing diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 85a8137fa003e..809913502c3d7 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1964,6 +1964,46 @@ end @test undoc == [:AbstractREPL, :BasicREPL, :LineEditREPL, :StreamREPL] end +struct A40735 + str::String +end + +# https://github.com/JuliaLang/julia/issues/40735 +@testset "Long printing" begin + previous = REPL.SHOW_MAXIMUM_BYTES + try + REPL.SHOW_MAXIMUM_BYTES = 1000 + str = string(('a':'z')...)^50 + @test length(str) > 1100 + # For a raw string, we correctly get the standard abbreviated output + output = sprint(REPL.show_limited, MIME"text/plain"(), str; context=:limit => true) + hint = """call `show(stdout, MIME"text/plain"(), ans)` to print without truncation""" + suffix = "[printing stopped after displaying 1000 bytes; $hint]" + @test !endswith(output, suffix) + @test contains(output, "bytes ⋯") + # For a struct without a custom `show` method, we don't hit the abbreviated + # 3-arg show on the inner string, so here we check that the REPL print-limiting + # feature is correctly kicking in. + a = A40735(str) + output = sprint(REPL.show_limited, MIME"text/plain"(), a; context=:limit => true) + @test endswith(output, suffix) + @test length(output) <= 1200 + # We also check some extreme cases + REPL.SHOW_MAXIMUM_BYTES = 1 + output = sprint(REPL.show_limited, MIME"text/plain"(), 1) + @test output == "1" + output = sprint(REPL.show_limited, MIME"text/plain"(), 12) + @test output == "1…[printing stopped after displaying 1 byte; $hint]" + REPL.SHOW_MAXIMUM_BYTES = 0 + output = sprint(REPL.show_limited, MIME"text/plain"(), 1) + @test output == "…[printing stopped after displaying 0 bytes; $hint]" + @test sprint(io -> show(REPL.LimitIO(io, 5), "abc")) == "\"abc\"" + @test_throws REPL.LimitIOException(1) sprint(io -> show(REPL.LimitIO(io, 1), "abc")) + finally + REPL.SHOW_MAXIMUM_BYTES = previous + end +end + @testset "Dummy Pkg prompt" begin # do this in an empty depot to test default for new users withenv("JULIA_DEPOT_PATH" => mktempdir() * (Sys.iswindows() ? ";" : ":"), "JULIA_LOAD_PATH" => nothing) do From c2e3498215abee63a4f75be3ba7a25d0a829880a Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 17 Oct 2024 11:11:13 -0300 Subject: [PATCH 426/548] Add inferFunctionAttrsPass to the pipeline so that libfuncs get attributes (#52946) I doubt this will make too much of a difference since we don't use too many libfuncs, but it's also quite a cheap pass if it makes any difference --------- Co-authored-by: Valentin Churavy --- src/pipeline.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pipeline.cpp b/src/pipeline.cpp index f300e4d7757b2..09d51598ea8b7 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -332,6 +333,7 @@ static void buildEarlySimplificationPipeline(ModulePassManager &MPM, PassBuilder MPM.addPass(ForceFunctionAttrsPass()); invokePipelineStartCallbacks(MPM, PB, O); MPM.addPass(Annotation2MetadataPass()); + MPM.addPass(InferFunctionAttrsPass()); MPM.addPass(ConstantMergePass()); { FunctionPassManager FPM; From afb65fabe0c68c9e7a579017613208d812324e88 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Thu, 17 Oct 2024 08:11:09 -0700 Subject: [PATCH 427/548] Fix printing of `AbstractDict`s with unknown length (#56009) Also fix interacting with them at the REPL. Fixes #55931 --- base/show.jl | 2 +- stdlib/REPL/src/REPLCompletions.jl | 2 +- stdlib/REPL/test/replcompletions.jl | 19 +++++++++++++++++-- test/show.jl | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/base/show.jl b/base/show.jl index a147c2037d70e..fb932838ac69a 100644 --- a/base/show.jl +++ b/base/show.jl @@ -152,7 +152,7 @@ function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) end function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} - isempty(t) && return show(io, t) + (isempty(t) || !haslength(t)) && return show(io, t) # show more descriptively, with one line per key/value pair recur_io = IOContext(io, :SHOWN_SET => t) limit = get(io, :limit, false)::Bool diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 77f7fdf15cc9c..5e80e17036559 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -995,7 +995,7 @@ function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Ma isa(objt, Core.Const) || return (nothing, nothing, nothing) obj = objt.val isa(obj, AbstractDict) || return (nothing, nothing, nothing) - length(obj)::Int < 1_000_000 || return (nothing, nothing, nothing) + (Base.haslength(obj) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing) begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [ lastindex(str)+1) return (obj, str[begin_of_key:end], begin_of_key) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 3f8addcace73b..8bee70226755f 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -68,6 +68,16 @@ let ex = quote Base.keys(d::CustomDict) = collect(keys(d.mydict)) Base.length(d::CustomDict) = length(d.mydict) + # Support AbstractDict with unknown length, #55931 + struct NoLengthDict{K,V} <: AbstractDict{K,V} + dict::Dict{K,V} + NoLengthDict{K,V}() where {K,V} = new(Dict{K,V}()) + end + Base.iterate(d::NoLengthDict, s...) = iterate(d.dict, s...) + Base.IteratorSize(::Type{<:NoLengthDict}) = Base.SizeUnknown() + Base.eltype(::Type{NoLengthDict{K,V}}) where {K,V} = Pair{K,V} + Base.setindex!(d::NoLengthDict, v, k) = d.dict[k] = v + test(x::T, y::T) where {T<:Real} = pass test(x::Real, y::Real) = pass test(x::AbstractArray{T}, y) where {T<:Real} = pass @@ -151,6 +161,7 @@ let ex = quote test_repl_comp_dict = CompletionFoo.test_dict test_repl_comp_customdict = CompletionFoo.test_customdict test_dict_ℂ = Dict(1=>2) + test_dict_no_length = CompletionFoo.NoLengthDict{Int,Int}() end ex.head = :toplevel Core.eval(Main, ex) @@ -1486,8 +1497,12 @@ test_dict_completion("CompletionFoo.test_customdict") test_dict_completion("test_repl_comp_dict") test_dict_completion("test_repl_comp_customdict") -# Issue #23004: this should not throw: -@test REPLCompletions.dict_identifier_key("test_dict_ℂ[\\", :other) isa Tuple +@testset "dict_identifier_key" begin + # Issue #23004: this should not throw: + @test REPLCompletions.dict_identifier_key("test_dict_ℂ[\\", :other) isa Tuple + # Issue #55931: neither should this: + @test REPLCompletions.dict_identifier_key("test_dict_no_length[", :other) isa NTuple{3,Nothing} +end @testset "completion of string/cmd macros (#22577)" begin c, r, res = test_complete("ra") diff --git a/test/show.jl b/test/show.jl index d9c3585b7c1df..976141f1ebb17 100644 --- a/test/show.jl +++ b/test/show.jl @@ -2773,3 +2773,21 @@ end do_expr1 = :(foo() do; bar(); end) @test !contains(sprint(show, do_expr1), " \n") end + +struct NoLengthDict{K,V} <: AbstractDict{K,V} + dict::Dict{K,V} + NoLengthDict{K,V}() where {K,V} = new(Dict{K,V}()) +end +Base.iterate(d::NoLengthDict, s...) = iterate(d.dict, s...) +Base.IteratorSize(::Type{<:NoLengthDict}) = Base.SizeUnknown() +Base.eltype(::Type{NoLengthDict{K,V}}) where {K,V} = Pair{K,V} +Base.setindex!(d::NoLengthDict, v, k) = d.dict[k] = v + +# Issue 55931 +@testset "show AbstractDict with unknown length" begin + x = NoLengthDict{Int,Int}() + x[1] = 2 + str = sprint(io->show(io, MIME("text/plain"), x)) + @test contains(str, "NoLengthDict") + @test contains(str, "1 => 2") +end From eb3ed5e8d137f2fd32a5624fa50652364df56b1b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 17 Oct 2024 12:03:24 -0400 Subject: [PATCH 428/548] module: Prepare `usings` array for world age partition (#55357) This is a relatively independent part of the bindings partition branch, extending the module usings list to gain `min_world` and `max_world` `size_t`s. These are always `0` and `(size_t)-1` respectively in this PR, which handles the GC and serialization implications of this change. The semantic part will come later. --- src/gc-debug.c | 3 ++- src/gc-stock.c | 12 ++++++----- src/julia.h | 8 +++++++- src/julia_internal.h | 22 ++++++++++++++++++++ src/module.c | 37 ++++++++++++++------------------- src/staticdata.c | 41 ++++++++++++++++++++++++++----------- test/clangsa/MissingRoots.c | 2 +- 7 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/gc-debug.c b/src/gc-debug.c index 19dd93af5f236..5c150aba68e10 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -1125,7 +1125,8 @@ int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT if (vt == jl_module_type) { jl_module_t *m = (jl_module_t*)obj; start = (char*)m->usings.items; - len = m->usings.len; + len = module_usings_length(m); + elsize = sizeof(struct _jl_module_using); } else if (vt == jl_simplevector_type) { start = (char*)jl_svec_data(obj); diff --git a/src/gc-stock.c b/src/gc-stock.c index 50a3896d4f9aa..fb5acabed3d5c 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2053,16 +2053,18 @@ STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent gc_heap_snapshot_record_module_to_binding(mb_parent, bindings, bindingkeyset); gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)mb_parent->parent); gc_try_claim_and_push(mq, (jl_value_t *)mb_parent->parent, &nptr); - size_t nusings = mb_parent->usings.len; + size_t nusings = module_usings_length(mb_parent); if (nusings > 0) { // this is only necessary because bindings for "using" modules // are added only when accessed. therefore if a module is replaced // after "using" it but before accessing it, this array might // contain the only reference. jl_value_t *obj_parent = (jl_value_t *)mb_parent; - jl_value_t **objary_begin = (jl_value_t **)mb_parent->usings.items; - jl_value_t **objary_end = objary_begin + nusings; - gc_mark_objarray(ptls, obj_parent, objary_begin, objary_end, 1, nptr); + struct _jl_module_using *objary_begin = (struct _jl_module_using *)mb_parent->usings.items; + struct _jl_module_using *objary_end = objary_begin + nusings; + static_assert(sizeof(struct _jl_module_using) == 3*sizeof(void *), "Mismatch in _jl_module_using size"); + static_assert(offsetof(struct _jl_module_using, mod) == 0, "Expected `mod` at the beginning of _jl_module_using"); + gc_mark_objarray(ptls, obj_parent, (jl_value_t**)objary_begin, (jl_value_t**)objary_end, 3, nptr); } else { gc_mark_push_remset(ptls, (jl_value_t *)mb_parent, nptr); @@ -2175,7 +2177,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (update_meta) gc_setmark(ptls, o, bits, sizeof(jl_module_t)); jl_module_t *mb_parent = (jl_module_t *)new_obj; - uintptr_t nptr = ((mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); + uintptr_t nptr = ((module_usings_length(mb_parent) + 1) << 2) | (bits & GC_OLD); gc_mark_module_binding(ptls, mb_parent, nptr, bits); } else if (vtag == jl_task_tag << 4) { diff --git a/src/julia.h b/src/julia.h index f42ac2a23aaeb..7bb5f31eda708 100644 --- a/src/julia.h +++ b/src/julia.h @@ -714,7 +714,7 @@ typedef struct _jl_module_t { jl_sym_t *file; int32_t line; // hidden fields: - arraylist_t usings; // modules with all bindings potentially imported + arraylist_t usings; /* arraylist of struct jl_module_using */ // modules with all bindings potentially imported jl_uuid_t build_id; jl_uuid_t uuid; _Atomic(uint32_t) counter; @@ -728,6 +728,12 @@ typedef struct _jl_module_t { intptr_t hash; } jl_module_t; +struct _jl_module_using { + jl_module_t *mod; + size_t min_world; + size_t max_world; +}; + struct _jl_globalref_t { JL_DATA_TYPE jl_module_t *mod; diff --git a/src/julia_internal.h b/src/julia_internal.h index c09bfc5c3eb42..b2026c671553b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -819,6 +819,28 @@ void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type); JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno); +STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; +STATIC_INLINE jl_module_t *module_usings_getmod(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; + +#ifndef __clang_gcanalyzer__ +// The analyzer doesn't like looking through the arraylist, so just model the +// access for it using this function +STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { + return (struct _jl_module_using *)&(m->usings.items[3*i]); +} +STATIC_INLINE jl_module_t *module_usings_getmod(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { + return module_usings_getidx(m, i)->mod; +} +#endif + +STATIC_INLINE size_t module_usings_length(jl_module_t *m) JL_NOTSAFEPOINT { + return m->usings.len/3; +} + +STATIC_INLINE size_t module_usings_max(jl_module_t *m) JL_NOTSAFEPOINT { + return m->usings.max/3; +} + jl_value_t *jl_eval_global_var(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *e); jl_value_t *jl_interpret_opaque_closure(jl_opaque_closure_t *clos, jl_value_t **args, size_t nargs); jl_value_t *jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t *src); diff --git a/src/module.c b/src/module.c index 36c35f50b44af..8dbac950235ee 100644 --- a/src/module.c +++ b/src/module.c @@ -373,16 +373,6 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ return b; } -static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; - -#ifndef __clang_gcanalyzer__ -// The analyzer doesn't like looking through the arraylist, so just model the -// access for it using this function -static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { - return (jl_module_t*)m->usings.items[i]; -} -#endif - static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_t world) { jl_ptr_kind_union_t owner_pku = jl_atomic_load_relaxed(&owner->restriction); @@ -407,11 +397,11 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl jl_binding_partition_t *bpart = NULL; jl_module_t *owner = NULL; JL_LOCK(&m->lock); - int i = (int)m->usings.len - 1; + int i = (int)module_usings_length(m) - 1; JL_UNLOCK(&m->lock); for (; i >= 0; --i) { JL_LOCK(&m->lock); - jl_module_t *imp = module_usings_getidx(m, i); + jl_module_t *imp = module_usings_getmod(m, i); JL_UNLOCK(&m->lock); jl_binding_t *tempb = jl_get_module_binding(imp, var, 0); if (tempb != NULL && tempb->exportp) { @@ -746,19 +736,24 @@ JL_DLLEXPORT void jl_module_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t module_import_(to, from, asname, s, 0); } - JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from) { if (to == from) return; JL_LOCK(&to->lock); - for (size_t i = 0; i < to->usings.len; i++) { - if (from == to->usings.items[i]) { + for (size_t i = 0; i < module_usings_length(to); i++) { + if (from == module_usings_getmod(to, i)) { JL_UNLOCK(&to->lock); return; } } - arraylist_push(&to->usings, from); + struct _jl_module_using new_item = { + .mod = from, + .min_world = 0, + .max_world = (size_t)-1 + }; + arraylist_grow(&to->usings, sizeof(struct _jl_module_using)/sizeof(void*)); + memcpy(&to->usings.items[to->usings.len-3], &new_item, sizeof(struct _jl_module_using)); jl_gc_wb(to, from); JL_UNLOCK(&to->lock); @@ -1096,12 +1091,12 @@ JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod JL_DLLEXPORT jl_value_t *jl_module_usings(jl_module_t *m) { JL_LOCK(&m->lock); - int j = m->usings.len; + int j = module_usings_length(m); jl_array_t *a = jl_alloc_array_1d(jl_array_any_type, j); JL_GC_PUSH1(&a); for (int i = 0; j > 0; i++) { j--; - jl_module_t *imp = (jl_module_t*)m->usings.items[i]; + jl_module_t *imp = module_usings_getmod(m, i); jl_array_ptr_set(a, j, (jl_value_t*)imp); } JL_UNLOCK(&m->lock); // may gc @@ -1156,10 +1151,8 @@ JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported, if (usings) { // If `usings` is specified, traverse the list of `using`-ed modules and incorporate // the names exported by those modules into the list. - for(int i=(int)m->usings.len-1; i >= 0; --i) { - jl_module_t *usinged = module_usings_getidx(m, i); - append_exported_names(a, usinged, all); - } + for (int i = module_usings_length(m)-1; i >= 0; i--) + append_exported_names(a, module_usings_getmod(m, i), all); } JL_GC_POP(); return (jl_value_t*)a; diff --git a/src/staticdata.c b/src/staticdata.c index 0a8cbe6db7c67..89122653758e5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -811,8 +811,8 @@ static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_ } } - for (size_t i = 0; i < m->usings.len; i++) { - jl_queue_for_serialization(s, (jl_value_t*)m->usings.items[i]); + for (size_t i = 0; i < module_usings_length(m); i++) { + jl_queue_for_serialization(s, module_usings_getmod(m, i)); } } @@ -1266,27 +1266,44 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t // write out the usings list memset(&newm->usings._space, 0, sizeof(newm->usings._space)); if (m->usings.items == &m->usings._space[0]) { - newm->usings.items = (void**)offsetof(jl_module_t, usings._space); + // Push these relocations here, to keep them in order. This pairs with the `newm->usings.items = ` below. arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings.items))); arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); size_t i; - for (i = 0; i < m->usings.len; i++) { - arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings._space[i]))); - arraylist_push(&s->relocs_list, (void*)backref_id(s, m->usings._space[i], s->link_ids_relocs)); + for (i = 0; i < module_usings_length(m); i++) { + struct _jl_module_using *newm_data = module_usings_getidx(newm, i); + struct _jl_module_using *data = module_usings_getidx(m, i); + // TODO: Remove dead entries + newm_data->min_world = data->min_world; + newm_data->max_world = data->max_world; + if (s->incremental) { + if (data->max_world != (size_t)-1) + newm_data->max_world = 0; + newm_data->min_world = 0; + } + arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings._space[3*i]))); + arraylist_push(&s->relocs_list, (void*)backref_id(s, data->mod, s->link_ids_relocs)); } + newm->usings.items = (void**)offsetof(jl_module_t, usings._space); } else { newm->usings.items = (void**)tot; arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings.items))); arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); size_t i; - for (i = 0; i < m->usings.len; i++) { - write_pointerfield(s, (jl_value_t*)m->usings.items[i]); - tot += sizeof(void*); - } - for (; i < m->usings.max; i++) { + for (i = 0; i < module_usings_length(m); i++) { + struct _jl_module_using *data = module_usings_getidx(m, i); + write_pointerfield(s, (jl_value_t*)data->mod); + write_uint(s->s, data->min_world); + write_uint(s->s, data->max_world); + static_assert(sizeof(struct _jl_module_using) == 3*sizeof(void*), "_jl_module_using mismatch"); + tot += sizeof(struct _jl_module_using); + } + for (; i < module_usings_max(m); i++) { write_pointer(s->s); - tot += sizeof(void*); + write_uint(s->s, 0); + write_uint(s->s, 0); + tot += sizeof(struct _jl_module_using); } } assert(ios_pos(s->s) - reloc_offset == tot); diff --git a/test/clangsa/MissingRoots.c b/test/clangsa/MissingRoots.c index f402dc30eb33e..0a0d5369eba44 100644 --- a/test/clangsa/MissingRoots.c +++ b/test/clangsa/MissingRoots.c @@ -328,7 +328,7 @@ void scopes() { jl_module_t *propagation(jl_module_t *m JL_PROPAGATES_ROOT); void module_member(jl_module_t *m) { - for(int i=(int)m->usings.len-1; i >= 0; --i) { + for(int i=(int)m->usings.len-1; i >= 0; i -= 3) { jl_module_t *imp = propagation(m); jl_gc_safepoint(); look_at_value((jl_value_t*)imp); From 1f935afc07edde9f8c2e1a0f05d4772e18a55e97 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 17 Oct 2024 15:50:28 -0400 Subject: [PATCH 429/548] [REPL] fix lock ordering mistake in load_pkg (#56215) Fixes #56206 --- base/loading.jl | 1 + stdlib/REPL/src/Pkg_beforeload.jl | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index b396c7897c1fd..49d0cb52cd37b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2093,6 +2093,7 @@ debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but mor function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) # handle recursive and concurrent calls to require assert_havelock(require_lock) + require_lock.reentrancy_cnt == 1 || throw(ConcurrencyViolationError("recursive call to start_loading")) while true loaded = stalecheck ? maybe_root_module(modkey) : nothing loaded isa Module && return loaded diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index 472fbc924668d..e110910bafc2f 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -1,17 +1,16 @@ ## 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() + REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") @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 = get(Base.package_locks, Base.PkgId(REPLExt), nothing) lock !== nothing && wait(lock[2]) - return REPLExt end + return REPLExt end ## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt From 1cf384222a8b70a1f9cafbefd56cc0940fbb66c1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 17 Oct 2024 17:16:25 -0400 Subject: [PATCH 430/548] REPL: fix unsafe_write return type (#56220) Fixes: #56219 I am not really sure why we have a test for this, but we need to make the test happy --- stdlib/REPL/src/REPL.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 88458f7de4666..b8f850c3e9ff9 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -525,7 +525,7 @@ function Base.unsafe_write(limiter::LimitIO, p::Ptr{UInt8}, nb::UInt) end # We won't hit the limit so we'll write the full `nb` bytes - bytes_written = Base.unsafe_write(limiter.io, p, nb) + bytes_written = Base.unsafe_write(limiter.io, p, nb)::Union{Int,UInt} limiter.n += bytes_written return bytes_written end From f1990e2e3d8d31087b23288f640ba16909f7ec7b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 18 Oct 2024 02:49:19 +0530 Subject: [PATCH 431/548] Fix triu/tril for partly initialized matrices (#55312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes ```julia julia> using LinearAlgebra, StaticArrays julia> M = Matrix{BigInt}(undef, 2, 2); M[1,1] = M[2,2] = M[1,2] = 3; julia> S = SizedMatrix{2,2}(M) 2×2 SizedMatrix{2, 2, BigInt, 2, Matrix{BigInt}} with indices SOneTo(2)×SOneTo(2): 3 3 #undef 3 julia> triu(S) ERROR: UndefRefError: access to undefined reference Stacktrace: [1] getindex @ ./essentials.jl:907 [inlined] [2] getindex @ ~/.julia/packages/StaticArrays/MSJcA/src/SizedArray.jl:92 [inlined] [3] copyto_unaliased! @ ./abstractarray.jl:1086 [inlined] [4] copyto!(dest::SizedMatrix{2, 2, BigInt, 2, Matrix{BigInt}}, src::SizedMatrix{2, 2, BigInt, 2, Matrix{BigInt}}) @ Base ./abstractarray.jl:1066 [5] copymutable @ ./abstractarray.jl:1200 [inlined] [6] triu(M::SizedMatrix{2, 2, BigInt, 2, Matrix{BigInt}}) @ LinearAlgebra ~/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LinearAlgebra/src/generic.jl:413 [7] top-level scope @ REPL[11]:1 ``` After this PR: ```julia julia> triu(S) 2×2 SizedMatrix{2, 2, BigInt, 2, Matrix{BigInt}} with indices SOneTo(2)×SOneTo(2): 3 3 0 3 ``` Only the indices that need to be copied are accessed, and the others are written to without being read. --- stdlib/LinearAlgebra/src/generic.jl | 80 ++++++++++------------------ stdlib/LinearAlgebra/test/generic.jl | 55 +++++++++++++++++++ 2 files changed, 83 insertions(+), 52 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index e5f23b4981616..6c65c49add74b 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -389,55 +389,7 @@ function cross(a::AbstractVector, b::AbstractVector) end """ - triu(M) - -Upper triangle of a matrix. - -# Examples -```jldoctest -julia> a = fill(1.0, (4,4)) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - -julia> triu(a) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 0.0 1.0 1.0 1.0 - 0.0 0.0 1.0 1.0 - 0.0 0.0 0.0 1.0 -``` -""" -triu(M::AbstractMatrix) = triu!(copymutable(M)) - -""" - tril(M) - -Lower triangle of a matrix. - -# Examples -```jldoctest -julia> a = fill(1.0, (4,4)) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - -julia> tril(a) -4×4 Matrix{Float64}: - 1.0 0.0 0.0 0.0 - 1.0 1.0 0.0 0.0 - 1.0 1.0 1.0 0.0 - 1.0 1.0 1.0 1.0 -``` -""" -tril(M::AbstractMatrix) = tril!(copymutable(M)) - -""" - triu(M, k::Integer) + triu(M, k::Integer = 0) Return the upper triangle of `M` starting from the `k`th superdiagonal. @@ -465,10 +417,22 @@ julia> triu(a,-3) 1.0 1.0 1.0 1.0 ``` """ -triu(M::AbstractMatrix,k::Integer) = triu!(copymutable(M),k) +function triu(M::AbstractMatrix, k::Integer = 0) + d = similar(M) + A = triu!(d,k) + if iszero(k) + copytrito!(A, M, 'U') + else + for col in axes(A,2) + rows = firstindex(A,1):min(col-k, lastindex(A,1)) + A[rows, col] = @view M[rows, col] + end + end + return A +end """ - tril(M, k::Integer) + tril(M, k::Integer = 0) Return the lower triangle of `M` starting from the `k`th superdiagonal. @@ -496,7 +460,19 @@ julia> tril(a,-3) 1.0 0.0 0.0 0.0 ``` """ -tril(M::AbstractMatrix,k::Integer) = tril!(copymutable(M),k) +function tril(M::AbstractMatrix,k::Integer=0) + d = similar(M) + A = tril!(d,k) + if iszero(k) + copytrito!(A, M, 'L') + else + for col in axes(A,2) + rows = max(firstindex(A,1),col-k):lastindex(A,1) + A[rows, col] = @view M[rows, col] + end + end + return A +end """ triu!(M) diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index e0a1704913f78..2bf9c75141700 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -18,6 +18,9 @@ using .Main.DualNumbers isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) using .Main.FillArrays +isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) +using .Main.SizedArrays + Random.seed!(123) n = 5 # should be odd @@ -725,4 +728,56 @@ end @test det(A) == det(M) end +@testset "tril/triu" begin + @testset "with partly initialized matrices" begin + function test_triu(M, k=nothing) + M[1,1] = M[2,2] = M[1,2] = M[1,3] = M[2,3] = 3 + if isnothing(k) + MU = triu(M) + else + MU = triu(M, k) + end + @test iszero(MU[2,1]) + @test MU[1,1] == MU[2,2] == MU[1,2] == MU[1,3] == MU[2,3] == 3 + end + test_triu(Matrix{BigInt}(undef, 2, 3)) + test_triu(Matrix{BigInt}(undef, 2, 3), 0) + test_triu(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3))) + test_triu(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3)), 0) + + function test_tril(M, k=nothing) + M[1,1] = M[2,2] = M[2,1] = 3 + if isnothing(k) + ML = tril(M) + else + ML = tril(M, k) + end + @test ML[1,2] == ML[1,3] == ML[2,3] == 0 + @test ML[1,1] == ML[2,2] == ML[2,1] == 3 + end + test_tril(Matrix{BigInt}(undef, 2, 3)) + test_tril(Matrix{BigInt}(undef, 2, 3), 0) + test_tril(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3))) + test_tril(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3)), 0) + end + + @testset "block arrays" begin + for nrows in 0:3, ncols in 0:3 + M = [randn(2,2) for _ in 1:nrows, _ in 1:ncols] + Mu = triu(M) + for col in axes(M,2) + rowcutoff = min(col, size(M,1)) + @test @views Mu[1:rowcutoff, col] == M[1:rowcutoff, col] + @test @views Mu[rowcutoff+1:end, col] == zero.(M[rowcutoff+1:end, col]) + end + Ml = tril(M) + for col in axes(M,2) + @test @views Ml[col:end, col] == M[col:end, col] + rowcutoff = min(col-1, size(M,1)) + @test @views Ml[1:rowcutoff, col] == zero.(M[1:rowcutoff, col]) + end + end + end +end + end # module TestGeneric From e33c6a8551e070e7936a2ac95180a6c834f56549 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 18 Oct 2024 17:04:44 +0530 Subject: [PATCH 432/548] Specialize adding/subtracting mixed Upper/LowerTriangular (#56149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/JuliaLang/julia/issues/56134 After this, ```julia julia> using LinearAlgebra julia> A = hermitianpart(rand(4, 4)) 4×4 Hermitian{Float64, Matrix{Float64}}: 0.387617 0.277226 0.67629 0.60678 0.277226 0.894101 0.388416 0.489141 0.67629 0.388416 0.100907 0.619955 0.60678 0.489141 0.619955 0.452605 julia> B = UpperTriangular(A) 4×4 UpperTriangular{Float64, Hermitian{Float64, Matrix{Float64}}}: 0.387617 0.277226 0.67629 0.60678 ⋅ 0.894101 0.388416 0.489141 ⋅ ⋅ 0.100907 0.619955 ⋅ ⋅ ⋅ 0.452605 julia> B - B' 4×4 Matrix{Float64}: 0.0 0.277226 0.67629 0.60678 -0.277226 0.0 0.388416 0.489141 -0.67629 -0.388416 0.0 0.619955 -0.60678 -0.489141 -0.619955 0.0 ``` This preserves the band structure of the parent, if any: ```julia julia> U = UpperTriangular(Diagonal(ones(4))) 4×4 UpperTriangular{Float64, Diagonal{Float64, Vector{Float64}}}: 1.0 0.0 0.0 0.0 ⋅ 1.0 0.0 0.0 ⋅ ⋅ 1.0 0.0 ⋅ ⋅ ⋅ 1.0 julia> U - U' 4×4 Diagonal{Float64, Vector{Float64}}: 0.0 ⋅ ⋅ ⋅ ⋅ 0.0 ⋅ ⋅ ⋅ ⋅ 0.0 ⋅ ⋅ ⋅ ⋅ 0.0 ``` This doesn't fully work with partly initialized matrices, and would need https://github.com/JuliaLang/julia/pull/55312 for that. The abstract triangular methods now construct matrices using `similar(parent(U), size(U))` so that the destinations are fully mutable. ```julia julia> @invoke B::LinearAlgebra.AbstractTriangular - B'::LinearAlgebra.AbstractTriangular 4×4 Matrix{Float64}: 0.0 0.277226 0.67629 0.60678 -0.277226 0.0 0.388416 0.489141 -0.67629 -0.388416 0.0 0.619955 -0.60678 -0.489141 -0.619955 0.0 ``` --------- Co-authored-by: Daniel Karrasch --- stdlib/LinearAlgebra/src/triangular.jl | 19 +++++++++-- stdlib/LinearAlgebra/test/triangular.jl | 43 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 71660bc5ca28c..83ef221329d33 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -142,6 +142,7 @@ UnitUpperTriangular const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}} const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}} const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}} +const UnitUpperOrUnitLowerTriangular{T,S} = Union{UnitUpperTriangular{T,S}, UnitLowerTriangular{T,S}} uppertriangular(M) = UpperTriangular(M) lowertriangular(M) = LowerTriangular(M) @@ -181,6 +182,16 @@ copy(A::UpperOrLowerTriangular{<:Any, <:StridedMaybeAdjOrTransMat}) = copyto!(si # then handle all methods that requires specific handling of upper/lower and unit diagonal +function full(A::Union{UpperTriangular,LowerTriangular}) + return _triangularize(A)(parent(A)) +end +function full(A::UnitUpperOrUnitLowerTriangular) + isupper = A isa UnitUpperTriangular + Ap = _triangularize(A)(parent(A), isupper ? 1 : -1) + Ap[diagind(Ap, IndexStyle(Ap))] = @view A[diagind(A, IndexStyle(A))] + return Ap +end + function full!(A::LowerTriangular) B = A.data tril!(B) @@ -571,6 +582,8 @@ end return A end +_triangularize(::UpperOrUnitUpperTriangular) = triu +_triangularize(::LowerOrUnitLowerTriangular) = tril _triangularize!(::UpperOrUnitUpperTriangular) = triu! _triangularize!(::LowerOrUnitLowerTriangular) = tril! @@ -880,7 +893,8 @@ function +(A::UnitLowerTriangular, B::UnitLowerTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) end -+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B) ++(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) + full(B) ++(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) + copyto!(similar(parent(B), size(B)), B) function -(A::UpperTriangular, B::UpperTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B @@ -914,7 +928,8 @@ function -(A::UnitLowerTriangular, B::UnitLowerTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) end --(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) +-(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) - full(B) +-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) - copyto!(similar(parent(B), size(B)), B) function kron(A::UpperTriangular{T,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{S,<:StridedMaybeAdjOrTransMat}) where {T,S} C = UpperTriangular(Matrix{promote_op(*, T, S)}(undef, _kronsize(A, B))) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index ec9a3079e2643..7acb3cbfc0c57 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1322,4 +1322,47 @@ end end end +@testset "addition/subtraction of mixed triangular" begin + for A in (Hermitian(rand(4, 4)), Diagonal(rand(5))) + for T in (UpperTriangular, LowerTriangular, + UnitUpperTriangular, UnitLowerTriangular) + B = T(A) + M = Matrix(B) + R = B - B' + if A isa Diagonal + @test R isa Diagonal + end + @test R == M - M' + R = B + B' + if A isa Diagonal + @test R isa Diagonal + end + @test R == M + M' + C = MyTriangular(B) + @test C - C' == M - M' + @test C + C' == M + M' + end + end + @testset "unfilled parent" begin + @testset for T in (UpperTriangular, LowerTriangular, + UnitUpperTriangular, UnitLowerTriangular) + F = Matrix{BigFloat}(undef, 2, 2) + B = T(F) + isupper = B isa Union{UpperTriangular, UnitUpperTriangular} + B[1+!isupper, 1+isupper] = 2 + if !(B isa Union{UnitUpperTriangular, UnitLowerTriangular}) + B[1,1] = B[2,2] = 3 + end + M = Matrix(B) + @test B - B' == M - M' + @test B + B' == M + M' + @test B - copy(B') == M - M' + @test B + copy(B') == M + M' + C = MyTriangular(B) + @test C - C' == M - M' + @test C + C' == M + M' + end + end +end + end # module TestTriangular From 6317e02035e250ad071275da1caa3240ca6a7390 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 18 Oct 2024 16:20:25 +0200 Subject: [PATCH 433/548] juliac: remove call to jl_set_newly_inferred (#56222) Moved in #56186 --- contrib/juliac-buildscript.jl | 1 - src/precompile_utils.c | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/juliac-buildscript.jl b/contrib/juliac-buildscript.jl index 50f96198c416b..490bca86e1cba 100644 --- a/contrib/juliac-buildscript.jl +++ b/contrib/juliac-buildscript.jl @@ -17,7 +17,6 @@ task.rngState3 = 0x3a77f7189200c20b task.rngState4 = 0x5502376d099035ae uuid_tuple = (UInt64(0), UInt64(0)) ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, uuid_tuple) -ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred) # Patch methods in Core and Base diff --git a/src/precompile_utils.c b/src/precompile_utils.c index a78d1e66dbb51..fc361d8b88e6f 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -312,10 +312,12 @@ static void *jl_precompile_worklist(jl_array_t *worklist, jl_array_t *extext_met } } } - n = jl_array_nrows(new_ext_cis); - for (i = 0; i < n; i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_ext_cis, i); - precompile_enq_specialization_(ci->def, m); + if (new_ext_cis) { + n = jl_array_nrows(new_ext_cis); + for (i = 0; i < n; i++) { + jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_ext_cis, i); + precompile_enq_specialization_(ci->def, m); + } } void *native_code = jl_precompile_(m, 1); JL_GC_POP(); From a64ffa308f04a5bb35dda785caaff85d52296bad Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:24:36 -0400 Subject: [PATCH 434/548] Fix `goto` insertion when dom-sorting IR in `slot2ssa` pass (#56189) Fix-up this pass a bit to correctly handle fall-through terminators that cannot have their BasicBlock extended (e.g. `Expr(:leave, ...)`) --- base/compiler/ssair/slot2ssa.jl | 75 ++++++++++++++++++++------------- test/compiler/irpasses.jl | 29 +++++++++++++ 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index e70633ffecf6a..2eacdf0f56cfe 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -339,43 +339,58 @@ RPO traversal and in particular, any use of an SSA value must come after (by linear order) its definition. """ function domsort_ssa!(ir::IRCode, domtree::DomTree) - # First compute the new order of basic blocks + # Mapping from new → old BB index + # An "old" index of 0 means that this was a BB inserted as part of a fixup (see below) result_order = Int[] - stack = Int[] + + # Mapping from old → new BB index bb_rename = fill(-1, length(ir.cfg.blocks)) - node = 1 - ncritbreaks = 0 - nnewfallthroughs = 0 - while node !== -1 - push!(result_order, node) - bb_rename[node] = length(result_order) - cs = domtree.nodes[node].children - terminator = ir[SSAValue(last(ir.cfg.blocks[node].stmts))][:stmt] - next_node = node + 1 - node = -1 + + # The number of GotoNodes we need to insert to preserve control-flow after sorting + nfixupstmts = 0 + + # node queued up for scheduling (-1 === nothing) + node_to_schedule = 1 + worklist = Int[] + while node_to_schedule !== -1 + # First assign a new BB index to `node_to_schedule` + push!(result_order, node_to_schedule) + bb_rename[node_to_schedule] = length(result_order) + cs = domtree.nodes[node_to_schedule].children + terminator = ir[SSAValue(last(ir.cfg.blocks[node_to_schedule].stmts))][:stmt] + fallthrough = node_to_schedule + 1 + node_to_schedule = -1 + # Adding the nodes in reverse sorted order attempts to retain # the original source order of the nodes as much as possible. # This is not required for correctness, but is easier on the humans - for child in Iterators.Reverse(cs) - if child == next_node + for node in Iterators.Reverse(cs) + if node == fallthrough # Schedule the fall through node first, # so we can retain the fall through - node = next_node + node_to_schedule = node else - push!(stack, child) + push!(worklist, node) end end - if node == -1 && !isempty(stack) - node = pop!(stack) + if node_to_schedule == -1 && !isempty(worklist) + node_to_schedule = pop!(worklist) end - if node != next_node && !isa(terminator, Union{GotoNode, ReturnNode}) + # If a fallthrough successor is no longer the fallthrough after sorting, we need to + # add a GotoNode (and either extend or split the basic block as necessary) + if node_to_schedule != fallthrough && !isa(terminator, Union{GotoNode, ReturnNode}) if isa(terminator, GotoIfNot) # Need to break the critical edge - ncritbreaks += 1 + push!(result_order, 0) + elseif isa(terminator, EnterNode) || isexpr(terminator, :leave) + # Cannot extend the BasicBlock with a goto, have to split it push!(result_order, 0) else - nnewfallthroughs += 1 + # No need for a new block, just extend + @assert !isterminator(terminator) end + # Reserve space for the fixup goto + nfixupstmts += 1 end end new_bbs = Vector{BasicBlock}(undef, length(result_order)) @@ -385,7 +400,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) nstmts += length(ir.cfg.blocks[i].stmts) end end - result = InstructionStream(nstmts + ncritbreaks + nnewfallthroughs) + result = InstructionStream(nstmts + nfixupstmts) inst_rename = Vector{SSAValue}(undef, length(ir.stmts) + length(ir.new_nodes)) @inbounds for i = 1:length(ir.stmts) inst_rename[i] = SSAValue(-1) @@ -394,7 +409,6 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) inst_rename[i + length(ir.stmts)] = SSAValue(i + length(result)) end bb_start_off = 0 - crit_edge_breaks_fixup = Tuple{Int, Int}[] for (new_bb, bb) in pairs(result_order) if bb == 0 nidx = bb_start_off + 1 @@ -426,8 +440,8 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) else result[inst_range[end]][:stmt] = GotoNode(bb_rename[terminator.label]) end - elseif isa(terminator, GotoIfNot) - # Check if we need to break the critical edge + elseif isa(terminator, GotoIfNot) || isa(terminator, EnterNode) || isexpr(terminator, :leave) + # Check if we need to break the critical edge or split the block if bb_rename[bb + 1] != new_bb + 1 @assert result_order[new_bb + 1] == 0 # Add an explicit goto node in the next basic block (we accounted for this above) @@ -435,11 +449,14 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) node = result[nidx] node[:stmt], node[:type], node[:line] = GotoNode(bb_rename[bb + 1]), Any, NoLineUpdate end - result[inst_range[end]][:stmt] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) - elseif !isa(terminator, ReturnNode) - if isa(terminator, EnterNode) + if isa(terminator, GotoIfNot) + result[inst_range[end]][:stmt] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) + elseif isa(terminator, EnterNode) result[inst_range[end]][:stmt] = EnterNode(terminator, terminator.catch_dest == 0 ? 0 : bb_rename[terminator.catch_dest]) + else + @assert isexpr(terminator, :leave) end + elseif !isa(terminator, ReturnNode) if bb_rename[bb + 1] != new_bb + 1 # Add an explicit goto node nidx = inst_range[end] + 1 @@ -452,7 +469,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) local new_preds, new_succs let bb = bb, bb_rename = bb_rename, result_order = result_order new_preds = Int[bb for bb in (rename_incoming_edge(i, bb, result_order, bb_rename) for i in ir.cfg.blocks[bb].preds) if bb != -1] - new_succs = Int[ rename_outgoing_edge(i, bb, result_order, bb_rename) for i in ir.cfg.blocks[bb].succs] + new_succs = Int[ rename_outgoing_edge(i, bb, result_order, bb_rename) for i in ir.cfg.blocks[bb].succs] end new_bbs[new_bb] = BasicBlock(inst_range, new_preds, new_succs) end diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 740ac5f4958e4..13ef05db2f23a 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1967,3 +1967,32 @@ let f = (x)->nothing, mi = Base.method_instance(f, (Base.RefValue{Nothing},)), c ir = Core.Compiler.sroa_pass!(ir, inlining) Core.Compiler.verify_ir(ir) end + +let code = Any[ + # block 1 + GotoNode(4), # skip + # block 2 + Expr(:leave, SSAValue(1)), # not domsorted - make sure we move it correctly + # block 3 + ReturnNode(2), + # block 4 + EnterNode(7), + # block 5 + GotoIfNot(Argument(1), 2), + # block 6 + Expr(:leave, SSAValue(1)), + # block 7 + ReturnNode(1), + # block 8 + ReturnNode(nothing), + ] + ir = make_ircode(code; ssavaluetypes=Any[Any, Any, Union{}, Any, Any, Any, Union{}, Union{}]) + @test length(ir.cfg.blocks) == 8 + Core.Compiler.verify_ir(ir) + + # The IR should remain valid after domsorting + # (esp. including the insertion of new BasicBlocks for any fix-ups) + domtree = Core.Compiler.construct_domtree(ir) + ir = Core.Compiler.domsort_ssa!(ir, domtree) + Core.Compiler.verify_ir(ir) +end From ca3713e7ac8489acd1afe0a47dc8ceeaafb4a292 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Fri, 18 Oct 2024 16:29:42 +0200 Subject: [PATCH 435/548] fix infinite recursion in `promote_type` for `Irrational` (#55870) Fixes #51001 --- base/irrationals.jl | 11 ++++++++++- test/numbers.jl | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index c51b66045723f..76222997865c0 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -45,7 +45,16 @@ promote_rule(::Type{<:AbstractIrrational}, ::Type{Float16}) = Float16 promote_rule(::Type{<:AbstractIrrational}, ::Type{Float32}) = Float32 promote_rule(::Type{<:AbstractIrrational}, ::Type{<:AbstractIrrational}) = Float64 promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where {T<:Real} = promote_type(Float64, T) -promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = promote_type(promote_type(S, real(T)), T) + +function promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} + U = promote_type(S, real(T)) + if S <: U + # prevent infinite recursion + promote_type(Float64, T) + else + promote_type(U, T) + end +end AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) diff --git a/test/numbers.jl b/test/numbers.jl index fc3dc2c06bb7c..dc4f2cb613d77 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2937,6 +2937,14 @@ end @test log(π,ComplexF32(2)) isa ComplexF32 end +@testset "irrational promotion shouldn't recurse without bound, issue #51001" begin + for s ∈ (:π, :ℯ) + T = Irrational{s} + @test promote_type(Complex{T}, T) <: Complex + @test promote_type(T, Complex{T}) <: Complex + end +end + @testset "printing non finite floats" begin let float_types = Set() allsubtypes!(Base, AbstractFloat, float_types) From e5aff12f63f7a2141f1f4a3eb69ff049618d6fbc Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 18 Oct 2024 12:01:45 -0300 Subject: [PATCH 436/548] codegen: replace store of freeze in allocop and in emit new struct with memset since aggregate stores are bad (#55879) This fixes the issues found in slack in the reinterprets of ```julia julia> split128_v2(x::UInt128) = (first(reinterpret(NTuple{2, UInt}, x)), last(reinterpret(NTuple{2, UInt}, x))) split128_v2 (generic function with 1 method) julia> split128(x::UInt128) = reinterpret(NTuple{2, UInt}, x) split128 (generic function with 1 method) @code_native split128(UInt128(5)) push rbp mov rbp, rsp mov rax, rdi mov qword ptr [rdi + 8], rdx mov qword ptr [rdi], rsi pop rbp ret @code_native split128_v2(UInt128(5)) push rbp mov rbp, rsp mov rax, rdi mov qword ptr [rdi], rsi mov qword ptr [rdi + 8], rdx pop rbp ret ``` vs on master where ```julia julia> @code_native split128(UInt128(5)) push rbp mov rbp, rsp mov eax, esi shr eax, 8 mov ecx, esi shr ecx, 16 mov r8, rsi mov r9, rsi vmovd xmm0, esi vpinsrb xmm0, xmm0, eax, 1 mov rax, rsi vpinsrb xmm0, xmm0, ecx, 2 mov rcx, rsi shr esi, 24 vpinsrb xmm0, xmm0, esi, 3 shr r8, 32 vpinsrb xmm0, xmm0, r8d, 4 shr r9, 40 vpinsrb xmm0, xmm0, r9d, 5 shr rax, 48 vpinsrb xmm0, xmm0, eax, 6 shr rcx, 56 vpinsrb xmm0, xmm0, ecx, 7 vpinsrb xmm0, xmm0, edx, 8 mov eax, edx shr eax, 8 vpinsrb xmm0, xmm0, eax, 9 mov eax, edx shr eax, 16 vpinsrb xmm0, xmm0, eax, 10 mov eax, edx shr eax, 24 vpinsrb xmm0, xmm0, eax, 11 mov rax, rdx shr rax, 32 vpinsrb xmm0, xmm0, eax, 12 mov rax, rdx shr rax, 40 vpinsrb xmm0, xmm0, eax, 13 mov rax, rdx shr rax, 48 vpinsrb xmm0, xmm0, eax, 14 mov rax, rdi shr rdx, 56 vpinsrb xmm0, xmm0, edx, 15 vmovdqu xmmword ptr [rdi], xmm0 pop rbp ret ``` --- src/cgutils.cpp | 31 ++++++++++++------------------- src/llvm-alloc-opt.cpp | 11 +++-------- test/llvmpasses/alloc-opt-pass.ll | 11 ++++------- 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 4547e693755cd..a166b0a2c4800 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -4213,7 +4213,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg else { strct = UndefValue::get(lt); if (nargs < nf) - strct = ctx.builder.CreateFreeze(strct); + strct = ctx.builder.CreateFreeze(strct); // Change this to zero initialize instead? } } else if (tracked.second) { @@ -4380,25 +4380,18 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg ctx.builder.restoreIP(savedIP); } } - for (size_t i = nargs; i < nf; i++) { - if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { - ssize_t offs = jl_field_offset(sty, i); - ssize_t ptrsoffs = -1; - if (!inline_roots.empty()) - std::tie(offs, ptrsoffs) = split_value_field(sty, i); - assert(ptrsoffs < 0 && offs >= 0); - int fsz = jl_field_size(sty, i) - 1; - if (init_as_value) { + if (init_as_value) { + for (size_t i = nargs; i < nf; i++) { + if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { + ssize_t offs = jl_field_offset(sty, i); + ssize_t ptrsoffs = -1; + if (!inline_roots.empty()) + std::tie(offs, ptrsoffs) = split_value_field(sty, i); + assert(ptrsoffs < 0 && offs >= 0); + int fsz = jl_field_size(sty, i) - 1; unsigned llvm_idx = convert_struct_offset(ctx, cast(lt), offs + fsz); strct = ctx.builder.CreateInsertValue(strct, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), ArrayRef(llvm_idx)); } - else { - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_unionselbyte); - Instruction *dest = cast(emit_ptrgep(ctx, strct, offs + fsz)); - if (promotion_point == nullptr) - promotion_point = dest; - ai.decorateInst(ctx.builder.CreateAlignedStore(ctx.builder.getInt8(0), dest, Align(1))); - } } } if (nargs < nf) { @@ -4407,9 +4400,9 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (promotion_point) ctx.builder.SetInsertPoint(promotion_point); if (strct) { - promotion_point = cast(ctx.builder.CreateFreeze(UndefValue::get(lt))); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack); - ai.decorateInst(ctx.builder.CreateStore(promotion_point, strct)); + promotion_point = ai.decorateInst(ctx.builder.CreateMemSet(strct, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), + jl_datatype_size(ty), MaybeAlign(jl_datatype_align(ty)))); } ctx.builder.restoreIP(savedIP); } diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 0ec88c9d56356..a9e1b1e02da42 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -646,14 +646,9 @@ void Optimizer::initializeAlloca(IRBuilder<> &prolog_builder, AllocaInst *buff, return; assert(!buff->isArrayAllocation()); Type *T = buff->getAllocatedType(); - Value *Init = UndefValue::get(T); - if ((allockind & AllocFnKind::Zeroed) != AllocFnKind::Unknown) - Init = Constant::getNullValue(T); // zero, as described - else if (allockind == AllocFnKind::Unknown) - Init = Constant::getNullValue(T); // assume zeroed since we didn't find the attribute - else - Init = prolog_builder.CreateFreeze(UndefValue::get(T)); // assume freeze, since LLVM does not natively support this case - prolog_builder.CreateStore(Init, buff); + const DataLayout &DL = F.getParent()->getDataLayout(); + prolog_builder.CreateMemSet(buff, ConstantInt::get(Type::getInt8Ty(prolog_builder.getContext()), 0), DL.getTypeAllocSize(T), buff->getAlign()); + } // This function should not erase any safepoint so that the lifetime marker can find and cache diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index b962157120456..665687e86835d 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -76,7 +76,7 @@ L3: ; preds = %L2, %L1, %0 ; CHECK-LABEL: @legal_int_types ; CHECK: alloca [12 x i8] ; CHECK-NOT: alloca i96 -; CHECK: store [12 x i8] zeroinitializer, +; CHECK: call void @llvm.memset.p0.i64(ptr align 16 %var1, ; CHECK: ret void define void @legal_int_types() { %pgcstack = call ptr @julia.get_pgcstack() @@ -140,7 +140,7 @@ L2: ; preds = %0 ; CHECK: alloca ; CHECK-NOT: call token(...) @llvm.julia.gc_preserve_begin ; CHECK: call void @llvm.lifetime.start -; CHECK: store [8 x i8] zeroinitializer, +; CHECK: call void @llvm.memset.p0.i64(ptr align 16 %v, ; CHECK-NOT: call void @llvm.lifetime.end define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret({}) %0) { %pgcstack = call ptr @julia.get_pgcstack() @@ -164,11 +164,8 @@ define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret ; CHECK: alloca [1 x i8] ; CHECK-DAG: alloca [2 x i8] ; CHECK-DAG: alloca [3 x i8] -; CHECK-DAG: freeze [1 x i8] undef -; CHECK-DAG: store [1 x i8] % -; CHECK-DAG: store [3 x i8] zeroinitializer, -; CHECK-NOT: store -; CHECK-NOT: zeroinitializer +; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 1 %var1, +; CHECK-DAG: call void @llvm.memset.p0.i64(ptr align 4 %var7, ; CHECK: ret void define void @initializers() { %pgcstack = call ptr @julia.get_pgcstack() From 1157c6f9c400d2409bd27225e252203800c46bbf Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 18 Oct 2024 15:13:33 -0400 Subject: [PATCH 437/548] fix reporting of precompile configs on CI (#56232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the header doesn't print for `Pkg.test` with coverage on ``` [8dfed614] Test v1.11.0 1077.2 ms ✓ RequiredInterfaces 1 dependency successfully precompiled in 1 seconds. 8 already precompiled. ``` --- base/precompilation.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index a39563178632f..359d2b61800b1 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -800,8 +800,8 @@ function precompilepkgs(pkgs::Vector{String}=String[]; name *= color_string(" $(config_str)", :light_black) end lock(print_lock) do - if !fancyprint && target === nothing && isempty(pkg_queue) - printpkgstyle(io, :Precompiling, "packages...") + if !fancyprint && isempty(pkg_queue) + printpkgstyle(io, :Precompiling, something(target, "packages...")) end end push!(pkg_queue, pkg_config) From fc40e629b1d2ddc94ad9ecb87bc308b2892044e5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Oct 2024 15:33:29 -0400 Subject: [PATCH 438/548] stream: fix reading LibuvStream into array (#56092) Adds a new internal function `_take!(dst::Array{T,N}, src::Array{T,N})` for doing an efficient `copyto!` equivalent. Previously it was assumed that `compact` did this automatically, which wasn't a great assumption. Fixes #56078 --- base/array.jl | 11 +++++++++++ base/stream.jl | 4 +++- test/read.jl | 22 +++++++++++++++------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/base/array.jl b/base/array.jl index a628c1212659d..40907b2b00317 100644 --- a/base/array.jl +++ b/base/array.jl @@ -355,6 +355,17 @@ copy return $(Expr(:new, :(typeof(a)), :(memoryref(newmem)), :(a.size))) end +# a mutating version of copyto! that results in dst aliasing src afterwards +function _take!(dst::Array{T,N}, src::Array{T,N}) where {T,N} + if getfield(dst, :ref) !== getfield(src, :ref) + setfield!(dst, :ref, getfield(src, :ref)) + end + if getfield(dst, :size) !== getfield(src, :size) + setfield!(dst, :size, getfield(src, :size)) + end + return dst +end + ## Constructors ## similar(a::Array{T,1}) where {T} = Vector{T}(undef, size(a,1)) diff --git a/base/stream.jl b/base/stream.jl index 3ca5717be29db..2f00538ad0e96 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -941,6 +941,7 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) if bytesavailable(sbuf) >= nb nread = readbytes!(sbuf, a, nb) else + initsize = length(a) newbuf = PipeBuffer(a, maxsize=nb) newbuf.size = newbuf.offset # reset the write pointer to the beginning nread = try @@ -951,7 +952,8 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) finally s.buffer = sbuf end - compact(newbuf) + _take!(a, _unsafe_take!(newbuf)) + length(a) >= initsize || resize!(a, initsize) end iolock_end() return nread diff --git a/test/read.jl b/test/read.jl index 34224c146864e..99903d92d270f 100644 --- a/test/read.jl +++ b/test/read.jl @@ -268,13 +268,27 @@ for (name, f) in l n2 = readbytes!(s2, a2) @test n1 == n2 @test length(a1) == length(a2) - @test a1[1:n1] == a2[1:n2] + let l = min(l, n) + @test a1[1:l] == a2[1:l] + end @test n <= length(text) || eof(s1) @test n <= length(text) || eof(s2) cleanup() end + # Test growing output array + let x = UInt8[], + io = io() + n = readbytes!(io, x) + @test n == 0 + @test isempty(x) + n = readbytes!(io, x, typemax(Int)) + @test n == length(x) + @test x == codeunits(text) + cleanup() + end + verbose && println("$name read!...") l = length(text) for n = [1, 2, l-2, l-1, l] @@ -477,12 +491,6 @@ let s = "qwerty" @test read(IOBuffer(s)) == codeunits(s) @test read(IOBuffer(s), 10) == codeunits(s) @test read(IOBuffer(s), 1) == codeunits(s)[1:1] - - # Test growing output array - x = UInt8[] - n = readbytes!(IOBuffer(s), x, 10) - @test x == codeunits(s) - @test n == length(x) end From 82b150645e318bcb6bcb450e945b3b689a1eb264 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Oct 2024 15:37:36 -0400 Subject: [PATCH 439/548] fix precompile process flag propagation (#56214) CacheFlags could get set, but were never propagated to the target process, so the result would be unusable. Additionally, the debug and optimization levels were not synchronized with the sysimg, causing a regression in pkgimage usability after moving out stdlibs. Fixes #56207 Fixes #56054 Fixes #56206 --- base/Base.jl | 2 +- base/loading.jl | 61 ++++++++++++++++++++++++++++------------ base/precompilation.jl | 63 +++++++++++++++++++++++++++--------------- base/show.jl | 5 +++- base/util.jl | 3 +- base/uuid.jl | 2 ++ pkgimage.mk | 3 +- src/staticdata_utils.c | 15 +++++----- test/loading.jl | 22 +++++++-------- 9 files changed, 112 insertions(+), 64 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 1e780bb15141a..9800462f855f9 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -533,6 +533,7 @@ include("deepcopy.jl") include("download.jl") include("summarysize.jl") include("errorshow.jl") +include("util.jl") include("initdefs.jl") Filesystem.__postinit__() @@ -549,7 +550,6 @@ include("loading.jl") # misc useful functions & macros include("timing.jl") -include("util.jl") include("client.jl") include("asyncmap.jl") diff --git a/base/loading.jl b/base/loading.jl index 49d0cb52cd37b..78c584d00852b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1683,6 +1683,8 @@ function CacheFlags(cf::CacheFlags=CacheFlags(ccall(:jl_cache_flags, UInt8, ())) opt_level === nothing ? cf.opt_level : opt_level ) end +# reflecting jloptions.c defaults +const DefaultCacheFlags = CacheFlags(use_pkgimages=true, debug_level=isdebugbuild() ? 2 : 1, check_bounds=0, inline=true, opt_level=2) function _cacheflag_to_uint8(cf::CacheFlags)::UInt8 f = UInt8(0) @@ -1694,12 +1696,29 @@ function _cacheflag_to_uint8(cf::CacheFlags)::UInt8 return f end +function translate_cache_flags(cacheflags::CacheFlags, defaultflags::CacheFlags) + opts = String[] + cacheflags.use_pkgimages != defaultflags.use_pkgimages && push!(opts, cacheflags.use_pkgimages ? "--pkgimages=yes" : "--pkgimages=no") + cacheflags.debug_level != defaultflags.debug_level && push!(opts, "-g$(cacheflags.debug_level)") + cacheflags.check_bounds != defaultflags.check_bounds && push!(opts, ("--check-bounds=auto", "--check-bounds=yes", "--check-bounds=no")[cacheflags.check_bounds + 1]) + cacheflags.inline != defaultflags.inline && push!(opts, cacheflags.inline ? "--inline=yes" : "--inline=no") + cacheflags.opt_level != defaultflags.opt_level && push!(opts, "-O$(cacheflags.opt_level)") + return opts +end + function show(io::IO, cf::CacheFlags) - print(io, "use_pkgimages = ", cf.use_pkgimages) - print(io, ", debug_level = ", cf.debug_level) - print(io, ", check_bounds = ", cf.check_bounds) - print(io, ", inline = ", cf.inline) - print(io, ", opt_level = ", cf.opt_level) + print(io, "CacheFlags(") + print(io, "; use_pkgimages=") + print(io, cf.use_pkgimages) + print(io, ", debug_level=") + print(io, cf.debug_level) + print(io, ", check_bounds=") + print(io, cf.check_bounds) + print(io, ", inline=") + print(io, cf.inline) + print(io, ", opt_level=") + print(io, cf.opt_level) + print(io, ")") end struct ImageTarget @@ -2953,7 +2972,8 @@ end const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, - concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) + concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), + internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) @@ -2996,24 +3016,29 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: deps = deps_eltype * "[" * join(deps_strs, ",") * "]" precomp_stack = "Base.PkgId[$(join(map(pkg_str, vcat(Base.precompilation_stack, pkg)), ", "))]" + if output_o === nothing + # remove options that make no difference given the other cache options + cacheflags = CacheFlags(cacheflags, opt_level=0) + end + opts = translate_cache_flags(cacheflags, CacheFlags()) # julia_cmd is generated for the running system, and must be fixed if running for precompile instead if output_o !== nothing @debug "Generating object cache file for $(repr("text/plain", pkg))" cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing) - opts = `--output-o $(output_o) --output-ji $(output) --output-incremental=yes` + push!(opts, "--output-o", output_o) else @debug "Generating cache file for $(repr("text/plain", pkg))" cpu_target = nothing - opts = `-O0 --output-ji $(output) --output-incremental=yes` end + push!(opts, "--output-ji", output) + isassigned(PRECOMPILE_TRACE_COMPILE) && push!(opts, "--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])") - trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[]) --trace-compile-timing` : `` io = open(pipeline(addenv(`$(julia_cmd(;cpu_target)::Cmd) - $(flags) - $(opts) - --startup-file=no --history-file=no --warn-overwrite=yes - --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no") - $trace - -`, + $(flags) + $(opts) + --output-incremental=yes + --startup-file=no --history-file=no --warn-overwrite=yes + $(have_color === nothing ? "--color=auto" : have_color ? "--color=yes" : "--color=no") + -`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), @@ -3131,7 +3156,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in close(tmpio_o) close(tmpio_so) end - p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout, isext) + p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, isext) if success(p) if cache_objects @@ -4136,5 +4161,5 @@ end precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing)) || @assert false precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String)) || @assert false -precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false -precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, CacheFlags, IO, IO)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, CacheFlags, IO, IO)) || @assert false diff --git a/base/precompilation.jl b/base/precompilation.jl index 359d2b61800b1..f597acef9b57f 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -362,7 +362,7 @@ function printpkgstyle(io, header, msg; color=:green) end const Config = Pair{Cmd, Base.CacheFlags} -const PkgConfig = Tuple{Base.PkgId,Config} +const PkgConfig = Tuple{PkgId,Config} function precompilepkgs(pkgs::Vector{String}=String[]; internal_call::Bool=false, @@ -375,8 +375,22 @@ function precompilepkgs(pkgs::Vector{String}=String[]; # asking for timing disables fancy mode, as timing is shown in non-fancy mode fancyprint::Bool = can_fancyprint(io) && !timing, manifest::Bool=false,) + # monomorphize this to avoid latency problems + _precompilepkgs(pkgs, internal_call, strict, warn_loaded, timing, _from_loading, + configs isa Vector{Config} ? configs : [configs], + IOContext{IO}(io), fancyprint, manifest) +end - configs = configs isa Config ? [configs] : configs +function _precompilepkgs(pkgs::Vector{String}, + internal_call::Bool, + strict::Bool, + warn_loaded::Bool, + timing::Bool, + _from_loading::Bool, + configs::Vector{Config}, + io::IOContext{IO}, + fancyprint::Bool, + manifest::Bool) requested_pkgs = copy(pkgs) # for understanding user intent time_start = time_ns() @@ -393,17 +407,32 @@ function precompilepkgs(pkgs::Vector{String}=String[]; if _from_loading && !Sys.isinteractive() && Base.get_bool_env("JULIA_TESTS", false) # suppress passive loading printing in julia test suite. `JULIA_TESTS` is set in Base.runtests - io = devnull + io = IOContext{IO}(devnull) end + nconfigs = length(configs) hascolor = get(io, :color, false)::Bool color_string(cstr::String, col::Union{Int64, Symbol}) = _color_string(cstr, col, hascolor) stale_cache = Dict{StaleCacheKey, Bool}() - exts = Dict{Base.PkgId, String}() # ext -> parent + exts = Dict{PkgId, String}() # ext -> parent # make a flat map of each dep and its direct deps - depsmap = Dict{Base.PkgId, Vector{Base.PkgId}}() - pkg_exts_map = Dict{Base.PkgId, Vector{Base.PkgId}}() + depsmap = Dict{PkgId, Vector{PkgId}}() + pkg_exts_map = Dict{PkgId, Vector{PkgId}}() + + function describe_pkg(pkg::PkgId, is_direct_dep::Bool, flags::Cmd, cacheflags::Base.CacheFlags) + name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name + name = is_direct_dep ? name : color_string(name, :light_black) + if nconfigs > 1 && !isempty(flags) + config_str = join(flags, " ") + name *= color_string(" `$config_str`", :light_black) + end + if nconfigs > 1 + config_str = join(Base.translate_cache_flags(cacheflags, Base.DefaultCacheFlags), " ") + name *= color_string(" $config_str", :light_black) + end + return name + end for (dep, deps) in env.deps pkg = Base.PkgId(dep, env.names[dep]) @@ -569,7 +598,6 @@ function precompilepkgs(pkgs::Vector{String}=String[]; end end - nconfigs = length(configs) target = nothing if nconfigs == 1 if !isempty(only(configs)[1]) @@ -584,7 +612,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; failed_deps = Dict{PkgConfig, String}() precomperr_deps = PkgConfig[] # packages that may succeed after a restart (i.e. loaded packages with no cache file) - print_lock = io isa Base.LibuvStream ? io.lock::ReentrantLock : ReentrantLock() + print_lock = io.io isa Base.LibuvStream ? io.io.lock::ReentrantLock : ReentrantLock() first_started = Base.Event() printloop_should_exit::Bool = !fancyprint # exit print loop immediately if not fancy printing interrupted_or_done = Base.Event() @@ -677,7 +705,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; n_print_rows = 0 while !printloop_should_exit lock(print_lock) do - term_size = Base.displaysize_(io) + term_size = displaysize(io) num_deps_show = max(term_size[1] - 3, 2) # show at least 2 deps pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) @@ -692,7 +720,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; bar.max = n_total - n_already_precomp # when sizing to the terminal width subtract a little to give some tolerance to resizing the # window between print cycles - termwidth = Base.displaysize_(io)[2] - 4 + termwidth = displaysize(io)[2] - 4 if !final_loop str = sprint(io -> show_progress(io, bar; termwidth, carriagereturn=false); context=io) print(iostr, Base._truncate_at_width_or_chars(true, str, termwidth), "\n") @@ -700,12 +728,8 @@ function precompilepkgs(pkgs::Vector{String}=String[]; for pkg_config in pkg_queue_show dep, config = pkg_config loaded = warn_loaded && haskey(Base.loaded_modules, dep) - _name = haskey(exts, dep) ? string(exts[dep], " → ", dep.name) : dep.name - name = dep in direct_deps ? _name : string(color_string(_name, :light_black)) - if nconfigs > 1 && !isempty(config[1]) - config_str = "$(join(config[1], " "))" - name *= color_string(" $(config_str)", :light_black) - end + flags, cacheflags = config + name = describe_pkg(dep, dep in direct_deps, flags, cacheflags) line = if pkg_config in precomperr_deps string(color_string(" ? ", Base.warn_color()), name) elseif haskey(failed_deps, pkg_config) @@ -793,12 +817,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; std_pipe = Base.link_pipe!(Pipe(); reader_supports_async=true, writer_supports_async=true) t_monitor = @async monitor_std(pkg_config, std_pipe; single_requested_pkg) - _name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name - name = is_direct_dep ? _name : string(color_string(_name, :light_black)) - if nconfigs > 1 && !isempty(flags) - config_str = "$(join(flags, " "))" - name *= color_string(" $(config_str)", :light_black) - end + name = describe_pkg(pkg, is_direct_dep, flags, cacheflags) lock(print_lock) do if !fancyprint && isempty(pkg_queue) printpkgstyle(io, :Precompiling, something(target, "packages...")) diff --git a/base/show.jl b/base/show.jl index fb932838ac69a..25ed99f50b5b0 100644 --- a/base/show.jl +++ b/base/show.jl @@ -324,8 +324,11 @@ end convert(::Type{IOContext}, io::IOContext) = io convert(::Type{IOContext}, io::IO) = IOContext(io, ioproperties(io))::IOContext +convert(::Type{IOContext{IO_t}}, io::IOContext{IO_t}) where {IO_t} = io +convert(::Type{IOContext{IO_t}}, io::IO) where {IO_t} = IOContext{IO_t}(io, ioproperties(io))::IOContext{IO_t} IOContext(io::IO) = convert(IOContext, io) +IOContext{IO_t}(io::IO) where {IO_t} = convert(IOContext{IO_t}, io) function IOContext(io::IO, KV::Pair) d = ioproperties(io) @@ -427,7 +430,7 @@ get(io::IO, key, default) = default keys(io::IOContext) = keys(io.dict) keys(io::IO) = keys(ImmutableDict{Symbol,Any}()) -displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : Base.displaysize_(io.io) +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) diff --git a/base/util.jl b/base/util.jl index 95d62c4a16e1d..3ce64e50f7e29 100644 --- a/base/util.jl +++ b/base/util.jl @@ -249,7 +249,7 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio end function julia_exename() - if !Base.isdebugbuild() + if !isdebugbuild() return @static Sys.iswindows() ? "julia.exe" : "julia" else return @static Sys.iswindows() ? "julia-debug.exe" : "julia-debug" @@ -530,7 +530,6 @@ function _crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) end _crc32c(io::IO, crc::UInt32=0x00000000) = _crc32c(io, typemax(Int64), crc) _crc32c(io::IOStream, crc::UInt32=0x00000000) = _crc32c(io, filesize(io)-position(io), crc) -_crc32c(uuid::UUID, crc::UInt32=0x00000000) = _crc32c(uuid.value, crc) _crc32c(x::UInt128, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt128}, Csize_t), crc, x, 16) _crc32c(x::UInt64, crc::UInt32=0x00000000) = diff --git a/base/uuid.jl b/base/uuid.jl index 9b2da3c6409db..56f3a6aa417e7 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -36,6 +36,8 @@ let Base.hash(uuid::UUID, h::UInt) = hash(uuid_hash_seed, hash(convert(NTuple{2, UInt64}, uuid), h)) end +_crc32c(uuid::UUID, crc::UInt32=0x00000000) = _crc32c(uuid.value, crc) + let @inline function uuid_kernel(s, i, u) _c = UInt32(@inbounds codeunit(s, i)) diff --git a/pkgimage.mk b/pkgimage.mk index 0bc035ee03b08..78b2618be549f 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -25,7 +25,8 @@ print-depot-path: @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') $(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled - @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') + @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e \ + 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)])') touch $@ $(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 5f1095fec9168..9a7653972ea7c 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -605,15 +605,15 @@ static void write_mod_list(ios_t *s, jl_array_t *a) write_int32(s, 0); } -// OPT_LEVEL should always be the upper bits #define OPT_LEVEL 6 +#define DEBUG_LEVEL 1 JL_DLLEXPORT uint8_t jl_cache_flags(void) { // OOICCDDP uint8_t flags = 0; flags |= (jl_options.use_pkgimages & 1); // 0-bit - flags |= (jl_options.debug_level & 3) << 1; // 1-2 bit + flags |= (jl_options.debug_level & 3) << DEBUG_LEVEL; // 1-2 bit flags |= (jl_options.check_bounds & 3) << 3; // 3-4 bit flags |= (jl_options.can_inline & 1) << 5; // 5-bit flags |= (jl_options.opt_level & 3) << OPT_LEVEL; // 6-7 bit @@ -636,14 +636,13 @@ JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actua actual_flags &= ~1; } - // 2. Check all flags, except opt level must be exact - uint8_t mask = (1 << OPT_LEVEL)-1; + // 2. Check all flags, except opt level and debug level must be exact + uint8_t mask = (~(3u << OPT_LEVEL) & ~(3u << DEBUG_LEVEL)) & 0x7f; if ((actual_flags & mask) != (requested_flags & mask)) return 0; - // 3. allow for higher optimization flags in cache - actual_flags >>= OPT_LEVEL; - requested_flags >>= OPT_LEVEL; - return actual_flags >= requested_flags; + // 3. allow for higher optimization and debug level flags in cache to minimize required compile option combinations + return ((actual_flags >> OPT_LEVEL) & 3) >= ((requested_flags >> OPT_LEVEL) & 3) && + ((actual_flags >> DEBUG_LEVEL) & 3) >= ((requested_flags >> DEBUG_LEVEL) & 3); } JL_DLLEXPORT uint8_t jl_match_cache_flags_current(uint8_t flags) diff --git a/test/loading.jl b/test/loading.jl index 9e7e40ff3b50a..ec4a0391a412a 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1225,10 +1225,7 @@ end @test cf.check_bounds == 3 @test cf.inline @test cf.opt_level == 3 - - io = PipeBuffer() - show(io, cf) - @test read(io, String) == "use_pkgimages = true, debug_level = 3, check_bounds = 3, inline = true, opt_level = 3" + @test repr(cf) == "CacheFlags(; use_pkgimages=true, debug_level=3, check_bounds=3, inline=true, opt_level=3)" end empty!(Base.DEPOT_PATH) @@ -1420,13 +1417,16 @@ end "JULIA_DEPOT_PATH" => depot_path, "JULIA_DEBUG" => "loading") - out = Pipe() - proc = run(pipeline(cmd, stdout=out, stderr=out)) - close(out.in) - - log = @async String(read(out)) - @test success(proc) - fetch(log) + out = Base.PipeEndpoint() + log = @async read(out, String) + try + proc = run(pipeline(cmd, stdout=out, stderr=out)) + @test success(proc) + catch + @show fetch(log) + rethrow() + end + return fetch(log) end log = load_package("Parent", `--compiled-modules=no --pkgimages=no`) From f36f34298e5154b14fea1ec680b4e6c369b61d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:42:50 +0100 Subject: [PATCH 440/548] Do not call `rand` during sysimage precompilation (#56227) This change by itself doesn't do anything significant on `master`, but when backported to the v1.11 branch it'll address #56177. However it'd be great if someone could tell _why_ this fixes that issue, because it looks very unrelated. --------- Co-authored-by: Ian Butterworth --- contrib/generate_precompile.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 04d13011d6223..037e8926d5003 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -183,10 +183,10 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) # interactive startup uses this write(IOBuffer(), "") - # not critical, but helps hide unrelated compilation from @time when using --trace-compile - foo() = rand(2,2) * rand(2,2) - @time foo() - @time foo() + # Not critical, but helps hide unrelated compilation from @time when using --trace-compile. + f55729() = Base.Experimental.@force_compile + @time @eval f55729() + @time @eval f55729() break # only actually need to do this once end From cd99cfc4d39c09b3edbb6040639b4baa47882f6e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Oct 2024 22:03:58 -0400 Subject: [PATCH 441/548] Split up the one big codegen lock into per-function locks and dependency edge tracking (#56179) Disjoint content can be LLVM optimized in parallel now, since codegen no longer has any ability to handle recursion, and compilation should even be able to run in parallel with the GC also. Removes any remaining global state, since that is unsafe. Adds a C++ shim for concurrent gc support in conjunction with using a `std::unique_lock` to DRY code. Fix RuntimeDyld implementation: Since we use the ForwardingMemoryManger instead of making a new RTDyldMemoryManager object every time, we need to reference count the finalizeMemory calls so that we only call that at the end of relocating everything when everything is ready. We already happen to conveniently have a shared_ptr here, so just use that instead of inventing a duplicate counter. Fixes many OC bugs, including mostly fixing #55035, since this bug is just that much harder to express in the more constrained API. --- src/aotcompile.cpp | 164 ++++- src/cgmemmgr.cpp | 128 ++-- src/clangsa/GCChecker.cpp | 11 +- src/codegen.cpp | 528 ++++++---------- src/debug-registry.h | 14 +- src/debuginfo.cpp | 31 +- src/engine.cpp | 87 ++- src/gf.c | 17 + src/jitlayers.cpp | 1216 +++++++++++++++++++++++-------------- src/jitlayers.h | 94 +-- src/julia.h | 12 +- src/julia_atomics.h | 4 +- src/julia_internal.h | 7 +- src/julia_locks.h | 27 + src/opaque_closure.c | 11 +- src/pipeline.cpp | 6 + src/stackwalk.c | 4 +- 17 files changed, 1336 insertions(+), 1025 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 279686c387e1b..a3ffdf1d051a9 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -295,12 +295,12 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ jl_value_t *ci = cgparams.lookup(mi, world, world); JL_GC_PROMISE_ROOTED(ci); jl_code_instance_t *codeinst = NULL; - JL_GC_PUSH1(&codeinst); if (ci != jl_nothing && jl_atomic_load_relaxed(&((jl_code_instance_t *)ci)->inferred) != jl_nothing) { codeinst = (jl_code_instance_t*)ci; } else { if (cgparams.lookup != jl_rettype_inferred_addr) { + // XXX: This will corrupt and leak a lot of memory which may be very bad jl_error("Refusing to automatically run type inference with custom cache lookup."); } else { @@ -309,15 +309,129 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ * it into the cache here, since it was explicitly requested and is * otherwise not reachable from anywhere in the system image. */ - if (!jl_mi_cache_has_ci(mi, codeinst)) + if (codeinst && !jl_mi_cache_has_ci(mi, codeinst)) { + JL_GC_PUSH1(&codeinst); jl_mi_cache_insert(mi, codeinst); + JL_GC_POP(); + } } } - JL_GC_POP(); return codeinst; } -arraylist_t new_invokes; +typedef DenseMap> jl_compiled_functions_t; +static void compile_workqueue(jl_codegen_params_t ¶ms, CompilationPolicy policy, jl_compiled_functions_t &compiled_functions) +{ + decltype(params.workqueue) workqueue; + std::swap(params.workqueue, workqueue); + jl_code_info_t *src = NULL; + jl_code_instance_t *codeinst = NULL; + JL_GC_PUSH2(&src, &codeinst); + assert(!params.cache); + while (!workqueue.empty()) { + auto it = workqueue.pop_back_val(); + codeinst = it.first; + auto &proto = it.second; + // try to emit code for this item from the workqueue + StringRef invokeName = ""; + StringRef preal_decl = ""; + bool preal_specsig = false; + { + auto it = compiled_functions.find(codeinst); + if (it == compiled_functions.end()) { + // Reinfer the function. The JIT came along and removed the inferred + // method body. See #34993 + if ((policy != CompilationPolicy::Default || params.params->trim) && + jl_atomic_load_relaxed(&codeinst->inferred) == jl_nothing) { + // XXX: SOURCE_MODE_FORCE_SOURCE is wrong here (neither sufficient nor necessary) + codeinst = jl_type_infer(codeinst->def, jl_atomic_load_relaxed(&codeinst->max_world), SOURCE_MODE_FORCE_SOURCE); + } + if (codeinst) { + orc::ThreadSafeModule result_m = + jl_create_ts_module(name_from_method_instance(codeinst->def), + params.tsctx, params.DL, params.TargetTriple); + auto decls = jl_emit_codeinst(result_m, codeinst, NULL, params); + if (result_m) + it = compiled_functions.insert(std::make_pair(codeinst, std::make_pair(std::move(result_m), std::move(decls)))).first; + } + } + if (it != compiled_functions.end()) { + auto &decls = it->second.second; + invokeName = decls.functionObject; + if (decls.functionObject == "jl_fptr_args") { + preal_decl = decls.specFunctionObject; + } + else if (decls.functionObject != "jl_fptr_sparam" && decls.functionObject != "jl_f_opaque_closure_call") { + preal_decl = decls.specFunctionObject; + preal_specsig = true; + } + } + } + // patch up the prototype we emitted earlier + Module *mod = proto.decl->getParent(); + assert(proto.decl->isDeclaration()); + Function *pinvoke = nullptr; + if (preal_decl.empty()) { + if (invokeName.empty() && params.params->trim) { + errs() << "Bailed out to invoke when compiling:"; + jl_(codeinst->def); + abort(); + } + pinvoke = emit_tojlinvoke(codeinst, invokeName, mod, params); + if (!proto.specsig) + proto.decl->replaceAllUsesWith(pinvoke); + } + if (proto.specsig && !preal_specsig) { + // get or build an fptr1 that can invoke codeinst + if (pinvoke == nullptr) + pinvoke = get_or_emit_fptr1(preal_decl, mod); + // emit specsig-to-(jl)invoke conversion + proto.decl->setLinkage(GlobalVariable::InternalLinkage); + //protodecl->setAlwaysInline(); + jl_init_function(proto.decl, params.TargetTriple); + jl_method_instance_t *mi = codeinst->def; + size_t nrealargs = jl_nparams(mi->specTypes); // number of actual arguments being passed + bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; + // TODO: maybe this can be cached in codeinst->specfptr? + emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke, 0, 0); + preal_decl = ""; // no need to fixup the name + } + if (!preal_decl.empty()) { + // merge and/or rename this prototype to the real function + if (Value *specfun = mod->getNamedValue(preal_decl)) { + if (proto.decl != specfun) + proto.decl->replaceAllUsesWith(specfun); + } + else { + proto.decl->setName(preal_decl); + } + } + if (proto.oc) { // additionally, if we are dealing with an oc, then we might also need to fix up the fptr1 reference too + assert(proto.specsig); + StringRef ocinvokeDecl = invokeName; + // if OC expected a specialized specsig dispatch, but we don't have it, use the inner trampoline here too + // XXX: this invoke translation logic is supposed to exactly match new_opaque_closure + if (!preal_specsig || ocinvokeDecl == "jl_f_opaque_closure_call" || ocinvokeDecl == "jl_fptr_interpret_call" || ocinvokeDecl == "jl_fptr_const_return") + ocinvokeDecl = pinvoke->getName(); + assert(!ocinvokeDecl.empty()); + assert(ocinvokeDecl != "jl_fptr_args"); + assert(ocinvokeDecl != "jl_fptr_sparam"); + // merge and/or rename this prototype to the real function + if (Value *specfun = mod->getNamedValue(ocinvokeDecl)) { + if (proto.oc != specfun) + proto.oc->replaceAllUsesWith(specfun); + } + else { + proto.oc->setName(ocinvokeDecl); + } + } + workqueue.append(params.workqueue); + params.workqueue.clear(); + } + JL_GC_POP(); +} + + // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup, and can // also be used be extern consumers like GPUCompiler.jl to obtain a module containing @@ -346,7 +460,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm orc::ThreadSafeContext ctx; orc::ThreadSafeModule backing; if (!llvmmod) { - ctx = jl_ExecutionEngine->acquireContext(); + ctx = jl_ExecutionEngine->makeContext(); backing = jl_create_ts_module("text", ctx); } orc::ThreadSafeModule &clone = llvmmod ? *unwrap(llvmmod) : backing; @@ -367,11 +481,11 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm params.imaging_mode = imaging; params.debug_level = cgparams->debug_info_level; params.external_linkage = _external_linkage; - arraylist_new(&new_invokes, 0); size_t compile_for[] = { jl_typeinf_world, _world }; int worlds = 0; if (jl_options.trim != JL_TRIM_NO) worlds = 1; + jl_compiled_functions_t compiled_functions; for (; worlds < 2; worlds++) { JL_TIMING(NATIVE_AOT, NATIVE_Codegen); size_t this_world = compile_for[worlds]; @@ -391,7 +505,6 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm continue; } mi = (jl_method_instance_t*)item; -compile_mi: src = NULL; // if this method is generally visible to the current compilation world, // and this is either the primary world, or not applicable in the primary world @@ -406,7 +519,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm jl_(mi); abort(); } - if (codeinst && !params.compiled_functions.count(codeinst) && !data->jl_fvar_map.count(codeinst)) { + if (codeinst && !compiled_functions.count(codeinst) && !data->jl_fvar_map.count(codeinst)) { // now add it to our compilation results // Const returns do not do codegen, but juliac inspects codegen results so make a dummy fvar entry to represent it if (jl_options.trim != JL_TRIM_NO && jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) { @@ -418,7 +531,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm Triple(clone.getModuleUnlocked()->getTargetTriple())); jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, NULL, params); if (result_m) - params.compiled_functions[codeinst] = {std::move(result_m), std::move(decls)}; + compiled_functions[codeinst] = {std::move(result_m), std::move(decls)}; else if (jl_options.trim != JL_TRIM_NO) { // if we're building a small image, we need to compile everything // to ensure that we have all the information we need. @@ -428,26 +541,19 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm } } } - } else if (this_world != jl_typeinf_world) { + } + else if (this_world != jl_typeinf_world) { /* jl_safe_printf("Codegen could not find requested codeinstance to be compiled\n"); jl_(mi); abort(); */ } - // TODO: is goto the best way to do this? - jl_compile_workqueue(params, policy); - mi = (jl_method_instance_t*)arraylist_pop(&new_invokes); - if (mi != NULL) { - goto compile_mi; - } } - - // finally, make sure all referenced methods also get compiled or fixed up - jl_compile_workqueue(params, policy); } JL_GC_POP(); - arraylist_free(&new_invokes); + // finally, make sure all referenced methods also get compiled or fixed up + compile_workqueue(params, policy, compiled_functions); // process the globals array, before jl_merge_module destroys them SmallVector gvars(params.global_targets.size()); @@ -464,7 +570,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm data->jl_value_to_llvm[idx] = global.first; idx++; } - CreateNativeMethods += params.compiled_functions.size(); + CreateNativeMethods += compiled_functions.size(); size_t offset = gvars.size(); data->jl_external_to_llvm.resize(params.external_fns.size()); @@ -489,7 +595,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm { JL_TIMING(NATIVE_AOT, NATIVE_Merge); Linker L(*clone.getModuleUnlocked()); - for (auto &def : params.compiled_functions) { + for (auto &def : compiled_functions) { jl_merge_module(clone, std::move(std::get<0>(def.second))); jl_code_instance_t *this_code = def.first; jl_llvm_functions_t decls = std::get<1>(def.second); @@ -573,9 +679,6 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm } ct->reentrant_timing &= ~1ull; } - if (ctx.getContext()) { - jl_ExecutionEngine->releaseContext(std::move(ctx)); - } return (void*)data; } @@ -1975,11 +2078,6 @@ void jl_dump_native_impl(void *native_code, } } -void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIRAnalysis analysis) -{ - PM->add(new TargetLibraryInfoWrapperPass(triple)); - PM->add(createTargetTransformInfoWrapperPass(std::move(analysis))); -} // sometimes in GDB you want to find out what code would be created from a mi extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instance_t *mi) @@ -2037,8 +2135,8 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ dump->F = nullptr; dump->TSM = nullptr; if (src && jl_is_code_info(src)) { - auto ctx = jl_ExecutionEngine->getContext(); - orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(mi), *ctx); + auto ctx = jl_ExecutionEngine->makeContext(); + orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(mi), ctx); uint64_t compiler_start_time = 0; uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) @@ -2046,7 +2144,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ auto target_info = m.withModuleDo([&](Module &M) { return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple())); }); - jl_codegen_params_t output(*ctx, std::move(target_info.first), std::move(target_info.second)); + jl_codegen_params_t output(ctx, std::move(target_info.first), std::move(target_info.second)); output.params = ¶ms; output.imaging_mode = imaging_default(); // This would be nice, but currently it causes some assembly regressions that make printed output diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index 8557698a4e513..c257d2a2e3331 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -32,14 +32,14 @@ namespace { -static size_t get_block_size(size_t size) +static size_t get_block_size(size_t size) JL_NOTSAFEPOINT { return (size > jl_page_size * 256 ? LLT_ALIGN(size, jl_page_size) : jl_page_size * 256); } // Wrapper function to mmap/munmap/mprotect pages... -static void *map_anon_page(size_t size) +static void *map_anon_page(size_t size) JL_NOTSAFEPOINT { #ifdef _OS_WINDOWS_ char *mem = (char*)VirtualAlloc(NULL, size + jl_page_size, @@ -54,7 +54,7 @@ static void *map_anon_page(size_t size) return mem; } -static void unmap_page(void *ptr, size_t size) +static void unmap_page(void *ptr, size_t size) JL_NOTSAFEPOINT { #ifdef _OS_WINDOWS_ VirtualFree(ptr, size, MEM_DECOMMIT); @@ -71,7 +71,7 @@ enum class Prot : int { NO = PAGE_NOACCESS }; -static void protect_page(void *ptr, size_t size, Prot flags) +static void protect_page(void *ptr, size_t size, Prot flags) JL_NOTSAFEPOINT { DWORD old_prot; if (!VirtualProtect(ptr, size, (DWORD)flags, &old_prot)) { @@ -89,7 +89,7 @@ enum class Prot : int { NO = PROT_NONE }; -static void protect_page(void *ptr, size_t size, Prot flags) +static void protect_page(void *ptr, size_t size, Prot flags) JL_NOTSAFEPOINT { int ret = mprotect(ptr, size, (int)flags); if (ret != 0) { @@ -98,7 +98,7 @@ static void protect_page(void *ptr, size_t size, Prot flags) } } -static bool check_fd_or_close(int fd) +static bool check_fd_or_close(int fd) JL_NOTSAFEPOINT { if (fd == -1) return false; @@ -129,7 +129,7 @@ static intptr_t anon_hdl = -1; // Also, creating big file mapping and then map pieces of it seems to // consume too much global resources. Therefore, we use each file mapping // as a block on windows -static void *create_shared_map(size_t size, size_t id) +static void *create_shared_map(size_t size, size_t id) JL_NOTSAFEPOINT { void *addr = MapViewOfFile((HANDLE)id, FILE_MAP_ALL_ACCESS, 0, 0, size); @@ -137,13 +137,13 @@ static void *create_shared_map(size_t size, size_t id) return addr; } -static intptr_t init_shared_map() +static intptr_t init_shared_map() JL_NOTSAFEPOINT { anon_hdl = 0; return 0; } -static void *alloc_shared_page(size_t size, size_t *id, bool exec) +static void *alloc_shared_page(size_t size, size_t *id, bool exec) JL_NOTSAFEPOINT { assert(size % jl_page_size == 0); DWORD file_mode = exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; @@ -162,7 +162,7 @@ static void *alloc_shared_page(size_t size, size_t *id, bool exec) } #else // _OS_WINDOWS_ // For shared mapped region -static intptr_t get_anon_hdl(void) +static intptr_t get_anon_hdl(void) JL_NOTSAFEPOINT { int fd = -1; @@ -228,7 +228,7 @@ static struct _make_shared_map_lock { }; } shared_map_lock; -static size_t get_map_size_inc() +static size_t get_map_size_inc() JL_NOTSAFEPOINT { rlimit rl; if (getrlimit(RLIMIT_FSIZE, &rl) != -1) { @@ -242,7 +242,7 @@ static size_t get_map_size_inc() return map_size_inc_default; } -static void *create_shared_map(size_t size, size_t id) +static void *create_shared_map(size_t size, size_t id) JL_NOTSAFEPOINT { void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, anon_hdl, id); @@ -250,7 +250,7 @@ static void *create_shared_map(size_t size, size_t id) return addr; } -static intptr_t init_shared_map() +static intptr_t init_shared_map() JL_NOTSAFEPOINT { anon_hdl = get_anon_hdl(); if (anon_hdl == -1) @@ -265,7 +265,7 @@ static intptr_t init_shared_map() return anon_hdl; } -static void *alloc_shared_page(size_t size, size_t *id, bool exec) +static void *alloc_shared_page(size_t size, size_t *id, bool exec) JL_NOTSAFEPOINT { assert(size % jl_page_size == 0); size_t off = jl_atomic_fetch_add(&map_offset, size); @@ -292,7 +292,7 @@ static void *alloc_shared_page(size_t size, size_t *id, bool exec) #ifdef _OS_LINUX_ // Using `/proc/self/mem`, A.K.A. Keno's remote memory manager. -ssize_t pwrite_addr(int fd, const void *buf, size_t nbyte, uintptr_t addr) +ssize_t pwrite_addr(int fd, const void *buf, size_t nbyte, uintptr_t addr) JL_NOTSAFEPOINT { static_assert(sizeof(off_t) >= 8, "off_t is smaller than 64bits"); #ifdef _P64 @@ -319,7 +319,7 @@ ssize_t pwrite_addr(int fd, const void *buf, size_t nbyte, uintptr_t addr) // Do not call this directly. // Use `get_self_mem_fd` which has a guard to call this only once. -static int _init_self_mem() +static int _init_self_mem() JL_NOTSAFEPOINT { struct utsname kernel; uname(&kernel); @@ -359,13 +359,13 @@ static int _init_self_mem() return fd; } -static int get_self_mem_fd() +static int get_self_mem_fd() JL_NOTSAFEPOINT { static int fd = _init_self_mem(); return fd; } -static void write_self_mem(void *dest, void *ptr, size_t size) +static void write_self_mem(void *dest, void *ptr, size_t size) JL_NOTSAFEPOINT { while (size > 0) { ssize_t ret = pwrite_addr(get_self_mem_fd(), ptr, size, (uintptr_t)dest); @@ -424,7 +424,7 @@ struct Block { Block(const Block&) = delete; Block &operator=(const Block&) = delete; - Block(Block &&other) + Block(Block &&other) JL_NOTSAFEPOINT : ptr(other.ptr), total(other.total), avail(other.avail) @@ -433,9 +433,9 @@ struct Block { other.total = other.avail = 0; } - Block() = default; + Block() JL_NOTSAFEPOINT = default; - void *alloc(size_t size, size_t align) + void *alloc(size_t size, size_t align) JL_NOTSAFEPOINT { size_t aligned_avail = avail & (-align); if (aligned_avail < size) @@ -444,7 +444,7 @@ struct Block { avail = aligned_avail - size; return p; } - void reset(void *addr, size_t size) + void reset(void *addr, size_t size) JL_NOTSAFEPOINT { if (avail >= jl_page_size) { uintptr_t end = uintptr_t(ptr) + total; @@ -462,7 +462,8 @@ class RWAllocator { static constexpr int nblocks = 8; Block blocks[nblocks]{}; public: - void *alloc(size_t size, size_t align) + RWAllocator() JL_NOTSAFEPOINT = default; + void *alloc(size_t size, size_t align) JL_NOTSAFEPOINT { size_t min_size = (size_t)-1; int min_id = 0; @@ -498,9 +499,9 @@ struct SplitPtrBlock : public Block { uintptr_t wr_ptr{0}; uint32_t state{0}; - SplitPtrBlock() = default; + SplitPtrBlock() JL_NOTSAFEPOINT = default; - void swap(SplitPtrBlock &other) + void swap(SplitPtrBlock &other) JL_NOTSAFEPOINT { std::swap(ptr, other.ptr); std::swap(total, other.total); @@ -509,7 +510,7 @@ struct SplitPtrBlock : public Block { std::swap(state, other.state); } - SplitPtrBlock(SplitPtrBlock &&other) + SplitPtrBlock(SplitPtrBlock &&other) JL_NOTSAFEPOINT : SplitPtrBlock() { swap(other); @@ -534,11 +535,12 @@ class ROAllocator { // but might not have all the permissions set or data copied yet. SmallVector completed; virtual void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr, - size_t size, size_t align) = 0; - virtual SplitPtrBlock alloc_block(size_t size) = 0; + size_t size, size_t align) JL_NOTSAFEPOINT = 0; + virtual SplitPtrBlock alloc_block(size_t size) JL_NOTSAFEPOINT = 0; public: - virtual ~ROAllocator() {} - virtual void finalize() + ROAllocator() JL_NOTSAFEPOINT = default; + virtual ~ROAllocator() JL_NOTSAFEPOINT {} + virtual void finalize() JL_NOTSAFEPOINT { for (auto &alloc: allocations) { // ensure the mapped pages are consistent @@ -552,7 +554,7 @@ class ROAllocator { } // Allocations that have not been finalized yet. SmallVector allocations; - void *alloc(size_t size, size_t align) + void *alloc(size_t size, size_t align) JL_NOTSAFEPOINT { size_t min_size = (size_t)-1; int min_id = 0; @@ -603,7 +605,7 @@ class ROAllocator { template class DualMapAllocator : public ROAllocator { protected: - void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr, size_t, size_t) override + void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr, size_t, size_t) override JL_NOTSAFEPOINT { assert((char*)rt_ptr >= block.ptr && (char*)rt_ptr < (block.ptr + block.total)); @@ -618,7 +620,7 @@ class DualMapAllocator : public ROAllocator { } return (char*)rt_ptr + (block.wr_ptr - uintptr_t(block.ptr)); } - SplitPtrBlock alloc_block(size_t size) override + SplitPtrBlock alloc_block(size_t size) override JL_NOTSAFEPOINT { SplitPtrBlock new_block; // use `wr_ptr` to record the id initially @@ -626,7 +628,7 @@ class DualMapAllocator : public ROAllocator { new_block.reset(ptr, size); return new_block; } - void finalize_block(SplitPtrBlock &block, bool reset) + void finalize_block(SplitPtrBlock &block, bool reset) JL_NOTSAFEPOINT { // This function handles setting the block to the right mode // and free'ing maps that are not needed anymore. @@ -662,11 +664,11 @@ class DualMapAllocator : public ROAllocator { } } public: - DualMapAllocator() + DualMapAllocator() JL_NOTSAFEPOINT { assert(anon_hdl != -1); } - void finalize() override + void finalize() override JL_NOTSAFEPOINT { for (auto &block : this->blocks) { finalize_block(block, false); @@ -685,7 +687,7 @@ class SelfMemAllocator : public ROAllocator { SmallVector temp_buff; protected: void *get_wr_ptr(SplitPtrBlock &block, void *rt_ptr, - size_t size, size_t align) override + size_t size, size_t align) override JL_NOTSAFEPOINT { assert(!(block.state & SplitPtrBlock::InitAlloc)); for (auto &wr_block: temp_buff) { @@ -699,13 +701,13 @@ class SelfMemAllocator : public ROAllocator { new_block.reset(map_anon_page(block_size), block_size); return new_block.alloc(size, align); } - SplitPtrBlock alloc_block(size_t size) override + SplitPtrBlock alloc_block(size_t size) override JL_NOTSAFEPOINT { SplitPtrBlock new_block; new_block.reset(map_anon_page(size), size); return new_block; } - void finalize_block(SplitPtrBlock &block, bool reset) + void finalize_block(SplitPtrBlock &block, bool reset) JL_NOTSAFEPOINT { if (!(block.state & SplitPtrBlock::Alloc)) return; @@ -718,13 +720,13 @@ class SelfMemAllocator : public ROAllocator { } } public: - SelfMemAllocator() + SelfMemAllocator() JL_NOTSAFEPOINT : ROAllocator(), temp_buff() { assert(get_self_mem_fd() != -1); } - void finalize() override + void finalize() override JL_NOTSAFEPOINT { for (auto &block : this->blocks) { finalize_block(block, false); @@ -770,17 +772,15 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { RWAllocator rw_alloc; std::unique_ptr> ro_alloc; std::unique_ptr> exe_alloc; - bool code_allocated; size_t total_allocated; public: - RTDyldMemoryManagerJL() + RTDyldMemoryManagerJL() JL_NOTSAFEPOINT : SectionMemoryManager(), pending_eh(), rw_alloc(), ro_alloc(), exe_alloc(), - code_allocated(false), total_allocated(0) { #ifdef _OS_LINUX_ @@ -794,12 +794,12 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { exe_alloc.reset(new DualMapAllocator()); } } - ~RTDyldMemoryManagerJL() override + ~RTDyldMemoryManagerJL() override JL_NOTSAFEPOINT { } - size_t getTotalBytes() { return total_allocated; } + size_t getTotalBytes() JL_NOTSAFEPOINT { return total_allocated; } void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, - size_t Size) override; + size_t Size) override JL_NOTSAFEPOINT; #if 0 // Disable for now since we are not actually using this. void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, @@ -807,16 +807,16 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { #endif uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, - StringRef SectionName) override; + StringRef SectionName) override JL_NOTSAFEPOINT; uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, - bool isReadOnly) override; + bool isReadOnly) override JL_NOTSAFEPOINT; using SectionMemoryManager::notifyObjectLoaded; void notifyObjectLoaded(RuntimeDyld &Dyld, - const object::ObjectFile &Obj) override; - bool finalizeMemory(std::string *ErrMsg = nullptr) override; + const object::ObjectFile &Obj) override JL_NOTSAFEPOINT; + bool finalizeMemory(std::string *ErrMsg = nullptr) override JL_NOTSAFEPOINT; template - void mapAddresses(DL &Dyld, Alloc &&allocator) + void mapAddresses(DL &Dyld, Alloc &&allocator) JL_NOTSAFEPOINT { for (auto &alloc: allocator->allocations) { if (alloc.rt_addr == alloc.wr_addr || alloc.relocated) @@ -826,7 +826,7 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { } } template - void mapAddresses(DL &Dyld) + void mapAddresses(DL &Dyld) JL_NOTSAFEPOINT { if (!ro_alloc) return; @@ -838,14 +838,9 @@ class RTDyldMemoryManagerJL : public SectionMemoryManager { uint8_t *RTDyldMemoryManagerJL::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, - StringRef SectionName) + StringRef SectionName) JL_NOTSAFEPOINT { // allocating more than one code section can confuse libunwind. -#if !defined(_COMPILER_MSAN_ENABLED_) && !defined(_COMPILER_ASAN_ENABLED_) - // TODO: Figure out why msan and now asan too need this. - assert(!code_allocated); - code_allocated = true; -#endif total_allocated += Size; jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, Size); jl_timing_counter_inc(JL_TIMING_COUNTER_JITCodeSize, Size); @@ -859,7 +854,7 @@ uint8_t *RTDyldMemoryManagerJL::allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, - bool isReadOnly) + bool isReadOnly) JL_NOTSAFEPOINT { total_allocated += Size; jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, Size); @@ -873,7 +868,7 @@ uint8_t *RTDyldMemoryManagerJL::allocateDataSection(uintptr_t Size, } void RTDyldMemoryManagerJL::notifyObjectLoaded(RuntimeDyld &Dyld, - const object::ObjectFile &Obj) + const object::ObjectFile &Obj) JL_NOTSAFEPOINT { if (!ro_alloc) { assert(!exe_alloc); @@ -884,9 +879,8 @@ void RTDyldMemoryManagerJL::notifyObjectLoaded(RuntimeDyld &Dyld, mapAddresses(Dyld); } -bool RTDyldMemoryManagerJL::finalizeMemory(std::string *ErrMsg) +bool RTDyldMemoryManagerJL::finalizeMemory(std::string *ErrMsg) JL_NOTSAFEPOINT { - code_allocated = false; if (ro_alloc) { ro_alloc->finalize(); assert(exe_alloc); @@ -904,7 +898,7 @@ bool RTDyldMemoryManagerJL::finalizeMemory(std::string *ErrMsg) void RTDyldMemoryManagerJL::registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, - size_t Size) + size_t Size) JL_NOTSAFEPOINT { if (uintptr_t(Addr) == LoadAddr) { register_eh_frames(Addr, Size); @@ -917,7 +911,7 @@ void RTDyldMemoryManagerJL::registerEHFrames(uint8_t *Addr, #if 0 void RTDyldMemoryManagerJL::deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, - size_t Size) + size_t Size) JL_NOTSAFEPOINT { deregister_eh_frames((uint8_t*)LoadAddr, Size); } @@ -925,12 +919,12 @@ void RTDyldMemoryManagerJL::deregisterEHFrames(uint8_t *Addr, } -RTDyldMemoryManager* createRTDyldMemoryManager() +RTDyldMemoryManager* createRTDyldMemoryManager() JL_NOTSAFEPOINT { return new RTDyldMemoryManagerJL(); } -size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm) +size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm) JL_NOTSAFEPOINT { return ((RTDyldMemoryManagerJL*)mm)->getTotalBytes(); } diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 31631eb70a4ad..4892ebdabd110 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -31,7 +31,7 @@ namespace { using namespace clang; using namespace ento; -#define PDP std::shared_ptr +typedef std::shared_ptr PDP; #define MakePDP make_unique static const Stmt *getStmtForDiagnostics(const ExplodedNode *N) @@ -394,13 +394,18 @@ PDP GCChecker::SafepointBugVisitor::VisitNode(const ExplodedNode *N, } else { PathDiagnosticLocation Pos = PathDiagnosticLocation::createDeclBegin( N->getLocationContext(), BRC.getSourceManager()); - return MakePDP(Pos, "Tracking JL_NOT_SAFEPOINT annotation here."); + if (Pos.isValid()) + return MakePDP(Pos, "Tracking JL_NOT_SAFEPOINT annotation here."); + //N->getLocation().dump(); } } else if (NewSafepointDisabled == (unsigned)-1) { PathDiagnosticLocation Pos = PathDiagnosticLocation::createDeclBegin( N->getLocationContext(), BRC.getSourceManager()); - return MakePDP(Pos, "Safepoints re-enabled here"); + if (Pos.isValid()) + return MakePDP(Pos, "Safepoints re-enabled here"); + //N->getLocation().dump(); } + // n.b. there may be no position here to report if they were disabled by julia_notsafepoint_enter/leave } return nullptr; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 3f69f4789493a..0ab26a65fcaaa 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -233,7 +233,6 @@ STATISTIC(EmittedSpecfunCalls, "Number of specialized calls emitted"); STATISTIC(EmittedInvokes, "Number of invokes emitted"); STATISTIC(EmittedCalls, "Number of calls emitted"); STATISTIC(EmittedUndefVarErrors, "Number of undef var errors emitted"); -STATISTIC(EmittedOpaqueClosureFunctions, "Number of opaque closures emitted"); STATISTIC(EmittedToJLInvokes, "Number of tojlinvoke calls emitted"); STATISTIC(EmittedCFuncInvalidates, "Number of C function invalidates emitted"); STATISTIC(GeneratedCFuncWrappers, "Number of C function wrappers generated"); @@ -1009,6 +1008,11 @@ static const auto jlinvoke_func = new JuliaFunction<>{ {AttributeSet(), Attributes(C, {Attribute::ReadOnly, Attribute::NoCapture})}); }, }; +static const auto jlopaque_closure_call_func = new JuliaFunction<>{ + XSTR(jl_f_opaque_closure_call), + get_func_sig, + get_func_attrs, +}; static const auto jlmethod_func = new JuliaFunction<>{ XSTR(jl_method_def), [](LLVMContext &C) { @@ -1606,7 +1610,7 @@ static const auto jltuple_func = new JuliaFunction<>{XSTR(jl_f_tuple), get_func_ static const auto jlintrinsic_func = new JuliaFunction<>{XSTR(jl_f_intrinsic_call), get_func3_sig, get_func_attrs}; static const auto &builtin_func_map() { - static std::map*> builtins = { + static auto builtins = new DenseMap*> { { jl_f_is_addr, new JuliaFunction<>{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, { jl_f_typeof_addr, new JuliaFunction<>{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, { jl_f_sizeof_addr, new JuliaFunction<>{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, @@ -1649,18 +1653,18 @@ static const auto &builtin_func_map() { { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} }, { jl_f_current_scope_addr, new JuliaFunction<>{XSTR(jl_f_current_scope), get_func_sig, get_func_attrs} }, }; - return builtins; + return *builtins; } static const auto &may_dispatch_builtins() { - static std::unordered_set builtins( + static auto builtins = new DenseSet( {jl_f__apply_iterate_addr, jl_f__apply_pure_addr, jl_f__call_in_world_addr, jl_f__call_in_world_total_addr, jl_f__call_latest_addr, }); - return builtins; + return *builtins; } static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction<>{XSTR(jl_new_opaque_closure_jlcall), get_func_sig, get_func_attrs}; @@ -2976,7 +2980,7 @@ static void jl_name_jlfuncparams_args(jl_codegen_params_t ¶ms, Function *F) F->getArg(3)->setName("sparams::Any"); } -static void jl_init_function(Function *F, const Triple &TT) +void jl_init_function(Function *F, const Triple &TT) { // set any attributes that *must* be set on all functions AttrBuilder attr(F->getContext()); @@ -3023,6 +3027,7 @@ static bool uses_specsig(jl_value_t *sig, bool needsparams, jl_value_t *rettype, if (jl_vararg_kind(jl_tparam(sig, jl_nparams(sig) - 1)) == JL_VARARG_UNBOUND) return false; // not invalid, consider if specialized signature is worthwhile + // n.b. sig is sometimes wrong for OC (tparam0 might be the captures type of the specialization, even though what gets passed in that slot is an OC object), so prefer_specsig is always set (instead of recomputing tparam0 using get_oc_type) if (prefer_specsig) return true; if (!deserves_retbox(rettype) && !jl_is_datatype_singleton((jl_datatype_t*)rettype) && rettype != (jl_value_t*)jl_bool_type) @@ -5236,7 +5241,15 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *t if (theF) theArgs.push_back(theF); for (size_t i = 0; i < nargs; i++) { - Value *arg = boxed(ctx, argv[i]); + Value *arg; + if (i == 0 && trampoline == julia_call3) { + const jl_cgval_t &f = argv[i]; + arg = f.inline_roots.empty() && f.ispointer() ? data_pointer(ctx, f) : value_to_pointer(ctx, f).V; + arg = decay_derived(ctx, arg); + } + else { + arg = boxed(ctx, argv[i]); + } theArgs.push_back(arg); } CallInst *result = ctx.builder.CreateCall(TheTrampoline, theArgs); @@ -5283,13 +5296,13 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos idx++; } for (size_t i = 0; i < nargs; i++) { - jl_value_t *jt = jl_nth_slot_type(specTypes, i); // n.b.: specTypes is required to be a datatype by construction for specsig if (is_opaque_closure && i == 0) { // Special implementation for opaque closures: their jt and thus - // julia_type_to_llvm values are likely wrong, so override the - // behavior here to directly pass the expected pointer based instead - // just on passing arg as a pointer + // julia_type_to_llvm values are likely wrong (based on captures instead of the OC), so override the + // behavior here to directly pass the expected pointer directly instead of + // computing it from the available information + // jl_value_t *oc_type = (jl_value_t*)jl_any_type; // more accurately: get_oc_type(specTypes, jlretty) jl_cgval_t arg = argv[i]; if (arg.isghost) { argvals[idx] = Constant::getNullValue(ctx.builder.getPtrTy(AddressSpace::Derived)); @@ -5302,6 +5315,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos idx++; continue; } + jl_value_t *jt = jl_nth_slot_type(specTypes, i); jl_cgval_t arg = update_julia_type(ctx, argv[i], jt); if (arg.typ == jl_bottom_type) return jl_cgval_t(); @@ -5519,6 +5533,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR // Check if we already queued this up auto it = ctx.call_targets.find(codeinst); if (need_to_emit && it != ctx.call_targets.end()) { + assert(it->second.specsig == specsig); protoname = it->second.decl->getName(); need_to_emit = cache_valid = false; } @@ -5559,7 +5574,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR handled = true; if (need_to_emit) { Function *trampoline_decl = cast(jl_Module->getNamedValue(protoname)); - ctx.call_targets[codeinst] = {cc, return_roots, trampoline_decl, specsig}; + ctx.call_targets[codeinst] = {cc, return_roots, trampoline_decl, nullptr, specsig}; if (trim_may_error(ctx.params->trim)) push_frames(ctx, ctx.linfo, mi); } @@ -5570,9 +5585,9 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR if (!handled) { if (trim_may_error(ctx.params->trim)) { if (lival.constant) { - arraylist_push(&new_invokes, lival.constant); push_frames(ctx, ctx.linfo, (jl_method_instance_t*)lival.constant); - } else { + } + else { errs() << "Dynamic call to unknown function"; errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; @@ -5728,20 +5743,16 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo } } FunctionCallee fptr; - Value *F; JuliaFunction<> *cc; if (f.typ == (jl_value_t*)jl_intrinsic_type) { fptr = prepare_call(jlintrinsic_func); - F = f.inline_roots.empty() && f.ispointer() ? data_pointer(ctx, f) : value_to_pointer(ctx, f).V; - F = decay_derived(ctx, F); cc = julia_call3; } else { fptr = FunctionCallee(get_func_sig(ctx.builder.getContext()), ctx.builder.CreateCall(prepare_call(jlgetbuiltinfptr_func), {emit_typeof(ctx, f)})); - F = boxed(ctx, f); cc = julia_call; } - Value *ret = emit_jlcall(ctx, fptr, F, ArrayRef(argv).drop_front(), nargs - 1, cc); + Value *ret = emit_jlcall(ctx, fptr, nullptr, argv, nargs, cc); setName(ctx.emission_context, ret, "Builtin_ret"); return mark_julia_type(ctx, ret, true, rt); } @@ -5758,52 +5769,12 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo JL_GC_POP(); return r; } + // TODO: else emit_oc_call } } int failed_dispatch = !argv[0].constant; if (ctx.params->trim != JL_TRIM_NO) { - size_t min_valid = 1; - size_t max_valid = ~(size_t)0; - size_t latest_world = jl_get_world_counter(); // TODO: marshal the world age of the compilation here. - - // Find all methods matching the call signature - jl_array_t *matches = NULL; - jl_value_t *tup = NULL; - JL_GC_PUSH2(&tup, &matches); - if (!failed_dispatch) { - SmallVector argtypes; - for (auto& arg: argv) - argtypes.push_back(arg.typ); - tup = jl_apply_tuple_type_v(argtypes.data(), argtypes.size()); - matches = (jl_array_t*)jl_matching_methods((jl_tupletype_t*)tup, jl_nothing, 10 /*TODO: make global*/, 1, - latest_world, &min_valid, &max_valid, NULL); - if ((jl_value_t*)matches == jl_nothing) - failed_dispatch = 1; - } - - // Expand each matching method to its unique specialization, if it has exactly one - if (!failed_dispatch) { - size_t k; - size_t len = new_invokes.len; - for (k = 0; k < jl_array_nrows(matches); k++) { - jl_method_match_t *match = (jl_method_match_t *)jl_array_ptr_ref(matches, k); - jl_method_instance_t *mi = jl_method_match_to_mi(match, latest_world, min_valid, max_valid, 0); - if (!mi) { - if (jl_array_nrows(matches) == 1) { - // if the method match is not compileable, but there is only one, fall back to - // unspecialized implementation - mi = jl_get_unspecialized(match->method); - } - else { - new_invokes.len = len; - failed_dispatch = 1; - break; - } - } - arraylist_push(&new_invokes, mi); - } - } - JL_GC_POP(); + abort(); // this code path is unsound, unsafe, and probably bad } if (failed_dispatch && trim_may_error(ctx.params->trim)) { @@ -6634,66 +6605,73 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met assert(jl_is_method_instance(mi)); ci = jl_atomic_load_relaxed(&mi->cache); } - - if (ci == NULL || (jl_value_t*)ci == jl_nothing) { - JL_GC_POP(); - return std::make_pair((Function*)NULL, (Function*)NULL); - } - auto inferred = jl_atomic_load_relaxed(&ci->inferred); - if (!inferred || inferred == jl_nothing) { + if (ci == NULL || (jl_value_t*)ci == jl_nothing || ci->rettype != rettype || !jl_egal(sigtype, mi->specTypes)) { // TODO: correctly handle the ABI conversion if rettype != ci->rettype JL_GC_POP(); return std::make_pair((Function*)NULL, (Function*)NULL); } - auto it = ctx.emission_context.compiled_functions.find(ci); - - if (it == ctx.emission_context.compiled_functions.end()) { - ++EmittedOpaqueClosureFunctions; - jl_code_info_t *ir = jl_uncompress_ir(closure_method, ci, (jl_value_t*)inferred); - JL_GC_PUSH1(&ir); - // TODO: Emit this inline and outline it late using LLVM's coroutine support. - orc::ThreadSafeModule closure_m = jl_create_ts_module( - name_from_method_instance(mi), ctx.emission_context.tsctx, - jl_Module->getDataLayout(), Triple(jl_Module->getTargetTriple())); - jl_llvm_functions_t closure_decls = emit_function(closure_m, mi, ir, rettype, ctx.emission_context); - JL_GC_POP(); - it = ctx.emission_context.compiled_functions.insert(std::make_pair(ci, std::make_pair(std::move(closure_m), std::move(closure_decls)))).first; + // method lookup code (similar to emit_invoke, and the inverse of emit_specsig_oc_call) + bool specsig = uses_specsig(sigtype, false, rettype, true); + std::string name; + std::string oc; + StringRef protoname; + StringRef proto_oc; + + // Check if we already queued this up + auto it = ctx.call_targets.find(ci); + bool need_to_emit = it == ctx.call_targets.end(); + if (!need_to_emit) { + assert(specsig == it->second.specsig); + if (specsig) { + protoname = it->second.decl->getName(); + proto_oc = it->second.oc->getName(); + } + else { + proto_oc = it->second.decl->getName(); + } + need_to_emit = false; + } + else { + if (specsig) { + raw_string_ostream(name) << "j_" << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); + protoname = StringRef(name); + } + raw_string_ostream(oc) << "j1_" << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); + proto_oc = StringRef(oc); } - auto &closure_m = it->second.first; - auto &closure_decls = it->second.second; - - assert(closure_decls.functionObject != "jl_fptr_sparam"); - bool isspecsig = closure_decls.functionObject != "jl_fptr_args"; - - Function *F = NULL; - std::string fname = isspecsig ? - closure_decls.functionObject : - closure_decls.specFunctionObject; - if (GlobalValue *V = jl_Module->getNamedValue(fname)) { + // Get the fptr1 OC + Function *F = nullptr; + if (GlobalValue *V = jl_Module->getNamedValue(proto_oc)) { F = cast(V); } else { F = Function::Create(get_func_sig(ctx.builder.getContext()), Function::ExternalLinkage, - fname, jl_Module); + proto_oc, jl_Module); jl_init_function(F, ctx.emission_context.TargetTriple); jl_name_jlfunc_args(ctx.emission_context, F); F->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_func_attrs(ctx.builder.getContext()), F->getAttributes()})); } - Function *specF = NULL; - if (!isspecsig) { - specF = F; + + // Get the specsig (if applicable) + Function *specF = nullptr; + jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; + unsigned return_roots = 0; + bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; + assert(is_opaque_closure); + if (specsig) { + bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); + jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, nullptr, protoname, mi->specTypes, rettype, is_opaque_closure, gcstack_arg); + cc = returninfo.cc; + return_roots = returninfo.return_roots; + specF = cast(returninfo.decl.getCallee()); } - else { - //emission context holds context lock so can get module - specF = closure_m.getModuleUnlocked()->getFunction(closure_decls.specFunctionObject); - if (specF) { - jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, NULL, - closure_decls.specFunctionObject, sigtype, rettype, true, JL_FEAT_TEST(ctx,gcstack_arg)); - specF = cast(returninfo.decl.getCallee()); - } + + if (need_to_emit) { + ctx.call_targets[ci] = {cc, return_roots, specsig ? specF : F, specsig ? F : nullptr, specsig}; } + JL_GC_POP(); return std::make_pair(F, specF); } @@ -7173,7 +7151,12 @@ static Value *get_scope_field(jl_codectx_t &ctx) return emit_ptrgep(ctx, ct, offsetof(jl_task_t, scope), "current_scope"); } -static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, Module *M, jl_codegen_params_t ¶ms) +Function *get_or_emit_fptr1(StringRef preal_decl, Module *M) +{ + return cast(M->getOrInsertFunction(preal_decl, get_func_sig(M->getContext()), get_func_attrs(M->getContext())).getCallee()); +} + +Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, Module *M, jl_codegen_params_t ¶ms) JL_NOTSAFEPOINT { ++EmittedToJLInvokes; jl_codectx_t ctx(M->getContext(), params, codeinst); @@ -7184,7 +7167,6 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptr name, M); jl_init_function(f, params.TargetTriple); if (trim_may_error(params.params->trim)) { - arraylist_push(&new_invokes, codeinst->def); // Try t compile this invoke // TODO: Debuginfo! push_frames(ctx, ctx.linfo, codeinst->def, 1); } @@ -7213,7 +7195,17 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptr return f; } -static void emit_cfunc_invalidate( +static jl_value_t *get_oc_type(jl_value_t *calltype, jl_value_t *rettype) JL_ALWAYS_LEAFTYPE +{ + jl_value_t *argtype = jl_argtype_without_function((jl_value_t*)calltype); + JL_GC_PUSH1(&argtype); + jl_value_t *oc_type JL_ALWAYS_LEAFTYPE = jl_apply_type2((jl_value_t*)jl_opaque_closure_type, argtype, rettype); + JL_GC_PROMISE_ROOTED(oc_type); + JL_GC_POP(); + return oc_type; +} + +void emit_specsig_to_fptr1( Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, size_t nargs, @@ -7240,14 +7232,18 @@ static void emit_cfunc_invalidate( ++AI; // gcstack_arg } for (size_t i = 0; i < nargs; i++) { - // n.b. calltype is required to be a datatype by construction for specsig - jl_value_t *jt = jl_nth_slot_type(calltype, i); if (i == 0 && is_for_opaque_closure) { + // `jt` would be wrong here (it is the captures type), so is not used used for + // the ABI decisions, but the argument actually will require boxing as its real type + // which can be exactly recomputed from the specialization, as that defined the ABI + jl_value_t *oc_type = get_oc_type(calltype, rettype); Value *arg_v = &*AI; ++AI; - myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const); + myargs[i] = mark_julia_slot(arg_v, (jl_value_t*)oc_type, NULL, ctx.tbaa().tbaa_const); continue; } + // n.b. calltype is required to be a datatype by construction for specsig + jl_value_t *jt = jl_nth_slot_type(calltype, i); bool isboxed = false; Type *et; if (deserves_argbox(jt)) { @@ -7335,16 +7331,6 @@ static void emit_cfunc_invalidate( } } -static void emit_cfunc_invalidate( - Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, - jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, - size_t nargs, jl_codegen_params_t ¶ms, - size_t min_world, size_t max_world) -{ - emit_cfunc_invalidate(gf_thunk, cc, return_roots, calltype, rettype, is_for_opaque_closure, nargs, params, - prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func), min_world, max_world); -} - static Function* gen_cfun_wrapper( Module *into, jl_codegen_params_t ¶ms, const function_sig_t &sig, jl_value_t *ff, const char *aliasname, @@ -7712,11 +7698,11 @@ static Function* gen_cfun_wrapper( GlobalVariable::InternalLinkage, funcName, M); jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); gf_thunk->setAttributes(AttributeList::get(M->getContext(), {returninfo.attrs, gf_thunk->getAttributes()})); - // build a specsig -> jl_apply_generic converter thunk + // build a specsig -> jl_apply_generic converter thunk // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), // but which has the signature of a specsig - emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, is_opaque_closure, nargs + 1, ctx.emission_context, - min_world, max_world); + emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, is_opaque_closure, nargs + 1, ctx.emission_context, + prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func), min_world, max_world); returninfo.decl = FunctionCallee(returninfo.decl.getFunctionType(), ctx.builder.CreateSelect(age_ok, returninfo.decl.getCallee(), gf_thunk)); } retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, codeinst->rettype, returninfo, nullptr, inputargs, nargs + 1); @@ -8026,7 +8012,8 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi } // generate a julia-callable function that calls f (AKA lam) -static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, jl_returninfo_t &f, unsigned nargs, int retarg, StringRef funcName, +// if is_opaque_closure, then generate the OC invoke, rather than a real invoke +static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, jl_returninfo_t &f, unsigned nargs, int retarg, bool is_opaque_closure, StringRef funcName, Module *M, jl_codegen_params_t ¶ms) { ++GeneratedInvokeWrappers; @@ -8055,11 +8042,14 @@ static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, j allocate_gc_frame(ctx, b0); SmallVector argv(nargs); - bool is_opaque_closure = jl_is_method(lam->def.value) && lam->def.method->is_for_opaque_closure; jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); for (size_t i = 0; i < nargs; ++i) { - jl_value_t *ty = ((i == 0) && is_opaque_closure) ? (jl_value_t*)jl_any_type : - jl_nth_slot_type(lam->specTypes, i); + if (i == 0 && is_opaque_closure) { + jl_value_t *oc_type = (jl_value_t*)jl_any_type; // more accurately: get_oc_type(lam->specTypes, jlretty) + argv[i] = mark_julia_slot(funcArg, oc_type, NULL, ctx.tbaa().tbaa_const); + continue; + } + jl_value_t *ty = jl_nth_slot_type(lam->specTypes, i); Value *theArg; if (i == 0) { theArg = funcArg; @@ -8455,6 +8445,7 @@ static jl_llvm_functions_t // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise // OpaqueClosure implicitly loads the env if (i == 0 && ctx.is_opaque_closure) { + // n.b. this is not really needed, because ty was already supposed to be correct if (jl_is_array(src->slottypes)) { ty = jl_array_ptr_ref((jl_array_t*)src->slottypes, i); } @@ -8554,7 +8545,7 @@ static jl_llvm_functions_t raw_string_ostream(wrapName) << "jfptr_" << ctx.name << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); declarations.functionObject = wrapName; size_t nparams = jl_nparams(lam->specTypes); - gen_invoke_wrapper(lam, jlrettype, returninfo, nparams, retarg, declarations.functionObject, M, ctx.emission_context); + gen_invoke_wrapper(lam, jlrettype, returninfo, nparams, retarg, ctx.is_opaque_closure, declarations.functionObject, M, ctx.emission_context); // TODO: add attributes: maybe_mark_argument_dereferenceable(Arg, argType) // TODO: add attributes: dereferenceable // TODO: (if needsparams) add attributes: dereferenceable, readonly, nocapture @@ -8564,11 +8555,10 @@ static jl_llvm_functions_t GlobalVariable::ExternalLinkage, declarations.specFunctionObject, M); jl_init_function(f, ctx.emission_context.TargetTriple); - if (needsparams) { + if (needsparams) jl_name_jlfuncparams_args(ctx.emission_context, f); - } else { + else jl_name_jlfunc_args(ctx.emission_context, f); - } f->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_func_attrs(ctx.builder.getContext()), f->getAttributes()})); returninfo.decl = f; declarations.functionObject = needsparams ? "jl_fptr_sparam" : "jl_fptr_args"; @@ -8940,76 +8930,73 @@ static jl_llvm_functions_t } for (i = 0; i < nreq && i < vinfoslen; i++) { jl_sym_t *s = slot_symbol(ctx, i); - jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); - // TODO: jl_nth_slot_type should call jl_rewrap_unionall? - // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise - bool isboxed = deserves_argbox(argType); - Type *llvmArgType = NULL; - if (i == 0 && ctx.is_opaque_closure) { - isboxed = false; - llvmArgType = ctx.builder.getPtrTy(AddressSpace::Derived); - argType = (jl_value_t*)jl_any_type; - } - else { - llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); - } jl_varinfo_t &vi = ctx.slots[i]; - if (s == jl_unused_sym || vi.value.constant) { - assert(vi.boxroot == NULL); - if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) { - ++AI; - auto tracked = CountTrackedPointers(llvmArgType); - if (tracked.count && !tracked.all) - ++AI; - } - continue; - } jl_cgval_t theArg; - // If this is an opaque closure, implicitly load the env and switch - // the world age. if (i == 0 && ctx.is_opaque_closure) { + // If this is an opaque closure, implicitly load the env and switch + // the world age. The specTypes value is wrong for this field, so + // this needs to be handled first. + // jl_value_t *oc_type = get_oc_type(calltype, rettype); + Value *oc_this = decay_derived(ctx, &*AI); + ++AI; // both specsig (derived) and fptr1 (box) pass this argument as a distinct argument // Load closure world - Value *oc_this = decay_derived(ctx, &*AI++); - Value *argaddr = oc_this; - Value *worldaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, world)); - + Value *worldaddr = emit_ptrgep(ctx, oc_this, offsetof(jl_opaque_closure_t, world)); jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); ctx.world_age_at_entry = closure_world.V; // The tls world in a OC is the world of the closure emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); - // Load closure env - Value *envaddr = emit_ptrgep(ctx, argaddr, offsetof(jl_opaque_closure_t, captures)); + if (s == jl_unused_sym || vi.value.constant) + continue; - jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, - nullptr, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); - theArg = update_julia_type(ctx, closure_env, vi.value.typ); - } - else if (specsig) { - theArg = get_specsig_arg(argType, llvmArgType, isboxed); + // Load closure env, which is always a boxed value (usually some Tuple) currently + Value *envaddr = emit_ptrgep(ctx, oc_this, offsetof(jl_opaque_closure_t, captures)); + theArg = typed_load(ctx, envaddr, NULL, (jl_value_t*)vi.value.typ, + nullptr, nullptr, /*isboxed*/true, AtomicOrdering::NotAtomic, false, sizeof(void*)); } else { - if (i == 0) { - // first (function) arg is separate in jlcall - theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); + jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + // TODO: jl_nth_slot_type should call jl_rewrap_unionall? + // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise + bool isboxed = deserves_argbox(argType); + Type *llvmArgType = NULL; + llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); + if (s == jl_unused_sym || vi.value.constant) { + assert(vi.boxroot == NULL); + if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) { + ++AI; + auto tracked = CountTrackedPointers(llvmArgType); + if (tracked.count && !tracked.all) + ++AI; + } + continue; + } + if (specsig) { + theArg = get_specsig_arg(argType, llvmArgType, isboxed); } else { - Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - Value *load = ai.decorateInst(maybe_mark_load_dereferenceable( - ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), - false, vi.value.typ)); - theArg = mark_julia_type(ctx, load, true, vi.value.typ); - if (debug_enabled && vi.dinfo && !vi.boxroot) { - SmallVector addr; - addr.push_back(llvm::dwarf::DW_OP_deref); - addr.push_back(llvm::dwarf::DW_OP_plus_uconst); - addr.push_back((i - 1) * sizeof(void*)); - if ((Metadata*)vi.dinfo->getType() != debugcache.jl_pvalue_dillvmt) + if (i == 0) { + // first (function) arg is separate in jlcall + theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); + } + else { + Value *argPtr = emit_ptrgep(ctx, argArray, (i - 1) * ctx.types().sizeof_ptr); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + Value *load = ai.decorateInst(maybe_mark_load_dereferenceable( + ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, argPtr, Align(sizeof(void*))), + false, vi.value.typ)); + theArg = mark_julia_type(ctx, load, true, vi.value.typ); + if (debug_enabled && vi.dinfo && !vi.boxroot) { + SmallVector addr; addr.push_back(llvm::dwarf::DW_OP_deref); - dbuilder.insertDeclare(pargArray, vi.dinfo, dbuilder.createExpression(addr), - topdebugloc, - ctx.builder.GetInsertBlock()); + addr.push_back(llvm::dwarf::DW_OP_plus_uconst); + addr.push_back((i - 1) * sizeof(void*)); + if ((Metadata*)vi.dinfo->getType() != debugcache.jl_pvalue_dillvmt) + addr.push_back(llvm::dwarf::DW_OP_deref); + dbuilder.insertDeclare(pargArray, vi.dinfo, dbuilder.createExpression(addr), + topdebugloc, + ctx.builder.GetInsertBlock()); + } } } } @@ -9996,7 +9983,6 @@ jl_llvm_functions_t jl_emit_code( { JL_TIMING(CODEGEN, CODEGEN_LLVM); jl_timing_show_func_sig((jl_value_t *)li->specTypes, JL_TIMING_DEFAULT_BLOCK); - // caller must hold codegen_lock jl_llvm_functions_t decls = {}; assert((params.params == &jl_default_cgparams /* fast path */ || !params.cache || compare_cgparams(params.params, &jl_default_cgparams)) && @@ -10031,33 +10017,38 @@ jl_llvm_functions_t jl_emit_code( return decls; } +static int effects_foldable(uint32_t effects) +{ + // N.B.: This needs to be kept in sync with Core.Compiler.is_foldable(effects, true) + return ((effects & 0x7) == 0) && // is_consistent(effects) + (((effects >> 10) & 0x03) == 0) && // is_noub(effects) + (((effects >> 3) & 0x03) == 0) && // is_effect_free(effects) + ((effects >> 6) & 0x01); // is_terminates(effects) +} + static jl_llvm_functions_t jl_emit_oc_wrapper(orc::ThreadSafeModule &m, jl_codegen_params_t ¶ms, jl_method_instance_t *mi, jl_value_t *rettype) { - Module *M = m.getModuleUnlocked(); - jl_codectx_t ctx(M->getContext(), params, 0, 0); - ctx.name = M->getModuleIdentifier().data(); - std::string funcName = get_function_name(true, false, ctx.name, ctx.emission_context.TargetTriple); jl_llvm_functions_t declarations; declarations.functionObject = "jl_f_opaque_closure_call"; if (uses_specsig(mi->specTypes, false, rettype, true)) { + // context lock is held by params + Module *M = m.getModuleUnlocked(); + jl_codectx_t ctx(M->getContext(), params, 0, 0); + ctx.name = M->getModuleIdentifier().data(); + std::string funcName = get_function_name(true, false, ctx.name, ctx.emission_context.TargetTriple); jl_returninfo_t returninfo = get_specsig_function(ctx, M, NULL, funcName, mi->specTypes, rettype, true, JL_FEAT_TEST(ctx,gcstack_arg)); Function *gf_thunk = cast(returninfo.decl.getCallee()); jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); size_t nrealargs = jl_nparams(mi->specTypes); - emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, mi->specTypes, rettype, true, nrealargs, ctx.emission_context, ctx.min_world, ctx.max_world); + emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots, + mi->specTypes, rettype, true, nrealargs, ctx.emission_context, + prepare_call_in(gf_thunk->getParent(), jlopaque_closure_call_func), // TODO: this could call emit_oc_call directly + ctx.min_world, ctx.max_world); declarations.specFunctionObject = funcName; } return declarations; } -static int effects_foldable(uint32_t effects) -{ - // N.B.: This needs to be kept in sync with Core.Compiler.is_foldable(effects, true) - return ((effects & 0x7) == 0) && // is_consistent(effects) - (((effects >> 10) & 0x03) == 0) && // is_noub(effects) - (((effects >> 3) & 0x03) == 0) && // is_effect_free(effects) - ((effects >> 6) & 0x01); // is_terminates(effects) -} jl_llvm_functions_t jl_emit_codeinst( orc::ThreadSafeModule &m, @@ -10070,12 +10061,14 @@ jl_llvm_functions_t jl_emit_codeinst( JL_GC_PUSH1(&src); if (!src) { src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); - jl_method_t *def = codeinst->def->def.method; + jl_method_instance_t *mi = codeinst->def; + jl_method_t *def = mi->def.method; // Check if this is the generic method for opaque closure wrappers - - // if so, generate the specsig -> invoke converter. + // if so, this must compile specptr such that it holds the specptr -> invoke wrapper + // to satisfy the dispatching implementation requirements of jl_f_opaque_closure_call if (def == jl_opaque_closure_method) { JL_GC_POP(); - return jl_emit_oc_wrapper(m, params, codeinst->def, codeinst->rettype); + return jl_emit_oc_wrapper(m, params, mi, codeinst->rettype); } if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def)) src = jl_uncompress_ir(def, codeinst, (jl_value_t*)src); @@ -10149,135 +10142,15 @@ jl_llvm_functions_t jl_emit_codeinst( return decls; } - -void jl_compile_workqueue( - jl_codegen_params_t ¶ms, - CompilationPolicy policy) -{ - JL_TIMING(CODEGEN, CODEGEN_Workqueue); - jl_code_info_t *src = NULL; - JL_GC_PUSH1(&src); - while (!params.workqueue.empty()) { - jl_code_instance_t *codeinst; - auto it = params.workqueue.back(); - codeinst = it.first; - auto proto = it.second; - params.workqueue.pop_back(); - // try to emit code for this item from the workqueue - StringRef preal_decl = ""; - bool preal_specsig = false; - jl_callptr_t invoke = NULL; - if (params.cache) { - // WARNING: this correctness is protected by an outer lock - uint8_t specsigflags; - void *fptr; - jl_read_codeinst_invoke(codeinst, &specsigflags, &invoke, &fptr, 0); - //if (specsig ? specsigflags & 0b1 : invoke == jl_fptr_args_addr) - if (invoke == jl_fptr_args_addr) { - preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); - } - else if (specsigflags & 0b1) { - preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); - preal_specsig = true; - } - } - if (preal_decl.empty()) { - auto it = params.compiled_functions.find(codeinst); - if (it == params.compiled_functions.end()) { - // Reinfer the function. The JIT came along and removed the inferred - // method body. See #34993 - if ((policy != CompilationPolicy::Default || params.params->trim) && - jl_atomic_load_relaxed(&codeinst->inferred) == jl_nothing) { - // XXX: SOURCE_MODE_FORCE_SOURCE is wrong here (neither sufficient nor necessary) - codeinst = jl_type_infer(codeinst->def, jl_atomic_load_relaxed(&codeinst->max_world), SOURCE_MODE_FORCE_SOURCE); - } - if (codeinst) { - orc::ThreadSafeModule result_m = - jl_create_ts_module(name_from_method_instance(codeinst->def), - params.tsctx, params.DL, params.TargetTriple); - auto decls = jl_emit_codeinst(result_m, codeinst, NULL, params); - if (result_m) - it = params.compiled_functions.insert(std::make_pair(codeinst, std::make_pair(std::move(result_m), std::move(decls)))).first; - } - } - if (it != params.compiled_functions.end()) { - auto &decls = it->second.second; - if (decls.functionObject == "jl_fptr_args") { - preal_decl = decls.specFunctionObject; - } - else if (decls.functionObject != "jl_fptr_sparam") { - preal_decl = decls.specFunctionObject; - preal_specsig = true; - } - } - } - // patch up the prototype we emitted earlier - Module *mod = proto.decl->getParent(); - assert(proto.decl->isDeclaration()); - if (proto.specsig) { - // expected specsig - if (!preal_specsig) { - if (params.params->trim) { - auto it = params.compiled_functions.find(codeinst); //TODO: What to do about this - errs() << "Bailed out to invoke when compiling:"; - jl_(codeinst->def); - if (it != params.compiled_functions.end()) { - errs() << it->second.second.functionObject << "\n"; - errs() << it->second.second.specFunctionObject << "\n"; - } else - errs() << "codeinst not in compile_functions\n"; - } - // emit specsig-to-(jl)invoke conversion - StringRef invokeName; - if (invoke != NULL) - invokeName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, invoke, codeinst); - Function *preal = emit_tojlinvoke(codeinst, invokeName, mod, params); - proto.decl->setLinkage(GlobalVariable::InternalLinkage); - //protodecl->setAlwaysInline(); - jl_init_function(proto.decl, params.TargetTriple); - size_t nrealargs = jl_nparams(codeinst->def->specTypes); // number of actual arguments being passed - // TODO: maybe this can be cached in codeinst->specfptr? - emit_cfunc_invalidate(proto.decl, proto.cc, proto.return_roots, codeinst->def->specTypes, codeinst->rettype, false, nrealargs, params, preal, 0, 0); - preal_decl = ""; // no need to fixup the name - } - else { - assert(!preal_decl.empty()); - } - } - else { - // expected non-specsig - if (preal_decl.empty() || preal_specsig) { - // emit jlcall1-to-(jl)invoke conversion - StringRef invokeName; - if (invoke != NULL) - invokeName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, invoke, codeinst); - preal_decl = emit_tojlinvoke(codeinst, invokeName, mod, params)->getName(); - } - } - if (!preal_decl.empty()) { - // merge and/or rename this prototype to the real function - if (Value *specfun = mod->getNamedValue(preal_decl)) { - if (proto.decl != specfun) - proto.decl->replaceAllUsesWith(specfun); - } - else { - proto.decl->setName(preal_decl); - } - } - } - JL_GC_POP(); -} - - // --- initialization --- -SmallVector, 0> gv_for_global; +static auto gv_for_global = new SmallVector, 0>(); static void global_jlvalue_to_llvm(JuliaVariable *var, jl_value_t **addr) { - gv_for_global.push_back(std::make_pair(addr, var)); + gv_for_global->push_back(std::make_pair(addr, var)); } static JuliaVariable *julia_const_gv(jl_value_t *val) { - for (auto &kv : gv_for_global) { + for (auto &kv : *gv_for_global) { if (*kv.first == val) return kv.second; } @@ -10286,6 +10159,9 @@ static JuliaVariable *julia_const_gv(jl_value_t *val) static void init_jit_functions(void) { + add_named_global("jl_fptr_args", jl_fptr_args_addr); + add_named_global("jl_fptr_sparam", jl_fptr_sparam_addr); + add_named_global("jl_f_opaque_closure_call", &jl_f_opaque_closure_call); add_named_global(jl_small_typeof_var, &jl_small_typeof); add_named_global(jlstack_chk_guard_var, &__stack_chk_guard); add_named_global(jlRTLD_DEFAULT_var, &jl_RTLD_DEFAULT_handle); diff --git a/src/debug-registry.h b/src/debug-registry.h index 4c9e13d8cd72d..4d0b7a44f19e5 100644 --- a/src/debug-registry.h +++ b/src/debug-registry.h @@ -32,7 +32,7 @@ class JITDebugInfoRegistry std::unique_lock lock; CResourceT &resource; - Lock(std::mutex &mutex, CResourceT &resource) JL_NOTSAFEPOINT : lock(mutex), resource(resource) {} + Lock(std::mutex &mutex, CResourceT &resource) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER : lock(mutex), resource(resource) {} Lock(Lock &&) JL_NOTSAFEPOINT = default; Lock &operator=(Lock &&) JL_NOTSAFEPOINT = default; @@ -56,7 +56,7 @@ class JITDebugInfoRegistry return resource; } - ~Lock() JL_NOTSAFEPOINT = default; + ~Lock() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE = default; }; private: @@ -68,15 +68,15 @@ class JITDebugInfoRegistry Locked(ResourceT resource = ResourceT()) JL_NOTSAFEPOINT : mutex(), resource(std::move(resource)) {} - LockT operator*() JL_NOTSAFEPOINT { + LockT operator*() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return LockT(mutex, resource); } - ConstLockT operator*() const JL_NOTSAFEPOINT { + ConstLockT operator*() const JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return ConstLockT(mutex, resource); } - ~Locked() JL_NOTSAFEPOINT = default; + ~Locked() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE = default; }; struct image_info_t { @@ -105,6 +105,7 @@ class JITDebugInfoRegistry std::unique_ptr object; std::unique_ptr context; LazyObjectInfo() = delete; + ~LazyObjectInfo() JL_NOTSAFEPOINT = default; }; struct SectionInfo { @@ -113,6 +114,7 @@ class JITDebugInfoRegistry ptrdiff_t slide; uint64_t SectionIndex; SectionInfo() = delete; + ~SectionInfo() JL_NOTSAFEPOINT = default; }; template @@ -145,7 +147,7 @@ class JITDebugInfoRegistry void add_code_in_flight(llvm::StringRef name, jl_code_instance_t *codeinst, const llvm::DataLayout &DL) JL_NOTSAFEPOINT; jl_method_instance_t *lookupLinfo(size_t pointer) JL_NOTSAFEPOINT; void registerJITObject(const llvm::object::ObjectFile &Object, - std::function getLoadAddress); + std::function getLoadAddress) JL_NOTSAFEPOINT; objectmap_t& getObjectMap() JL_NOTSAFEPOINT; void add_image_info(image_info_t info) JL_NOTSAFEPOINT; bool get_image_info(uint64_t base, image_info_t *info) const JL_NOTSAFEPOINT; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index f6fca47e9a889..31f1ba8281a89 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -58,7 +58,7 @@ extern "C" void __register_frame(void*) JL_NOTSAFEPOINT; extern "C" void __deregister_frame(void*) JL_NOTSAFEPOINT; template -static void processFDEs(const char *EHFrameAddr, size_t EHFrameSize, callback f) +static void processFDEs(const char *EHFrameAddr, size_t EHFrameSize, callback f) JL_NOTSAFEPOINT { const char *P = EHFrameAddr; const char *End = P + EHFrameSize; @@ -164,6 +164,12 @@ static void jl_profile_atomic(T f) JL_NOTSAFEPOINT // --- storing and accessing source location metadata --- void jl_add_code_in_flight(StringRef name, jl_code_instance_t *codeinst, const DataLayout &DL) { + // Non-opaque-closure MethodInstances are considered globally rooted + // through their methods, but for OC, we need to create a global root + // here. + jl_method_instance_t *mi = codeinst->def; + if (jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure) + jl_as_global_root((jl_value_t*)mi, 1); getJITDebugRegistry().add_code_in_flight(name, codeinst, DL); } @@ -369,11 +375,6 @@ void JITDebugInfoRegistry::registerJITObject(const object::ObjectFile &Object, if (codeinst) { JL_GC_PROMISE_ROOTED(codeinst); mi = codeinst->def; - // Non-opaque-closure MethodInstances are considered globally rooted - // through their methods, but for OC, we need to create a global root - // here. - if (jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure) - mi = (jl_method_instance_t*)jl_as_global_root((jl_value_t*)mi, 1); } jl_profile_atomic([&]() JL_NOTSAFEPOINT { if (mi) @@ -1281,14 +1282,14 @@ void register_eh_frames(uint8_t *Addr, size_t Size) { // On OS X OS X __register_frame takes a single FDE as an argument. // See http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-April/061768.html - processFDEs((char*)Addr, Size, [](const char *Entry) { + processFDEs((char*)Addr, Size, [](const char *Entry) JL_NOTSAFEPOINT { getJITDebugRegistry().libc_frames.libc_register_frame(Entry); }); } void deregister_eh_frames(uint8_t *Addr, size_t Size) { - processFDEs((char*)Addr, Size, [](const char *Entry) { + processFDEs((char*)Addr, Size, [](const char *Entry) JL_NOTSAFEPOINT { getJITDebugRegistry().libc_frames.libc_deregister_frame(Entry); }); } @@ -1300,7 +1301,7 @@ void deregister_eh_frames(uint8_t *Addr, size_t Size) // Skip over an arbitrary long LEB128 encoding. // Return the pointer to the first unprocessed byte. -static const uint8_t *consume_leb128(const uint8_t *Addr, const uint8_t *End) +static const uint8_t *consume_leb128(const uint8_t *Addr, const uint8_t *End) JL_NOTSAFEPOINT { const uint8_t *P = Addr; while ((*P >> 7) != 0 && P < End) @@ -1312,7 +1313,7 @@ static const uint8_t *consume_leb128(const uint8_t *Addr, const uint8_t *End) // bytes than what there are more bytes than what the type can store. // Adjust the pointer to the first unprocessed byte. template static T parse_leb128(const uint8_t *&Addr, - const uint8_t *End) + const uint8_t *End) JL_NOTSAFEPOINT { typedef typename std::make_unsigned::type uT; uT v = 0; @@ -1335,7 +1336,7 @@ template static T parse_leb128(const uint8_t *&Addr, } template -static U safe_trunc(T t) +static U safe_trunc(T t) JL_NOTSAFEPOINT { assert((t >= static_cast(std::numeric_limits::min())) && (t <= static_cast(std::numeric_limits::max()))); @@ -1375,7 +1376,7 @@ enum DW_EH_PE : uint8_t { }; // Parse the CIE and return the type of encoding used by FDE -static DW_EH_PE parseCIE(const uint8_t *Addr, const uint8_t *End) +static DW_EH_PE parseCIE(const uint8_t *Addr, const uint8_t *End) JL_NOTSAFEPOINT { // https://www.airs.com/blog/archives/460 // Length (4 bytes) @@ -1481,7 +1482,7 @@ void register_eh_frames(uint8_t *Addr, size_t Size) // Now first count the number of FDEs size_t nentries = 0; - processFDEs((char*)Addr, Size, [&](const char*){ nentries++; }); + processFDEs((char*)Addr, Size, [&](const char*) JL_NOTSAFEPOINT { nentries++; }); if (nentries == 0) return; @@ -1510,7 +1511,7 @@ void register_eh_frames(uint8_t *Addr, size_t Size) // CIE's (may not happen) without parsing it every time. const uint8_t *cur_cie = nullptr; DW_EH_PE encoding = DW_EH_PE_omit; - processFDEs((char*)Addr, Size, [&](const char *Entry) { + processFDEs((char*)Addr, Size, [&](const char *Entry) JL_NOTSAFEPOINT { // Skip Length (4bytes) and CIE offset (4bytes) uint32_t fde_size = *(const uint32_t*)Entry; uint32_t cie_id = ((const uint32_t*)Entry)[1]; @@ -1631,7 +1632,7 @@ void deregister_eh_frames(uint8_t *Addr, size_t Size) #endif extern "C" JL_DLLEXPORT_CODEGEN -uint64_t jl_getUnwindInfo_impl(uint64_t dwAddr) +uint64_t jl_getUnwindInfo_impl(uint64_t dwAddr) JL_NOTSAFEPOINT { // Might be called from unmanaged thread jl_lock_profile(); diff --git a/src/engine.cpp b/src/engine.cpp index 6db4dce44e48e..2b68de731c4dd 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -45,8 +45,8 @@ template<> struct llvm::DenseMapInfo { } }; -static std::mutex engine_lock; -static std::condition_variable engine_wait; +static std::mutex engine_lock; // n.b. this lock is only ever held briefly +static std::condition_variable engine_wait; // but it may be waiting a while in this state // map from MethodInstance to threadid that owns it currently for inference static DenseMap Reservations; // vector of which threads are blocked and which lease they need @@ -63,55 +63,51 @@ jl_code_instance_t *jl_engine_reserve(jl_method_instance_t *m, jl_value_t *owner ct->ptls->engine_nqueued++; // disables finalizers until inference is finished on this method graph jl_code_instance_t *ci = jl_new_codeinst_uninit(m, owner); // allocate a placeholder JL_GC_PUSH1(&ci); - int8_t gc_state = jl_gc_safe_enter(ct->ptls); - InferKey key = {m, owner}; - std::unique_lock lock(engine_lock); auto tid = jl_atomic_load_relaxed(&ct->tid); - if ((signed)Awaiting.size() < tid + 1) - Awaiting.resize(tid + 1); - while (1) { - auto record = Reservations.find(key); - if (record == Reservations.end()) { - Reservations[key] = ReservationInfo{tid, ci}; - lock.unlock(); - jl_gc_safe_leave(ct->ptls, gc_state); // contains jl_gc_safepoint - JL_GC_POP(); - return ci; - } - // before waiting, need to run deadlock/cycle detection - // there is a cycle if the thread holding our lease is blocked - // and waiting for (transitively) any lease that is held by this thread - auto wait_tid = record->second.tid; - while (1) { - if (wait_tid == tid) { - lock.unlock(); - jl_gc_safe_leave(ct->ptls, gc_state); // contains jl_gc_safepoint - JL_GC_POP(); - ct->ptls->engine_nqueued--; - return ci; // break the cycle + if (([tid, m, owner, ci] () -> bool { // necessary scope block / lambda for unique_lock + jl_unique_gcsafe_lock lock(engine_lock); + InferKey key{m, owner}; + if ((signed)Awaiting.size() < tid + 1) + Awaiting.resize(tid + 1); + while (1) { + auto record = Reservations.find(key); + if (record == Reservations.end()) { + Reservations[key] = ReservationInfo{tid, ci}; + return false; + } + // before waiting, need to run deadlock/cycle detection + // there is a cycle if the thread holding our lease is blocked + // and waiting for (transitively) any lease that is held by this thread + auto wait_tid = record->second.tid; + while (1) { + if (wait_tid == tid) + return true; + if ((signed)Awaiting.size() <= wait_tid) + break; // no cycle, since it is running (and this should be unreachable) + auto key2 = Awaiting[wait_tid]; + if (key2.mi == nullptr) + break; // no cycle, since it is running + auto record2 = Reservations.find(key2); + if (record2 == Reservations.end()) + break; // no cycle, since it is about to resume + assert(wait_tid != record2->second.tid); + wait_tid = record2->second.tid; + } + Awaiting[tid] = key; + lock.wait(engine_wait); + Awaiting[tid] = InferKey{}; } - if ((signed)Awaiting.size() <= wait_tid) - break; // no cycle, since it is running (and this should be unreachable) - auto key2 = Awaiting[wait_tid]; - if (key2.mi == nullptr) - break; // no cycle, since it is running - auto record2 = Reservations.find(key2); - if (record2 == Reservations.end()) - break; // no cycle, since it is about to resume - assert(wait_tid != record2->second.tid); - wait_tid = record2->second.tid; - } - Awaiting[tid] = key; - engine_wait.wait(lock); - Awaiting[tid] = InferKey{}; - } + })()) + ct->ptls->engine_nqueued--; + JL_GC_POP(); + return ci; } int jl_engine_hasreserved(jl_method_instance_t *m, jl_value_t *owner) { jl_task_t *ct = jl_current_task; InferKey key = {m, owner}; - std::unique_lock lock(engine_lock); + std::unique_lock lock(engine_lock); auto record = Reservations.find(key); return record != Reservations.end() && record->second.tid == jl_atomic_load_relaxed(&ct->tid); } @@ -123,7 +119,7 @@ STATIC_INLINE int gc_marked(uintptr_t bits) JL_NOTSAFEPOINT void jl_engine_sweep(jl_ptls_t *gc_all_tls_states) { - std::unique_lock lock(engine_lock); + std::unique_lock lock(engine_lock); bool any = false; for (auto I = Reservations.begin(); I != Reservations.end(); ++I) { jl_code_instance_t *ci = I->second.ci; @@ -142,7 +138,7 @@ void jl_engine_sweep(jl_ptls_t *gc_all_tls_states) void jl_engine_fulfill(jl_code_instance_t *ci, jl_code_info_t *src) { jl_task_t *ct = jl_current_task; - std::unique_lock lock(engine_lock); + std::unique_lock lock(engine_lock); auto record = Reservations.find(InferKey{ci->def, ci->owner}); if (record == Reservations.end() || record->second.ci != ci) return; @@ -152,7 +148,6 @@ void jl_engine_fulfill(jl_code_instance_t *ci, jl_code_info_t *src) engine_wait.notify_all(); } - #ifdef __cplusplus } #endif diff --git a/src/gf.c b/src/gf.c index fc2e62ebff96b..e77c950c38ae4 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3222,6 +3222,23 @@ jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_v return tt; } +// undo jl_argtype_with_function transform +jl_value_t *jl_argtype_without_function(jl_value_t *ftypes) +{ + jl_value_t *types = jl_unwrap_unionall(ftypes); + size_t l = jl_nparams(types); + if (l == 1 && jl_is_vararg(jl_tparam0(types))) + return ftypes; + jl_value_t *tt = (jl_value_t*)jl_alloc_svec(l - 1); + JL_GC_PUSH1(&tt); + for (size_t i = 1; i < l; i++) + jl_svecset(tt, i - 1, jl_tparam(types, i)); + tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt, 0); + tt = jl_rewrap_unionall_(tt, types); + JL_GC_POP(); + return tt; +} + #ifdef JL_TRACE static int trace_en = 0; static int error_en = 1; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 313449dda5557..8b8004af03616 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -64,9 +64,6 @@ using namespace llvm; #define DEBUG_TYPE "julia_jitlayers" STATISTIC(LinkedGlobals, "Number of globals linked"); -STATISTIC(CompiledCodeinsts, "Number of codeinsts compiled directly"); -STATISTIC(MaxWorkqueueSize, "Maximum number of elements in the workqueue"); -STATISTIC(IndirectCodeinsts, "Number of dependent codeinsts compiled"); STATISTIC(SpecFPtrCount, "Number of specialized function pointers compiled"); STATISTIC(UnspecFPtrCount, "Number of specialized function pointers compiled"); STATISTIC(ModulesAdded, "Number of modules added to the JIT"); @@ -151,13 +148,6 @@ void jl_dump_llvm_opt_impl(void *s) **jl_ExecutionEngine->get_dump_llvm_opt_stream() = (ios_t*)s; } -#ifndef JL_USE_JITLINK -static int jl_add_to_ee( - orc::ThreadSafeModule &M, - const StringMap &NewExports, - DenseMap &Queued, - SmallVectorImpl &Stack) JL_NOTSAFEPOINT; -#endif static void jl_decorate_module(Module &M) JL_NOTSAFEPOINT; void jl_link_global(GlobalVariable *GV, void *addr) JL_NOTSAFEPOINT @@ -187,214 +177,536 @@ void jl_jit_globals(std::map &globals) JL_NOTSAFEPOINT } } -// this generates llvm code for the lambda info -// and adds the result to the jitlayers -// (and the shadow module), -// and generates code for it -static jl_callptr_t _jl_compile_codeinst( - jl_code_instance_t *codeinst, - jl_code_info_t *src, - orc::ThreadSafeContext context) + // lock for places where only single threaded behavior is implemented, so we need GC support +static jl_mutex_t jitlock; + // locks for adding external code to the JIT atomically +static std::mutex extern_c_lock; + // locks and barriers for this state +static std::mutex engine_lock; +static std::condition_variable engine_wait; +static int threads_in_compiler_phase; + // the TSM for each codeinst +static SmallVector sharedmodules; +static DenseMap emittedmodules; + // the invoke and specsig function names in the JIT +static DenseMap invokenames; + // everything that any thread wants to compile right now +static DenseSet compileready; + // everything that any thread has compiled recently +static DenseSet linkready; + // a map from a codeinst to the outgoing edges needed before linking it +static DenseMap> complete_graph; + // the state for each codeinst and the number of unresolved edges (we don't + // really need this once JITLink is available everywhere, since every module + // is automatically complete, and we can emit any required fixups later as a + // separate module) +static DenseMap> incompletemodules; + // the set of incoming unresolved edges resolved by a codeinstance +static DenseMap> incomplete_rgraph; + +// Lock hierarchy here: +// jitlock is outermost, can contain others and allows GC +// engine_lock is next +// ThreadSafeContext locks are next, they should not be nested (unless engine_lock is also held, but this may make TSAN sad anyways) +// extern_c_lock is next +// jl_ExecutionEngine internal locks are exclusive to this list, since OrcJIT promises to never hold a lock over a materialization unit: +// construct a query object from a query set and query handler +// lock the session +// lodge query against requested symbols, collect required materializers (if any) +// unlock the session +// dispatch materializers (if any) +// However, this guarantee relies on Julia releasing all TSC locks before causing any materialization units to be dispatched +// as materialization may need to acquire TSC locks. + + +static void finish_params(Module *M, jl_codegen_params_t ¶ms) JL_NOTSAFEPOINT { - // caller must hold codegen_lock - // and have disabled finalizers - uint64_t start_time = 0; - bool timed = !!*jl_ExecutionEngine->get_dump_compiles_stream(); - if (timed) - start_time = jl_hrtime(); + if (params._shared_module) { + sharedmodules.push_back(orc::ThreadSafeModule(std::move(params._shared_module), params.tsctx)); + } + + // In imaging mode, we can't inline global variable initializers in order to preserve + // the fiction that we don't know what loads from the global will return. Thus, we + // need to emit a separate module for the globals before any functions are compiled, + // to ensure that the globals are defined when they are compiled. + if (params.imaging_mode) { + if (!params.global_targets.empty()) { + void **globalslots = new void*[params.global_targets.size()]; + void **slot = globalslots; + for (auto &global : params.global_targets) { + auto GV = global.second; + *slot = global.first; + jl_ExecutionEngine->addGlobalMapping(GV->getName(), (uintptr_t)slot); + slot++; + } +#ifdef __clang_analyzer__ + static void **leaker = globalslots; // for the purpose of the analyzer, we need to expressly leak this variable or it thinks we forgot to free it +#endif + } + } + else { + StringMap NewGlobals; + for (auto &global : params.global_targets) { + NewGlobals[global.second->getName()] = global.first; + } + for (auto &GV : M->globals()) { + auto InitValue = NewGlobals.find(GV.getName()); + if (InitValue != NewGlobals.end()) { + jl_link_global(&GV, InitValue->second); + } + } + } +} - assert(jl_is_code_instance(codeinst)); - JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); - jl_callptr_t fptr = NULL; - // emit the code in LLVM IR form - jl_codegen_params_t params(std::move(context), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context - params.cache = true; - params.imaging_mode = imaging_default(); - params.debug_level = jl_options.debug_level; - { - orc::ThreadSafeModule result_m = - jl_create_ts_module(name_from_method_instance(codeinst->def), params.tsctx, params.DL, params.TargetTriple); - jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, src, params); - if (result_m) - params.compiled_functions[codeinst] = {std::move(result_m), std::move(decls)}; - jl_compile_workqueue(params, CompilationPolicy::Default); - - if (params._shared_module) { - jl_ExecutionEngine->optimizeDLSyms(*params._shared_module); - jl_ExecutionEngine->addModule(orc::ThreadSafeModule(std::move(params._shared_module), params.tsctx)); +static int jl_analyze_workqueue(jl_code_instance_t *callee, jl_codegen_params_t ¶ms, bool forceall=false) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER +{ + jl_task_t *ct = jl_current_task; + decltype(params.workqueue) edges; + std::swap(params.workqueue, edges); + for (auto &it : edges) { + jl_code_instance_t *codeinst = it.first; + auto &proto = it.second; + // try to emit code for this item from the workqueue + StringRef invokeName = ""; + StringRef preal_decl = ""; + bool preal_specsig = false; + jl_callptr_t invoke = nullptr; + bool isedge = false; + assert(params.cache); + // Checking the cache here is merely an optimization and not strictly required + // But it must be consistent with the following invokenames lookup, which is protected by the engine_lock + uint8_t specsigflags; + void *fptr; + jl_read_codeinst_invoke(codeinst, &specsigflags, &invoke, &fptr, 0); + //if (specsig ? specsigflags & 0b1 : invoke == jl_fptr_args_addr) + if (invoke == jl_fptr_args_addr) { + preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); } - - // In imaging mode, we can't inline global variable initializers in order to preserve - // the fiction that we don't know what loads from the global will return. Thus, we - // need to emit a separate module for the globals before any functions are compiled, - // to ensure that the globals are defined when they are compiled. - if (params.imaging_mode) { - // Won't contain any PLT/dlsym calls, so no need to optimize those - if (!params.global_targets.empty()) { - void **globalslots = new void*[params.global_targets.size()]; - void **slot = globalslots; - for (auto &global : params.global_targets) { - auto GV = global.second; - *slot = global.first; - jl_ExecutionEngine->addGlobalMapping(GV->getName(), (uintptr_t)slot); - slot++; + else if (specsigflags & 0b1) { + preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); + preal_specsig = true; + } + bool force = forceall || invoke != nullptr; + if (preal_decl.empty()) { + auto it = invokenames.find(codeinst); + if (it != invokenames.end()) { + auto &decls = it->second; + invokeName = decls.functionObject; + if (decls.functionObject == "jl_fptr_args") { + preal_decl = decls.specFunctionObject; + isedge = true; } -#ifdef __clang_analyzer__ - static void **leaker = globalslots; // for the purpose of the analyzer, we need to expressly leak this variable or it thinks we forgot to free it -#endif + else if (decls.functionObject != "jl_fptr_sparam" && decls.functionObject != "jl_f_opaque_closure_call") { + preal_decl = decls.specFunctionObject; + preal_specsig = true; + isedge = true; + } + force = true; } } - else { - StringMap NewGlobals; - for (auto &global : params.global_targets) { - NewGlobals[global.second->getName()] = global.first; + if (!preal_decl.empty() || force) { + // if we have a prototype emitted, compare it to what we emitted earlier + Module *mod = proto.decl->getParent(); + assert(proto.decl->isDeclaration()); + Function *pinvoke = nullptr; + if (preal_decl.empty()) { + if (invoke != nullptr && invokeName.empty()) { + assert(invoke != jl_fptr_args_addr); + if (invoke == jl_fptr_sparam_addr) + invokeName = "jl_fptr_sparam"; + else if (invoke == jl_f_opaque_closure_call_addr) + invokeName = "jl_f_opaque_closure_call"; + else + invokeName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, invoke, codeinst); + } + pinvoke = emit_tojlinvoke(codeinst, invokeName, mod, params); + if (!proto.specsig) + proto.decl->replaceAllUsesWith(pinvoke); + isedge = false; } - for (auto &def : params.compiled_functions) { - auto M = std::get<0>(def.second).getModuleUnlocked(); - for (auto &GV : M->globals()) { - auto InitValue = NewGlobals.find(GV.getName()); - if (InitValue != NewGlobals.end()) { - jl_link_global(&GV, InitValue->second); - } + if (proto.specsig && !preal_specsig) { + // get or build an fptr1 that can invoke codeinst + if (pinvoke == nullptr) + pinvoke = get_or_emit_fptr1(preal_decl, mod); + // emit specsig-to-(jl)invoke conversion + proto.decl->setLinkage(GlobalVariable::InternalLinkage); + //protodecl->setAlwaysInline(); + jl_init_function(proto.decl, params.TargetTriple); + // TODO: maybe this can be cached in codeinst->specfptr? + int8_t gc_state = jl_gc_unsafe_enter(ct->ptls); // codegen may contain safepoints (such as jl_subtype calls) + jl_method_instance_t *mi = codeinst->def; + size_t nrealargs = jl_nparams(mi->specTypes); // number of actual arguments being passed + bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; + emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke, 0, 0); + jl_gc_unsafe_leave(ct->ptls, gc_state); + preal_decl = ""; // no need to fixup the name + } + if (!preal_decl.empty()) { + // merge and/or rename this prototype to the real function + if (Value *specfun = mod->getNamedValue(preal_decl)) { + if (proto.decl != specfun) + proto.decl->replaceAllUsesWith(specfun); + } + else { + proto.decl->setName(preal_decl); } } - } - -#ifndef JL_USE_JITLINK - // Collect the exported functions from the params.compiled_functions modules, - // which form dependencies on which functions need to be - // compiled first. Cycles of functions are compiled together. - // (essentially we compile a DAG of SCCs in reverse topological order, - // if we treat declarations of external functions as edges from declaration - // to definition) - StringMap NewExports; - for (auto &def : params.compiled_functions) { - orc::ThreadSafeModule &TSM = std::get<0>(def.second); - //The underlying context object is still locked because params is not destroyed yet - auto M = TSM.getModuleUnlocked(); - jl_ExecutionEngine->optimizeDLSyms(*M); - for (auto &F : M->global_objects()) { - if (!F.isDeclaration() && F.getLinkage() == GlobalValue::ExternalLinkage) { - NewExports[F.getName()] = &TSM; + if (proto.oc) { // additionally, if we are dealing with an OC constructor, then we might also need to fix up the fptr1 reference too + assert(proto.specsig); + StringRef ocinvokeDecl = invokeName; + if (invoke != nullptr && ocinvokeDecl.empty()) { + // check for some special tokens used by opaque_closure.c and convert those to their real functions + assert(invoke != jl_fptr_args_addr); + assert(invoke != jl_fptr_sparam_addr); + if (invoke == jl_fptr_interpret_call_addr) + ocinvokeDecl = "jl_fptr_interpret_call"; + else if (invoke == jl_fptr_const_return_addr) + ocinvokeDecl = "jl_fptr_const_return"; + else if (invoke == jl_f_opaque_closure_call_addr) + ocinvokeDecl = "jl_f_opaque_closure_call"; + //else if (invoke == jl_interpret_opaque_closure_addr) + else + ocinvokeDecl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, invoke, codeinst); + } + // if OC expected a specialized specsig dispatch, but we don't have it, use the inner trampoline here too + // XXX: this invoke translation logic is supposed to exactly match new_opaque_closure + if (!preal_specsig || ocinvokeDecl == "jl_f_opaque_closure_call" || ocinvokeDecl == "jl_fptr_interpret_call" || ocinvokeDecl == "jl_fptr_const_return") { + if (pinvoke == nullptr) + ocinvokeDecl = get_or_emit_fptr1(preal_decl, mod)->getName(); + else + ocinvokeDecl = pinvoke->getName(); + } + assert(!ocinvokeDecl.empty()); + assert(ocinvokeDecl != "jl_fptr_args"); + assert(ocinvokeDecl != "jl_fptr_sparam"); + // merge and/or rename this prototype to the real function + if (Value *specfun = mod->getNamedValue(ocinvokeDecl)) { + if (proto.oc != specfun) + proto.oc->replaceAllUsesWith(specfun); + } + else { + proto.oc->setName(ocinvokeDecl); } } } - DenseMap Queued; - SmallVector Stack; - for (auto &def : params.compiled_functions) { - // Add the results to the execution engine now - orc::ThreadSafeModule &M = std::get<0>(def.second); - jl_add_to_ee(M, NewExports, Queued, Stack); - assert(Queued.empty() && Stack.empty() && !M); + else { + isedge = true; + params.workqueue.push_back(it); + incomplete_rgraph[codeinst].push_back(callee); } -#else - for (auto &def : params.compiled_functions) { - // Add the results to the execution engine now - orc::ThreadSafeModule &M = std::get<0>(def.second); - if (M) - jl_ExecutionEngine->addModule(std::move(M)); + if (isedge) + complete_graph[callee].push_back(codeinst); + } + return params.workqueue.size(); +} + +// test whether codeinst->invoke is usable already without further compilation needed +static bool jl_is_compiled_codeinst(jl_code_instance_t *codeinst) JL_NOTSAFEPOINT +{ + auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + if (invoke == nullptr || invoke == jl_fptr_wait_for_compiled_addr) + return false; + return true; +} + +// move codeinst (and deps) from incompletemodules to emitted modules +// and populate compileready from complete_graph +static void prepare_compile(jl_code_instance_t *codeinst) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER +{ + SmallVector workqueue; + workqueue.push_back(codeinst); + while (!workqueue.empty()) { + codeinst = workqueue.pop_back_val(); + if (!invokenames.count(codeinst)) { + // this means it should be compiled already while the callee was in stasis + assert(jl_is_compiled_codeinst(codeinst)); + continue; } -#endif - ++CompiledCodeinsts; - MaxWorkqueueSize.updateMax(params.compiled_functions.size()); - IndirectCodeinsts += params.compiled_functions.size() - 1; - } - - // batch compile job for all new functions - SmallVector NewDefs; - for (auto &def : params.compiled_functions) { - jl_llvm_functions_t &decls = std::get<1>(def.second); - if (decls.functionObject != "jl_fptr_args" && - decls.functionObject != "jl_fptr_sparam" && - decls.functionObject != "jl_f_opaque_closure_call") - NewDefs.push_back(decls.functionObject); - if (!decls.specFunctionObject.empty()) - NewDefs.push_back(decls.specFunctionObject); - } - auto Addrs = jl_ExecutionEngine->findSymbols(NewDefs); - - size_t i = 0; - size_t nextaddr = 0; - for (auto &def : params.compiled_functions) { - jl_code_instance_t *this_code = def.first; - if (i < jl_timing_print_limit) - jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_DEFAULT_BLOCK); - - jl_llvm_functions_t &decls = std::get<1>(def.second); - jl_callptr_t addr; - bool isspecsig = false; - if (decls.functionObject == "jl_fptr_args") { - addr = jl_fptr_args_addr; + // if this was incomplete, force completion now of it + auto it = incompletemodules.find(codeinst); + if (it != incompletemodules.end()) { + int waiting = 0; + auto &edges = complete_graph[codeinst]; + auto edges_end = std::remove_if(edges.begin(), edges.end(), [&waiting, codeinst] (jl_code_instance_t *edge) JL_NOTSAFEPOINT -> bool { + auto &redges = incomplete_rgraph[edge]; + // waiting += std::erase(redges, codeinst); + auto redges_end = std::remove(redges.begin(), redges.end(), codeinst); + if (redges_end != redges.end()) { + waiting += redges.end() - redges_end; + redges.erase(redges_end, redges.end()); + assert(!invokenames.count(edge)); + } + return !invokenames.count(edge); + }); + edges.erase(edges_end, edges.end()); + assert(waiting == std::get<1>(it->second)); + std::get<1>(it->second) = 0; + auto ¶ms = std::get<0>(it->second); + params.tsctx_lock = params.tsctx.getLock(); + waiting = jl_analyze_workqueue(codeinst, params, true); // may safepoint + assert(!waiting); (void)waiting; + Module *M = emittedmodules[codeinst].getModuleUnlocked(); + finish_params(M, params); + incompletemodules.erase(it); + } + // and then indicate this should be compiled now + if (!linkready.count(codeinst) && compileready.insert(codeinst).second) { + auto edges = complete_graph.find(codeinst); + if (edges != complete_graph.end()) { + workqueue.append(edges->second); + } + } + } +} + +// notify any other pending work that this edge now has code defined +static void complete_emit(jl_code_instance_t *edge) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER +{ + auto notify = incomplete_rgraph.find(edge); + if (notify == incomplete_rgraph.end()) + return; + auto redges = std::move(notify->second); + incomplete_rgraph.erase(notify); + for (size_t i = 0; i < redges.size(); i++) { + jl_code_instance_t *callee = redges[i]; + auto it = incompletemodules.find(callee); + assert(it != incompletemodules.end()); + if (--std::get<1>(it->second) == 0) { + auto ¶ms = std::get<0>(it->second); + params.tsctx_lock = params.tsctx.getLock(); + assert(callee == it->first); + int waiting = jl_analyze_workqueue(callee, params); // may safepoint + assert(!waiting); (void)waiting; + Module *M = emittedmodules[callee].getModuleUnlocked(); + finish_params(M, params); + incompletemodules.erase(it); } - else if (decls.functionObject == "jl_fptr_sparam") { - addr = jl_fptr_sparam_addr; + } +} + + +// set the invoke field for codeinst (and all deps, and assist with other pending work from other threads) now +static void jl_compile_codeinst_now(jl_code_instance_t *codeinst) +{ + jl_unique_gcsafe_lock lock(engine_lock); + if (!invokenames.count(codeinst)) + return; + threads_in_compiler_phase++; + prepare_compile(codeinst); // may safepoint + while (1) { + // TODO: split up this work by ThreadSafeContext, so two threads don't need to get the same locks and stall + if (!sharedmodules.empty()) { + auto TSM = sharedmodules.pop_back_val(); + lock.native.unlock(); + { + auto Lock = TSM.getContext().getLock(); + jl_ExecutionEngine->optimizeDLSyms(*TSM.getModuleUnlocked()); // may safepoint + } + jl_ExecutionEngine->addModule(std::move(TSM)); + lock.native.lock(); } - else if (decls.functionObject == "jl_f_opaque_closure_call") { - addr = jl_f_opaque_closure_call_addr; + else if (!compileready.empty()) { + // move a function from compileready to linkready then compile it + auto compilenext = compileready.begin(); + codeinst = *compilenext; + compileready.erase(compilenext); + auto TSMref = emittedmodules.find(codeinst); + assert(TSMref != emittedmodules.end()); + auto TSM = std::move(TSMref->second); + linkready.insert(codeinst); + emittedmodules.erase(TSMref); + lock.native.unlock(); + uint64_t start_time = jl_hrtime(); + { + auto Lock = TSM.getContext().getLock(); + jl_ExecutionEngine->optimizeDLSyms(*TSM.getModuleUnlocked()); // may safepoint + } + jl_ExecutionEngine->addModule(std::move(TSM)); // may safepoint + // If logging of the compilation stream is enabled, + // then dump the method-instance specialization type to the stream + jl_method_instance_t *mi = codeinst->def; + if (jl_is_method(mi->def.method)) { + auto stream = *jl_ExecutionEngine->get_dump_compiles_stream(); + if (stream) { + uint64_t end_time = jl_hrtime(); + ios_printf(stream, "%" PRIu64 "\t\"", end_time - start_time); + jl_static_show((JL_STREAM*)stream, mi->specTypes); + ios_printf(stream, "\"\n"); + } + } + lock.native.lock(); } else { - assert(NewDefs[nextaddr] == decls.functionObject); - addr = (jl_callptr_t)Addrs[nextaddr++]; - assert(addr); - isspecsig = true; + break; } - if (!decls.specFunctionObject.empty()) { - void *prev_specptr = NULL; - assert(NewDefs[nextaddr] == decls.specFunctionObject); - void *spec = (void*)Addrs[nextaddr++]; - assert(spec); - if (jl_atomic_cmpswap_acqrel(&this_code->specptr.fptr, &prev_specptr, spec)) { - // only set specsig and invoke if we were the first to set specptr - jl_atomic_store_relaxed(&this_code->specsigflags, (uint8_t) isspecsig); - // we might overwrite invokeptr here; that's ok, anybody who relied on the identity of invokeptr - // either assumes that specptr was null, doesn't care about specptr, - // or will wait until specsigflags has 0b10 set before reloading invoke - jl_atomic_store_release(&this_code->invoke, addr); - jl_atomic_store_release(&this_code->specsigflags, (uint8_t) (0b10 | isspecsig)); - } else { - //someone else beat us, don't commit any results - while (!(jl_atomic_load_acquire(&this_code->specsigflags) & 0b10)) { - jl_cpu_pause(); + } + codeinst = nullptr; + // barrier until all threads have finished calling addModule + if (--threads_in_compiler_phase == 0) { + // the last thread out will finish linking everything + // then release all of the other threads + // move the function pointers out from invokenames to the codeinst + + // batch compile job for all new functions + SmallVector NewDefs; + for (auto &this_code : linkready) { + auto it = invokenames.find(this_code); + assert(it != invokenames.end()); + jl_llvm_functions_t &decls = it->second; + assert(!decls.functionObject.empty()); + if (decls.functionObject != "jl_fptr_args" && + decls.functionObject != "jl_fptr_sparam" && + decls.functionObject != "jl_f_opaque_closure_call") + NewDefs.push_back(decls.functionObject); + if (!decls.specFunctionObject.empty()) + NewDefs.push_back(decls.specFunctionObject); + } + auto Addrs = jl_ExecutionEngine->findSymbols(NewDefs); + + size_t nextaddr = 0; + for (auto &this_code : linkready) { + auto it = invokenames.find(this_code); + assert(it != invokenames.end()); + jl_llvm_functions_t &decls = it->second; + jl_callptr_t addr; + bool isspecsig = false; + if (decls.functionObject == "jl_fptr_args") { + addr = jl_fptr_args_addr; + } + else if (decls.functionObject == "jl_fptr_sparam") { + addr = jl_fptr_sparam_addr; + } + else if (decls.functionObject == "jl_f_opaque_closure_call") { + addr = jl_f_opaque_closure_call_addr; + } + else { + assert(NewDefs[nextaddr] == decls.functionObject); + addr = (jl_callptr_t)Addrs[nextaddr++]; + assert(addr); + isspecsig = true; + } + if (!decls.specFunctionObject.empty()) { + void *prev_specptr = nullptr; + assert(NewDefs[nextaddr] == decls.specFunctionObject); + void *spec = (void*)Addrs[nextaddr++]; + assert(spec); + if (jl_atomic_cmpswap_acqrel(&this_code->specptr.fptr, &prev_specptr, spec)) { + // only set specsig and invoke if we were the first to set specptr + jl_atomic_store_relaxed(&this_code->specsigflags, (uint8_t) isspecsig); + // we might overwrite invokeptr here; that's ok, anybody who relied on the identity of invokeptr + // either assumes that specptr was null, doesn't care about specptr, + // or will wait until specsigflags has 0b10 set before reloading invoke + jl_atomic_store_release(&this_code->invoke, addr); + jl_atomic_store_release(&this_code->specsigflags, (uint8_t) (0b10 | isspecsig)); + } + else { + //someone else beat us, don't commit any results + while (!(jl_atomic_load_acquire(&this_code->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + addr = jl_atomic_load_relaxed(&this_code->invoke); } - addr = jl_atomic_load_relaxed(&this_code->invoke); } - } else { - jl_callptr_t prev_invoke = NULL; - // Allow replacing addr if it is either NULL or our special waiting placeholder. - if (!jl_atomic_cmpswap_acqrel(&this_code->invoke, &prev_invoke, addr)) { - if (prev_invoke == jl_fptr_wait_for_compiled_addr && !jl_atomic_cmpswap_acqrel(&this_code->invoke, &prev_invoke, addr)) { - addr = prev_invoke; - //TODO do we want to potentially promote invoke anyways? (e.g. invoke is jl_interpret_call or some other - //known lesser function) + else { + jl_callptr_t prev_invoke = nullptr; + // Allow replacing addr if it is either nullptr or our special waiting placeholder. + if (!jl_atomic_cmpswap_acqrel(&this_code->invoke, &prev_invoke, addr)) { + if (prev_invoke == jl_fptr_wait_for_compiled_addr && !jl_atomic_cmpswap_acqrel(&this_code->invoke, &prev_invoke, addr)) { + addr = prev_invoke; + //TODO do we want to potentially promote invoke anyways? (e.g. invoke is jl_interpret_call or some other + //known lesser function) + } } } + invokenames.erase(it); + complete_graph.erase(this_code); } - if (this_code == codeinst) - fptr = addr; - i++; + linkready.clear(); + engine_wait.notify_all(); + } + else while (threads_in_compiler_phase) { + lock.wait(engine_wait); } - if (i > jl_timing_print_limit) - jl_timing_printf(JL_TIMING_DEFAULT_BLOCK, "... <%d methods truncated>", i - 10); +} - uint64_t end_time = 0; - if (timed) - end_time = jl_hrtime(); - - // If logging of the compilation stream is enabled, - // then dump the method-instance specialization type to the stream - jl_method_instance_t *mi = codeinst->def; - if (jl_is_method(mi->def.method)) { - auto stream = *jl_ExecutionEngine->get_dump_compiles_stream(); - if (stream) { - ios_printf(stream, "%" PRIu64 "\t\"", end_time - start_time); - jl_static_show((JL_STREAM*)stream, mi->specTypes); - ios_printf(stream, "\"\n"); +static void jl_emit_codeinst_to_jit( + jl_code_instance_t *codeinst, + jl_code_info_t *src) +{ + { // lock scope + jl_unique_gcsafe_lock lock(engine_lock); + if (invokenames.count(codeinst) || jl_is_compiled_codeinst(codeinst)) + return; + } + JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); + // emit the code in LLVM IR form to the new context + jl_codegen_params_t params(std::make_unique(), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context + params.cache = true; + params.imaging_mode = imaging_default(); + params.debug_level = jl_options.debug_level; + orc::ThreadSafeModule result_m = + jl_create_ts_module(name_from_method_instance(codeinst->def), params.tsctx, params.DL, params.TargetTriple); + jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, src, params); // contains safepoints + if (!result_m) + return; + { // drop lock before acquiring engine_lock + auto release = std::move(params.tsctx_lock); + } + jl_unique_gcsafe_lock lock(engine_lock); + if (invokenames.count(codeinst) || jl_is_compiled_codeinst(codeinst)) + return; // destroy everything + invokenames[codeinst] = std::move(decls); + complete_emit(codeinst); + params.tsctx_lock = params.tsctx.getLock(); // re-acquire lock + int waiting = jl_analyze_workqueue(codeinst, params); + if (waiting) { + auto release = std::move(params.tsctx_lock); // unlock again before moving from it + incompletemodules.insert(std::pair(codeinst, std::tuple(std::move(params), waiting))); + } + else { + finish_params(result_m.getModuleUnlocked(), params); + } + emittedmodules[codeinst] = std::move(result_m); +} + +static void recursive_compile_graph( + jl_code_instance_t *codeinst, + jl_code_info_t *src) +{ + jl_emit_codeinst_to_jit(codeinst, src); + DenseSet Seen; + SmallVector workqueue; + workqueue.push_back(codeinst); + // if any edges were incomplete, try to complete them now + while (!workqueue.empty()) { + auto this_code = workqueue.pop_back_val(); + if (Seen.insert(this_code).second) { + if (this_code != codeinst) + jl_emit_codeinst_to_jit(this_code, nullptr); // contains safepoints + jl_unique_gcsafe_lock lock(engine_lock); + auto edges = complete_graph.find(this_code); + if (edges != complete_graph.end()) { + workqueue.append(edges->second); + } } } - return fptr; } +// this generates llvm code for the lambda info +// and adds the result to the jitlayers +// (and the shadow module), +// and generates code for it +static jl_callptr_t _jl_compile_codeinst( + jl_code_instance_t *codeinst, + jl_code_info_t *src) +{ + recursive_compile_graph(codeinst, src); + jl_compile_codeinst_now(codeinst); + return jl_atomic_load_acquire(&codeinst->invoke); +} + + const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); // compile a C-callable alias @@ -415,42 +727,40 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * orc::ThreadSafeModule backing; if (into == NULL) { if (!pparams) { - ctx = jl_ExecutionEngine->acquireContext(); + ctx = jl_ExecutionEngine->makeContext(); } backing = jl_create_ts_module("cextern", pparams ? pparams->tsctx : ctx, pparams ? pparams->DL : jl_ExecutionEngine->getDataLayout(), pparams ? pparams->TargetTriple : jl_ExecutionEngine->getTargetTriple()); into = &backing; } - auto target_info = into->withModuleDo([&](Module &M) { - return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple())); - }); - jl_codegen_params_t params(into->getContext(), std::move(target_info.first), std::move(target_info.second)); - params.imaging_mode = imaging_default(); - params.debug_level = jl_options.debug_level; - if (pparams == NULL) - pparams = ¶ms; - assert(pparams->tsctx.getContext() == into->getContext().getContext()); - const char *name = jl_generate_ccallable(wrap(into), sysimg, declrt, sigt, *pparams); bool success = true; - if (!sysimg) { - JL_LOCK(&jl_ExecutionEngine->jitlock); - if (jl_ExecutionEngine->getGlobalValueAddress(name)) { - success = false; - } - if (success && p == NULL) { - jl_jit_globals(params.global_targets); - assert(params.workqueue.empty()); - if (params._shared_module) { - jl_ExecutionEngine->optimizeDLSyms(*params._shared_module); - jl_ExecutionEngine->addModule(orc::ThreadSafeModule(std::move(params._shared_module), params.tsctx)); + { + auto Lock = into->getContext().getLock(); + Module *M = into->getModuleUnlocked(); + jl_codegen_params_t params(into->getContext(), M->getDataLayout(), Triple(M->getTargetTriple())); + params.imaging_mode = imaging_default(); + params.debug_level = jl_options.debug_level; + if (pparams == NULL) + pparams = ¶ms; + assert(pparams->tsctx.getContext() == into->getContext().getContext()); + const char *name = jl_generate_ccallable(wrap(into), sysimg, declrt, sigt, *pparams); + if (!sysimg) { + jl_unique_gcsafe_lock lock(extern_c_lock); + if (jl_ExecutionEngine->getGlobalValueAddress(name)) { + success = false; + } + if (success && p == NULL) { + jl_jit_globals(params.global_targets); + assert(params.workqueue.empty()); + if (params._shared_module) { + jl_ExecutionEngine->optimizeDLSyms(*params._shared_module); // safepoint + jl_ExecutionEngine->addModule(orc::ThreadSafeModule(std::move(params._shared_module), params.tsctx)); + } + } + if (success && llvmmod == NULL) { + jl_ExecutionEngine->optimizeDLSyms(*M); // safepoint + jl_ExecutionEngine->addModule(std::move(*into)); } } - if (success && llvmmod == NULL) { - into->withModuleDo([&](Module &M) { - jl_ExecutionEngine->optimizeDLSyms(M); - }); - jl_ExecutionEngine->addModule(std::move(*into)); - } - JL_UNLOCK(&jl_ExecutionEngine->jitlock); // Might GC } if (timed) { if (measure_compile_time_enabled) { @@ -459,9 +769,6 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * } ct->reentrant_timing &= ~1ull; } - if (ctx.getContext()) { - jl_ExecutionEngine->releaseContext(std::move(ctx)); - } return success; } @@ -512,18 +819,13 @@ extern "C" JL_DLLEXPORT_CODEGEN int jl_compile_codeinst_impl(jl_code_instance_t *ci) { int newly_compiled = 0; - if (jl_atomic_load_relaxed(&ci->invoke) != NULL) { - return newly_compiled; - } - JL_LOCK(&jl_ExecutionEngine->jitlock); if (jl_atomic_load_relaxed(&ci->invoke) == NULL) { ++SpecFPtrCount; uint64_t start = jl_typeinf_timing_begin(); - _jl_compile_codeinst(ci, NULL, *jl_ExecutionEngine->getContext()); + _jl_compile_codeinst(ci, NULL); jl_typeinf_timing_end(start, 0); newly_compiled = 1; } - JL_UNLOCK(&jl_ExecutionEngine->jitlock); // Might GC return newly_compiled; } @@ -541,38 +843,39 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) compiler_start_time = jl_hrtime(); - JL_LOCK(&jl_ExecutionEngine->jitlock); - if (jl_atomic_load_relaxed(&unspec->invoke) == NULL) { - jl_code_info_t *src = NULL; - JL_GC_PUSH1(&src); - jl_method_t *def = unspec->def->def.method; - if (jl_is_method(def)) { - src = (jl_code_info_t*)def->source; - if (src && (jl_value_t*)src != jl_nothing) - src = jl_uncompress_ir(def, NULL, (jl_value_t*)src); - } - else { - jl_method_instance_t *mi = unspec->def; - jl_code_instance_t *uninferred = jl_cached_uninferred( - jl_atomic_load_relaxed(&mi->cache), 1); - assert(uninferred); - src = (jl_code_info_t*)jl_atomic_load_relaxed(&uninferred->inferred); - assert(src); - } - if (src) { + jl_code_info_t *src = NULL; + JL_GC_PUSH1(&src); + jl_method_t *def = unspec->def->def.method; + if (jl_is_method(def)) { + src = (jl_code_info_t*)def->source; + if (src && (jl_value_t*)src != jl_nothing) + src = jl_uncompress_ir(def, NULL, (jl_value_t*)src); + } + else { + jl_method_instance_t *mi = unspec->def; + jl_code_instance_t *uninferred = jl_cached_uninferred( + jl_atomic_load_relaxed(&mi->cache), 1); + assert(uninferred); + src = (jl_code_info_t*)jl_atomic_load_relaxed(&uninferred->inferred); + assert(src); + } + if (src) { + // TODO: first prepare recursive_compile_graph(unspec, src) before taking this lock to avoid recursion? + JL_LOCK(&jitlock); // TODO: use a better lock + if (jl_atomic_load_relaxed(&unspec->invoke) == NULL) { assert(jl_is_code_info(src)); ++UnspecFPtrCount; jl_debuginfo_t *debuginfo = src->debuginfo; jl_atomic_store_release(&unspec->debuginfo, debuginfo); // n.b. this assumes the field was previously NULL, which is not entirely true jl_gc_wb(unspec, debuginfo); - _jl_compile_codeinst(unspec, src, *jl_ExecutionEngine->getContext()); + _jl_compile_codeinst(unspec, src); } - jl_callptr_t null = nullptr; - // if we hit a codegen bug (or ran into a broken generated function or llvmcall), fall back to the interpreter as a last resort - jl_atomic_cmpswap(&unspec->invoke, &null, jl_fptr_interpret_call_addr); - JL_GC_POP(); + JL_UNLOCK(&jitlock); // Might GC } - JL_UNLOCK(&jl_ExecutionEngine->jitlock); // Might GC + JL_GC_POP(); + jl_callptr_t null = nullptr; + // if we hit a codegen bug (or ran into a broken generated function or llvmcall), fall back to the interpreter as a last resort + jl_atomic_cmpswap(&unspec->invoke, &null, jl_fptr_interpret_call_addr); if (timed) { if (measure_compile_time_enabled) { auto end = jl_hrtime(); @@ -634,8 +937,8 @@ static auto countBasicBlocks(const Function &F) JL_NOTSAFEPOINT static constexpr size_t N_optlevels = 4; -static Expected selectOptLevel(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) { - TSM.withModuleDo([](Module &M) { +static orc::ThreadSafeModule selectOptLevel(orc::ThreadSafeModule TSM) JL_NOTSAFEPOINT { + TSM.withModuleDo([](Module &M) JL_NOTSAFEPOINT { size_t opt_level = std::max(static_cast(jl_options.opt_level), 0); do { if (jl_generating_output()) { @@ -661,7 +964,10 @@ static Expected selectOptLevel(orc::ThreadSafeModule TSM, opt_level = std::min(opt_level, N_optlevels - 1); M.addModuleFlag(Module::Warning, "julia.optlevel", opt_level); }); - return std::move(TSM); + return TSM; +} +static orc::ThreadSafeModule selectOptLevel(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { + return selectOptLevel(std::move(TSM)); } void jl_register_jit_object(const object::ObjectFile &debugObj, @@ -699,8 +1005,8 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { { std::lock_guard lock(PluginMutex); assert(PendingObjs.count(&MR) == 0); - PendingObjs[&MR] = std::unique_ptr( - new JITObjectInfo{std::move(NewBuffer), std::move(NewObj), {}}); + PendingObjs[&MR] = std::unique_ptr(new JITObjectInfo{ + std::move(NewBuffer), std::move(NewObj), {}}); } } @@ -870,7 +1176,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { // TODO: Port our memory management optimisations to JITLink instead of using the // default InProcessMemoryManager. -std::unique_ptr createJITLinkMemoryManager() { +std::unique_ptr createJITLinkMemoryManager() JL_NOTSAFEPOINT { #if JL_LLVM_VERSION < 160000 return cantFail(orc::MapperJITLinkMemoryManager::CreateWithMapper()); #else @@ -900,7 +1206,7 @@ class JLEHFrameRegistrar final : public jitlink::EHFrameRegistrar { } }; -RTDyldMemoryManager* createRTDyldMemoryManager(void); +RTDyldMemoryManager *createRTDyldMemoryManager(void) JL_NOTSAFEPOINT; // A simple forwarding class, since OrcJIT v2 needs a unique_ptr, while we have a shared_ptr class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { @@ -909,7 +1215,10 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { public: ForwardingMemoryManager(std::shared_ptr MemMgr) : MemMgr(MemMgr) {} - virtual ~ForwardingMemoryManager() = default; + ForwardingMemoryManager(ForwardingMemoryManager &) = delete; + virtual ~ForwardingMemoryManager() { + assert(!MemMgr); + } virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override { @@ -947,7 +1256,11 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { return MemMgr->deregisterEHFrames(); } virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { - return MemMgr->finalizeMemory(ErrMsg); + bool b = false; + if (MemMgr.use_count() == 2) + b = MemMgr->finalizeMemory(ErrMsg); + MemMgr.reset(); + return b; } virtual void notifyObjectLoaded(RuntimeDyld &RTDyld, const object::ObjectFile &Obj) override { @@ -955,10 +1268,10 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { } }; - -void registerRTDyldJITObject(const object::ObjectFile &Object, - const RuntimeDyld::LoadedObjectInfo &L, - const std::shared_ptr &MemMgr) +#ifndef JL_USE_JITLINK +static void registerRTDyldJITObject(orc::MaterializationResponsibility &MR, + const object::ObjectFile &Object, + const RuntimeDyld::LoadedObjectInfo &L) { StringMap loadedSections; for (const object::SectionRef &lSection : Object.sections()) { @@ -980,6 +1293,8 @@ void registerRTDyldJITObject(const object::ObjectFile &Object, auto DebugObject = L.getObjectForDebug(Object); // ELF requires us to make a copy to mutate the header with the section load addresses. On other platforms this is a no-op. jl_register_jit_object(DebugObject.getBinary() ? *DebugObject.getBinary() : Object, getLoadAddress); } +#endif + namespace { static std::unique_ptr createTargetMachine() JL_NOTSAFEPOINT { TargetOptions options = TargetOptions(); @@ -1078,9 +1393,6 @@ namespace { fixupTM(*TM); return std::unique_ptr(TM); } -} // namespace - -namespace { typedef NewPM PassManager; @@ -1131,14 +1443,14 @@ namespace { }; template - struct OptimizerT { - OptimizerT(TargetMachine &TM, SmallVector, 0> &printers, std::mutex &llvm_printing_mutex) JL_NOTSAFEPOINT { + struct sizedOptimizerT { + sizedOptimizerT(TargetMachine &TM, SmallVector, 0> &printers, std::mutex &llvm_printing_mutex) JL_NOTSAFEPOINT { for (size_t i = 0; i < N; i++) { PMs[i] = std::make_unique>>(PMCreator(TM, i, printers, llvm_printing_mutex)); } } - OptimizerResultT operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { + orc::ThreadSafeModule operator()(orc::ThreadSafeModule TSM) JL_NOTSAFEPOINT { TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { auto PoolIdx = cast(cast(M.getModuleFlag("julia.optlevel"))->getValue())->getZExtValue(); assert(PoolIdx < N && "Invalid optimization pool index"); @@ -1243,12 +1555,23 @@ namespace { llvm_unreachable("optlevel is between 0 and 3!"); } }); - return Expected{std::move(TSM)}; + return TSM; } private: std::array>>, N> PMs; }; + // shim for converting a unique_ptr to a TransformFunction to a TransformFunction + template + struct IRTransformRef { + IRTransformRef(T &transform) : transform(transform) {} + OptimizerResultT operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { + return transform(std::move(TSM), R); + } + private: + T &transform; + }; + template struct CompilerT : orc::IRCompileLayer::IRCompiler { @@ -1264,7 +1587,8 @@ namespace { size_t PoolIdx; if (auto opt_level = M.getModuleFlag("julia.optlevel")) { PoolIdx = cast(cast(opt_level)->getValue())->getZExtValue(); - } else { + } + else { PoolIdx = jl_options.opt_level; } assert(PoolIdx < N && "Invalid optimization level for compiler!"); @@ -1273,74 +1597,89 @@ namespace { std::array>>, N> TMs; }; +} - struct JITPointersT { - - JITPointersT(SharedBytesT &SharedBytes, std::mutex &Lock) JL_NOTSAFEPOINT - : SharedBytes(SharedBytes), Lock(Lock) {} +struct JuliaOJIT::OptimizerT { + OptimizerT(TargetMachine &TM, SmallVector, 0> &printers, std::mutex &llvm_printing_mutex) + : opt(TM, printers, llvm_printing_mutex) {} + orc::ThreadSafeModule operator()(orc::ThreadSafeModule TSM) JL_NOTSAFEPOINT { + return opt(std::move(TSM)); + } + OptimizerResultT operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { + return opt(std::move(TSM)); + } +private: + struct sizedOptimizerT opt; +}; - Expected operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { - TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { - std::lock_guard locked(Lock); - for (auto &GV : make_early_inc_range(M.globals())) { - if (auto *Shared = getSharedBytes(GV)) { - ++InternedGlobals; - GV.replaceAllUsesWith(Shared); - GV.eraseFromParent(); - } +struct JuliaOJIT::JITPointersT { + JITPointersT(SharedBytesT &SharedBytes, std::mutex &Lock) JL_NOTSAFEPOINT + : SharedBytes(SharedBytes), Lock(Lock) {} + + orc::ThreadSafeModule operator()(orc::ThreadSafeModule TSM) JL_NOTSAFEPOINT { + TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { + std::lock_guard locked(Lock); + for (auto &GV : make_early_inc_range(M.globals())) { + if (auto *Shared = getSharedBytes(GV)) { + ++InternedGlobals; + GV.replaceAllUsesWith(Shared); + GV.eraseFromParent(); } + } - // Windows needs some inline asm to help - // build unwind tables, if they have any functions to decorate - if (!M.functions().empty()) - jl_decorate_module(M); - }); - return std::move(TSM); - } + // Windows needs some inline asm to help + // build unwind tables, if they have any functions to decorate + if (!M.functions().empty()) + jl_decorate_module(M); + }); + return TSM; + } + Expected operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { + return operator()(std::move(TSM)); + } - private: - // optimize memory by turning long strings into memoized copies, instead of - // making a copy per object file of output. - // we memoize them using a StringSet with a custom-alignment allocator - // to ensure they are properly aligned - Constant *getSharedBytes(GlobalVariable &GV) JL_NOTSAFEPOINT { - // We could probably technically get away with - // interning even external linkage globals, - // as long as they have global unnamedaddr, - // but currently we shouldn't be emitting those - // except in imaging mode, and we don't want to - // do this optimization there. - if (GV.hasExternalLinkage() || !GV.hasGlobalUnnamedAddr()) { - return nullptr; - } - if (!GV.hasInitializer()) { - return nullptr; - } - if (!GV.isConstant()) { - return nullptr; - } - auto CDS = dyn_cast(GV.getInitializer()); - if (!CDS) { - return nullptr; - } - StringRef Data = CDS->getRawDataValues(); - if (Data.size() < 16) { - // Cutoff, since we don't want to intern small strings - return nullptr; - } - Align Required = GV.getAlign().valueOrOne(); - Align Preferred = MaxAlignedAlloc::alignment(Data.size()); - if (Required > Preferred) - return nullptr; - StringRef Interned = SharedBytes.insert(Data).first->getKey(); - assert(llvm::isAddrAligned(Preferred, Interned.data())); - return literal_static_pointer_val(Interned.data(), GV.getType()); +private: + // optimize memory by turning long strings into memoized copies, instead of + // making a copy per object file of output. + // we memoize them using a StringSet with a custom-alignment allocator + // to ensure they are properly aligned + Constant *getSharedBytes(GlobalVariable &GV) JL_NOTSAFEPOINT { + // We could probably technically get away with + // interning even external linkage globals, + // as long as they have global unnamedaddr, + // but currently we shouldn't be emitting those + // except in imaging mode, and we don't want to + // do this optimization there. + if (GV.hasExternalLinkage() || !GV.hasGlobalUnnamedAddr()) { + return nullptr; } + if (!GV.hasInitializer()) { + return nullptr; + } + if (!GV.isConstant()) { + return nullptr; + } + auto CDS = dyn_cast(GV.getInitializer()); + if (!CDS) { + return nullptr; + } + StringRef Data = CDS->getRawDataValues(); + if (Data.size() < 16) { + // Cutoff, since we don't want to intern small strings + return nullptr; + } + Align Required = GV.getAlign().valueOrOne(); + Align Preferred = MaxAlignedAlloc::alignment(Data.size()); + if (Required > Preferred) + return nullptr; + StringRef Interned = SharedBytes.insert(Data).first->getKey(); + assert(llvm::isAddrAligned(Preferred, Interned.data())); + return literal_static_pointer_val(Interned.data(), GV.getType()); + } - SharedBytesT &SharedBytes; - std::mutex &Lock; - }; -} + SharedBytesT &SharedBytes; + std::mutex &Lock; +}; struct JuliaOJIT::DLSymOptimizer { @@ -1362,20 +1701,24 @@ struct JuliaOJIT::DLSymOptimizer { #undef INIT_RUNTIME_LIBRARY } + ~DLSymOptimizer() JL_NOTSAFEPOINT = default; - void *lookup_symbol(void *libhandle, const char *fname) { + void *lookup_symbol(void *libhandle, const char *fname) JL_NOTSAFEPOINT { void *addr; jl_dlsym(libhandle, fname, &addr, 0); return addr; } - void *lookup(const char *libname, const char *fname) { + void *lookup(const char *libname, const char *fname) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER { StringRef lib(libname); StringRef f(fname); std::lock_guard lock(symbols_mutex); auto uit = user_symbols.find(lib); if (uit == user_symbols.end()) { + jl_task_t *ct = jl_current_task; + int8_t gc_state = jl_gc_unsafe_enter(ct->ptls); void *handle = jl_get_library_(libname, 0); + jl_gc_unsafe_leave(ct->ptls, gc_state); if (!handle) return nullptr; uit = user_symbols.insert(std::make_pair(lib, std::make_pair(handle, StringMap()))).first; @@ -1390,7 +1733,7 @@ struct JuliaOJIT::DLSymOptimizer { return handle; } - void *lookup(uintptr_t libidx, const char *fname) { + void *lookup(uintptr_t libidx, const char *fname) JL_NOTSAFEPOINT { std::lock_guard lock(symbols_mutex); runtime_symbols.resize(std::max(runtime_symbols.size(), libidx + 1)); auto it = runtime_symbols[libidx].second.find(fname); @@ -1402,7 +1745,7 @@ struct JuliaOJIT::DLSymOptimizer { return handle; } - void operator()(Module &M) { + void operator()(Module &M) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER { for (auto &GV : M.globals()) { auto Name = GV.getName(); if (Name.starts_with("jlplt") && Name.ends_with("got")) { @@ -1518,7 +1861,7 @@ struct JuliaOJIT::DLSymOptimizer { bool named; }; -void optimizeDLSyms(Module &M) { +void optimizeDLSyms(Module &M) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER { JuliaOJIT::DLSymOptimizer(true)(M); } @@ -1552,10 +1895,6 @@ llvm::DataLayout jl_create_datalayout(TargetMachine &TM) { return jl_data_layout; } -#ifdef _COMPILER_ASAN_ENABLED_ -int64_t ___asan_globals_registered; -#endif - JuliaOJIT::JuliaOJIT() : TM(createTargetMachine()), DL(jl_create_datalayout(*TM)), @@ -1564,34 +1903,27 @@ JuliaOJIT::JuliaOJIT() JD(ES.createBareJITDylib("JuliaOJIT")), ExternalJD(ES.createBareJITDylib("JuliaExternal")), DLSymOpt(std::make_unique(false)), - ContextPool([](){ - auto ctx = std::make_unique(); - #if JL_LLVM_VERSION < 170000 - SetOpaquePointer(*ctx); - #endif - return orc::ThreadSafeContext(std::move(ctx)); - }), #ifdef JL_USE_JITLINK MemMgr(createJITLinkMemoryManager()), ObjectLayer(ES, *MemMgr), - CompileLayer(ES, ObjectLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), #else MemMgr(createRTDyldMemoryManager()), - ObjectLayer( + UnlockedObjectLayer( ES, [this]() { std::unique_ptr result(new ForwardingMemoryManager(MemMgr)); return result; } ), - LockLayer(ObjectLayer), - CompileLayer(ES, LockLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), + ObjectLayer(UnlockedObjectLayer), #endif - JITPointersLayer(ES, CompileLayer, orc::IRTransformLayer::TransformFunction(JITPointersT(SharedBytes, RLST_mutex))), - OptimizeLayer(ES, JITPointersLayer, orc::IRTransformLayer::TransformFunction(OptimizerT(*TM, PrintLLVMTimers, llvm_printing_mutex))), - OptSelLayer(ES, OptimizeLayer, orc::IRTransformLayer::TransformFunction(selectOptLevel)) + CompileLayer(ES, ObjectLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), + JITPointers(std::make_unique(SharedBytes, RLST_mutex)), + JITPointersLayer(ES, CompileLayer, IRTransformRef(*JITPointers)), + Optimizers(std::make_unique(*TM, PrintLLVMTimers, llvm_printing_mutex)), + OptimizeLayer(ES, JITPointersLayer, IRTransformRef(*Optimizers)), + OptSelLayer(ES, OptimizeLayer, static_cast(selectOptLevel)) { - JL_MUTEX_INIT(&this->jitlock, "JuliaOJIT"); #ifdef JL_USE_JITLINK # if defined(LLVM_SHLIB) // When dynamically linking against LLVM, use our custom EH frame registration code @@ -1606,12 +1938,7 @@ JuliaOJIT::JuliaOJIT() ObjectLayer.addPlugin(std::make_unique()); ObjectLayer.addPlugin(std::make_unique(jit_bytes_size)); #else - ObjectLayer.setNotifyLoaded( - [this](orc::MaterializationResponsibility &MR, - const object::ObjectFile &Object, - const RuntimeDyld::LoadedObjectInfo &LO) { - registerRTDyldJITObject(Object, LO, MemMgr); - }); + UnlockedObjectLayer.setNotifyLoaded(registerRTDyldJITObject); #endif std::string ErrorStr; @@ -1741,19 +2068,34 @@ JuliaOJIT::JuliaOJIT() #endif cantFail(GlobalJD.define(orc::absoluteSymbols(msan_crt))); #endif +#if JL_LLVM_VERSION < 190000 #ifdef _COMPILER_ASAN_ENABLED_ + // this is a hack to work around a bad assertion: + // /workspace/srcdir/llvm-project/llvm/lib/ExecutionEngine/Orc/Core.cpp:3028: llvm::Error llvm::orc::ExecutionSession::OL_notifyResolved(llvm::orc::MaterializationResponsibility&, const SymbolMap&): Assertion `(KV.second.getFlags() & ~JITSymbolFlags::Common) == (I->second & ~JITSymbolFlags::Common) && "Resolving symbol with incorrect flags"' failed. + // hopefully fixed upstream by e7698a13e319a9919af04d3d693a6f6ea7168a44 + static int64_t jl___asan_globals_registered; orc::SymbolMap asan_crt; #if JL_LLVM_VERSION >= 170000 - asan_crt[mangle("___asan_globals_registered")] = {ExecutorAddr::fromPtr(&___asan_globals_registered), JITSymbolFlags::Exported}; + asan_crt[mangle("___asan_globals_registered")] = {ExecutorAddr::fromPtr(&jl___asan_globals_registered), JITSymbolFlags::Common | JITSymbolFlags::Exported}; #else - asan_crt[mangle("___asan_globals_registered")] = JITEvaluatedSymbol::fromPointer(&___asan_globals_registered, JITSymbolFlags::Exported); + asan_crt[mangle("___asan_globals_registered")] = JITEvaluatedSymbol::fromPointer(&jl___asan_globals_registered, JITSymbolFlags::Common | JITSymbolFlags::Exported); #endif cantFail(JD.define(orc::absoluteSymbols(asan_crt))); #endif +#endif } JuliaOJIT::~JuliaOJIT() = default; +ThreadSafeContext JuliaOJIT::makeContext() +{ + auto ctx = std::make_unique(); + #if JL_LLVM_VERSION < 170000 + SetOpaquePointer(*ctx); + #endif + return orc::ThreadSafeContext(std::move(ctx)); +} + orc::SymbolStringPtr JuliaOJIT::mangle(StringRef Name) { std::string MangleName = getMangledName(Name); @@ -1773,40 +2115,32 @@ void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) { JL_TIMING(LLVM_JIT, JIT_Total); ++ModulesAdded; -#ifndef JL_USE_JITLINK - orc::SymbolLookupSet NewExports; - TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { - for (auto &F : M.global_values()) { - if (!F.isDeclaration() && F.getLinkage() == GlobalValue::ExternalLinkage) { - auto Name = ES.intern(getMangledName(F.getName())); - NewExports.add(std::move(Name)); - } - } - assert(!verifyLLVMIR(M)); - }); -#endif - - auto Err = OptSelLayer.add(JD, std::move(TSM)); + TSM = selectOptLevel(std::move(TSM)); + TSM = (*Optimizers)(std::move(TSM)); + TSM = (*JITPointers)(std::move(TSM)); + auto Lock = TSM.getContext().getLock(); + Module &M = *TSM.getModuleUnlocked(); + // Treat this as if one of the passes might contain a safepoint + // even though that shouldn't be the case and might be unwise + Expected> Obj = CompileLayer.getCompiler()(M); + if (!Obj) { + ES.reportError(Obj.takeError()); + errs() << "Failed to add module to JIT!\n"; + errs() << "Dumping failing module\n" << M << "\n"; + return; + } + { auto release = std::move(Lock); } + auto Err = JuliaOJIT::addObjectFile(JD, std::move(*Obj)); if (Err) { ES.reportError(std::move(Err)); - errs() << "Failed to add module to JIT!\n"; + errs() << "Failed to add objectfile to JIT!\n"; abort(); } -#ifndef JL_USE_JITLINK - // force eager compilation (for now), due to memory management specifics - // (can't handle compilation recursion) - auto Lookups = ES.lookup({{&JD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly}}, NewExports); - if (!Lookups) { - ES.reportError(Lookups.takeError()); - errs() << "Failed to lookup symbols in module!\n"; - } -#endif } Error JuliaOJIT::addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, bool ShouldOptimize) { - if (auto Err = TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT -> Error - { + if (auto Err = TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT -> Error { if (M.getDataLayout().isDefault()) M.setDataLayout(DL); if (M.getDataLayout() != DL) @@ -1815,24 +2149,29 @@ Error JuliaOJIT::addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, M.getDataLayout().getStringRepresentation() + " (module) vs " + DL.getStringRepresentation() + " (jit)", inconvertibleErrorCode()); - + // OrcJIT requires that all modules / files have unique names: + M.setModuleIdentifier((M.getModuleIdentifier() + Twine("-") + Twine(jl_atomic_fetch_add_relaxed(&jitcounter, 1))).str()); return Error::success(); - })) + })) return Err; + //if (ShouldOptimize) + // return OptimizeLayer.add(JD, std::move(TSM)); return CompileLayer.add(JD.getDefaultResourceTracker(), std::move(TSM)); } Error JuliaOJIT::addObjectFile(orc::JITDylib &JD, std::unique_ptr Obj) { assert(Obj && "Can not add null object"); -#ifdef JL_USE_JITLINK + // OrcJIT requires that all modules / files have unique names: + // https://llvm.org/doxygen/namespacellvm_1_1orc.html#a1f5a1bc60c220cdccbab0f26b2a425e1 + // so we have to force a copy here + std::string Name = ("jitted-" + Twine(jl_atomic_fetch_add_relaxed(&jitcounter, 1))).str(); + Obj = Obj->getMemBufferCopy(Obj->getBuffer(), Name); return ObjectLayer.add(JD.getDefaultResourceTracker(), std::move(Obj)); -#else - return LockLayer.add(JD.getDefaultResourceTracker(), std::move(Obj)); -#endif } SmallVector JuliaOJIT::findSymbols(ArrayRef Names) { + // assert(MemMgr.use_count() == 1); (true single-threaded, but slightly race-y to assert it with concurrent threads) DenseMap Unmangled; orc::SymbolLookupSet Exports; for (StringRef Name : Names) { @@ -1978,6 +2317,7 @@ void JuliaOJIT::enableJITDebuggingSupport() addAbsoluteToMap(GDBFunctions,llvm_orc_registerJITLoaderGDBAllocAction); auto registerJITLoaderGDBWrapper = addAbsoluteToMap(GDBFunctions,llvm_orc_registerJITLoaderGDBWrapper); cantFail(JD.define(orc::absoluteSymbols(GDBFunctions))); + (void)registerJITLoaderGDBWrapper; if (TM->getTargetTriple().isOSBinFormatMachO()) ObjectLayer.addPlugin(cantFail(orc::GDBJITDebugInfoRegistrationPlugin::Create(ES, JD, TM->getTargetTriple()))); #ifndef _COMPILER_ASAN_ENABLED_ // TODO: Fix duplicated sections spam #51794 @@ -2013,12 +2353,12 @@ void JuliaOJIT::enableOProfileJITEventListener() void JuliaOJIT::enablePerfJITEventListener() { #if JL_LLVM_VERSION >= 180000 - orc::SymbolMap PerfFunctions; - auto StartAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfStart); - auto EndAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfEnd); - auto ImplAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfImpl); - cantFail(JD.define(orc::absoluteSymbols(PerfFunctions))); if (TM->getTargetTriple().isOSBinFormatELF()) { + orc::SymbolMap PerfFunctions; + auto StartAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfStart); + auto EndAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfEnd); + auto ImplAddr = addAbsoluteToMap(PerfFunctions,llvm_orc_registerJITLoaderPerfImpl); + cantFail(JD.define(orc::absoluteSymbols(PerfFunctions))); ObjectLayer.addPlugin(cantFail(DebugInfoPreservationPlugin::Create())); //ObjectLayer.addPlugin(cantFail(PerfSupportPlugin::Create( // ES.getExecutorProcessControl(), *JD, true, true))); @@ -2032,7 +2372,7 @@ void JuliaOJIT::enablePerfJITEventListener() void JuliaOJIT::RegisterJITEventListener(JITEventListener *L) { if (L) - ObjectLayer.registerJITEventListener(*L); + UnlockedObjectLayer.registerJITEventListener(*L); } void JuliaOJIT::enableJITDebuggingSupport() { @@ -2071,7 +2411,7 @@ std::string JuliaOJIT::getMangledName(const GlobalValue *GV) size_t JuliaOJIT::getTotalBytes() const { - auto bytes = jit_bytes_size.load(std::memory_order_relaxed); + auto bytes = jl_atomic_load_relaxed(&jit_bytes_size); #ifndef JL_USE_JITLINK size_t getRTDyldMemoryManagerTotalBytes(RTDyldMemoryManager *mm) JL_NOTSAFEPOINT; bytes += getRTDyldMemoryManagerTotalBytes(MemMgr.get()); @@ -2081,7 +2421,7 @@ size_t JuliaOJIT::getTotalBytes() const void JuliaOJIT::addBytes(size_t bytes) { - jit_bytes_size.fetch_add(bytes, std::memory_order_relaxed); + jl_atomic_fetch_add_relaxed(&jit_bytes_size, bytes); } void JuliaOJIT::printTimers() @@ -2326,74 +2666,6 @@ static void jl_decorate_module(Module &M) { #undef ASM_USES_ELF } -#ifndef JL_USE_JITLINK -// Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable -static int jl_add_to_ee( - orc::ThreadSafeModule &M, - const StringMap &NewExports, - DenseMap &Queued, - SmallVectorImpl &Stack) -{ - // First check if the TSM is empty (already compiled) - if (!M) - return 0; - // Next check and record if it is on the stack somewhere - { - auto &Id = Queued[&M]; - if (Id) - return Id; - Stack.push_back(&M); - Id = Stack.size(); - } - // Finally work out the SCC - int depth = Stack.size(); - int MergeUp = depth; - SmallVector Children; - M.withModuleDo([&](Module &m) JL_NOTSAFEPOINT { - for (auto &F : m.global_objects()) { - if (F.isDeclaration() && F.getLinkage() == GlobalValue::ExternalLinkage) { - auto Callee = NewExports.find(F.getName()); - if (Callee != NewExports.end()) { - auto *CM = Callee->second; - if (*CM && CM != &M) { - auto Down = Queued.find(CM); - if (Down != Queued.end()) - MergeUp = std::min(MergeUp, Down->second); - else - Children.push_back(CM); - } - } - } - } - }); - assert(MergeUp > 0); - for (auto *CM : Children) { - int Down = jl_add_to_ee(*CM, NewExports, Queued, Stack); - assert(Down <= (int)Stack.size()); - if (Down) - MergeUp = std::min(MergeUp, Down); - } - if (MergeUp < depth) - return MergeUp; - while (1) { - // Not in a cycle (or at the top of it) - // remove SCC state and merge every CM from the cycle into M - orc::ThreadSafeModule *CM = Stack.back(); - auto it = Queued.find(CM); - assert(it->second == (int)Stack.size()); - Queued.erase(it); - Stack.pop_back(); - if ((int)Stack.size() < depth) { - assert(&M == CM); - break; - } - jl_merge_module(M, std::move(*CM)); - } - jl_ExecutionEngine->addModule(std::move(M)); - return 0; -} -#endif - // helper function for adding a DLLImport (dlsym) address to the execution engine void add_named_global(StringRef name, void *addr) { diff --git a/src/jitlayers.h b/src/jitlayers.h index f4b9a6ea5395a..ba4ac3081795e 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -69,7 +69,6 @@ using namespace llvm; extern "C" jl_cgparams_t jl_default_cgparams; -extern arraylist_t new_invokes; DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeContext, LLVMOrcThreadSafeContextRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeModule, LLVMOrcThreadSafeModuleRef) @@ -154,11 +153,11 @@ struct jl_locked_stream { std::unique_lock lck; ios_t *&stream; - lock(std::mutex &mutex, ios_t *&stream) JL_NOTSAFEPOINT + lock(std::mutex &mutex, ios_t *&stream) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER : lck(mutex), stream(stream) {} lock(lock&) = delete; lock(lock&&) JL_NOTSAFEPOINT = default; - ~lock() JL_NOTSAFEPOINT = default; + ~lock() JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT = default; ios_t *&operator*() JL_NOTSAFEPOINT { return stream; @@ -177,8 +176,8 @@ struct jl_locked_stream { } }; - jl_locked_stream() JL_NOTSAFEPOINT = default; - ~jl_locked_stream() JL_NOTSAFEPOINT = default; + jl_locked_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER = default; + ~jl_locked_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE = default; lock operator*() JL_NOTSAFEPOINT { return lock(mutex, stream); @@ -210,12 +209,12 @@ struct jl_codegen_call_target_t { jl_returninfo_t::CallingConv cc; unsigned return_roots; llvm::Function *decl; + llvm::Function *oc; bool specsig; }; typedef SmallVector, 0> jl_workqueue_t; -// TODO DenseMap? -typedef std::map> jl_compiled_functions_t; + typedef std::list> CallFrames; struct jl_codegen_params_t { orc::ThreadSafeContext tsctx; @@ -229,7 +228,6 @@ struct jl_codegen_params_t { typedef StringMap SymMapGV; // outputs jl_workqueue_t workqueue; - jl_compiled_functions_t compiled_functions; std::map global_targets; std::map, GlobalVariable*> external_fns; std::map ditypes; @@ -292,13 +290,20 @@ enum CompilationPolicy { Extern = 1, }; -void jl_compile_workqueue( - jl_codegen_params_t ¶ms, - CompilationPolicy policy); - Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_tupletype_t *argt, jl_codegen_params_t ¶ms); +Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, Module *M, jl_codegen_params_t ¶ms) JL_NOTSAFEPOINT; +void emit_specsig_to_fptr1( + Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, + jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, + size_t nargs, + jl_codegen_params_t ¶ms, + Function *target, + size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +Function *get_or_emit_fptr1(StringRef Name, Module *M) JL_NOTSAFEPOINT; +void jl_init_function(Function *F, const Triple &TT) JL_NOTSAFEPOINT; + void add_named_global(StringRef name, void *addr) JL_NOTSAFEPOINT; static inline Constant *literal_static_pointer_val(const void *p, Type *T) JL_NOTSAFEPOINT @@ -371,6 +376,11 @@ using OptimizerResultT = Expected; using SharedBytesT = StringSet::MapEntryTy)>>; class JuliaOJIT { +private: + // any verification the user wants to do when adding an OwningResource to the pool + template + static void verifyResource(AnyT &resource) JL_NOTSAFEPOINT { } + static void verifyResource(orc::ThreadSafeContext &context) JL_NOTSAFEPOINT { assert(context.getContext()); } public: #ifdef JL_USE_JITLINK typedef orc::ObjectLinkingLayer ObjLayerT; @@ -385,13 +395,13 @@ class JuliaOJIT { std::unique_ptr O) override { JL_TIMING(LLVM_JIT, JIT_Link); #ifndef JL_USE_JITLINK - std::lock_guard lock(EmissionMutex); + std::lock_guard lock(EmissionMutex); #endif BaseLayer.emit(std::move(R), std::move(O)); } private: orc::ObjectLayer &BaseLayer; - std::mutex EmissionMutex; + std::recursive_mutex EmissionMutex; }; #endif typedef orc::IRCompileLayer CompileLayerT; @@ -420,11 +430,16 @@ class JuliaOJIT { : pool(pool), resource(std::move(resource)) {} OwningResource(const OwningResource &) = delete; OwningResource &operator=(const OwningResource &) = delete; - OwningResource(OwningResource &&) JL_NOTSAFEPOINT = default; + OwningResource(OwningResource &&other) JL_NOTSAFEPOINT + : pool(other.pool), resource(std::move(other.resource)) { + other.resource.reset(); + } OwningResource &operator=(OwningResource &&) JL_NOTSAFEPOINT = default; ~OwningResource() JL_NOTSAFEPOINT { // _LEAVE - if (resource) + if (resource) { + verifyResource(*resource); pool.release(std::move(*resource)); + } } ResourceT release() JL_NOTSAFEPOINT { ResourceT res(std::move(*resource)); @@ -510,7 +525,11 @@ class JuliaOJIT { std::unique_ptr mutex; }; + typedef ResourcePool> ContextPoolT; + struct DLSymOptimizer; + struct OptimizerT; + struct JITPointersT; #ifndef JL_USE_JITLINK void RegisterJITEventListener(JITEventListener *L) JL_NOTSAFEPOINT; @@ -528,7 +547,7 @@ class JuliaOJIT { orc::SymbolStringPtr mangle(StringRef Name) JL_NOTSAFEPOINT; void addGlobalMapping(StringRef Name, uint64_t Addr) JL_NOTSAFEPOINT; - void addModule(orc::ThreadSafeModule M) JL_NOTSAFEPOINT; + void addModule(orc::ThreadSafeModule M) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER; //Methods for the C API Error addExternalModule(orc::JITDylib &JD, orc::ThreadSafeModule TSM, @@ -552,15 +571,7 @@ class JuliaOJIT { uint64_t getGlobalValueAddress(StringRef Name) JL_NOTSAFEPOINT; uint64_t getFunctionAddress(StringRef Name) JL_NOTSAFEPOINT; StringRef getFunctionAtAddress(uint64_t Addr, jl_callptr_t invoke, jl_code_instance_t *codeinst) JL_NOTSAFEPOINT; - auto getContext() JL_NOTSAFEPOINT { - return *ContextPool; - } - orc::ThreadSafeContext acquireContext() { // JL_NOTSAFEPOINT_ENTER? - return ContextPool.acquire(); - } - void releaseContext(orc::ThreadSafeContext &&ctx) { // JL_NOTSAFEPOINT_LEAVE? - ContextPool.release(std::move(ctx)); - } + orc::ThreadSafeContext makeContext() JL_NOTSAFEPOINT; const DataLayout& getDataLayout() const JL_NOTSAFEPOINT; // TargetMachine pass-through methods @@ -576,22 +587,21 @@ class JuliaOJIT { void addBytes(size_t bytes) JL_NOTSAFEPOINT; void printTimers() JL_NOTSAFEPOINT; - jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT { + jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return dump_emitted_mi_name_stream; } - jl_locked_stream &get_dump_compiles_stream() JL_NOTSAFEPOINT { + jl_locked_stream &get_dump_compiles_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return dump_compiles_stream; } - jl_locked_stream &get_dump_llvm_opt_stream() JL_NOTSAFEPOINT { + jl_locked_stream &get_dump_llvm_opt_stream() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER { return dump_llvm_opt_stream; } std::string getMangledName(StringRef Name) JL_NOTSAFEPOINT; std::string getMangledName(const GlobalValue *GV) JL_NOTSAFEPOINT; - // Note that this is a safepoint due to jl_get_library_ and jl_dlsym calls - void optimizeDLSyms(Module &M); - - jl_mutex_t jitlock; + // Note that this is a potential safepoint due to jl_get_library_ and jl_dlsym calls + // but may be called from inside safe-regions due to jit compilation locks + void optimizeDLSyms(Module &M) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER; private: @@ -618,20 +628,20 @@ class JuliaOJIT { std::mutex llvm_printing_mutex{}; SmallVector, 0> PrintLLVMTimers; - ResourcePool> ContextPool; - - std::atomic jit_bytes_size{0}; -#ifndef JL_USE_JITLINK - const std::shared_ptr MemMgr; -#else + _Atomic(size_t) jit_bytes_size{0}; + _Atomic(size_t) jitcounter{0}; +#ifdef JL_USE_JITLINK const std::unique_ptr MemMgr; -#endif ObjLayerT ObjectLayer; -#ifndef JL_USE_JITLINK - LockLayerT LockLayer; +#else + const std::shared_ptr MemMgr; // shared_ptr protected by LockLayerT.EmissionMutex + ObjLayerT UnlockedObjectLayer; + LockLayerT ObjectLayer; #endif CompileLayerT CompileLayer; + std::unique_ptr JITPointers; JITPointersLayerT JITPointersLayer; + std::unique_ptr Optimizers; OptimizeLayerT OptimizeLayer; OptSelLayerT OptSelLayer; }; diff --git a/src/julia.h b/src/julia.h index 7bb5f31eda708..168ba0deff1ec 100644 --- a/src/julia.h +++ b/src/julia.h @@ -426,8 +426,8 @@ typedef struct _jl_opaque_closure_t { jl_value_t *captures; size_t world; jl_method_t *source; - jl_fptr_args_t invoke; - void *specptr; + jl_fptr_args_t invoke; // n.b. despite the similar name, this is not an invoke ABI (jl_call_t / julia.call2), but rather the fptr1 (jl_fptr_args_t / julia.call) ABI + void *specptr; // n.b. despite the similarity in field name, this is not arbitrary private data for jlcall, but rather the codegen ABI for specsig, and is mandatory if specsig is valid } jl_opaque_closure_t; // This type represents an executable operation @@ -475,7 +475,7 @@ typedef struct _jl_code_instance_t { // & 0b100 == From image _Atomic(uint8_t) precompile; // if set, this will be added to the output system image uint8_t relocatability; // nonzero if all roots are built into sysimg or tagged by module key - _Atomic(jl_callptr_t) invoke; // jlcall entry point + _Atomic(jl_callptr_t) invoke; // jlcall entry point usually, but if this codeinst belongs to an OC Method, then this is an jl_fptr_args_t fptr1 instead, unless it is not, because it is a special token object instead union _jl_generic_specptr_t { _Atomic(void*) fptr; _Atomic(jl_fptr_args_t) fptr1; @@ -2339,7 +2339,13 @@ JL_DLLEXPORT JL_CONST_FUNC jl_gcframe_t **(jl_get_pgcstack)(void) JL_GLOBALLY_RO extern JL_DLLIMPORT int jl_task_gcstack_offset; extern JL_DLLIMPORT int jl_task_ptls_offset; +#ifdef __cplusplus +} +#endif #include "julia_locks.h" // requires jl_task_t definition +#ifdef __cplusplus +extern "C" { +#endif // Return the exception currently being handled, or `jl_nothing`. // diff --git a/src/julia_atomics.h b/src/julia_atomics.h index c094afcc54cd5..d05f0fafab28f 100644 --- a/src/julia_atomics.h +++ b/src/julia_atomics.h @@ -103,12 +103,12 @@ enum jl_memory_order { // this wrong thus we include the correct definitions here (with implicit // conversion), instead of using the macro version template -T jl_atomic_load(std::atomic *ptr) +T jl_atomic_load(const std::atomic *ptr) { return std::atomic_load(ptr); } template -T jl_atomic_load_explicit(std::atomic *ptr, std::memory_order order) +T jl_atomic_load_explicit(const std::atomic *ptr, std::memory_order order) { return std::atomic_load_explicit(ptr, order); } diff --git a/src/julia_internal.h b/src/julia_internal.h index 82c91c6d073af..bb8169c6e5f9e 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1715,13 +1715,14 @@ JL_DLLEXPORT int jl_tupletype_length_compat(jl_value_t *v, size_t nargs) JL_NOTS JL_DLLEXPORT jl_value_t *jl_argtype_with_function(jl_value_t *f, jl_value_t *types0); JL_DLLEXPORT jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_value_t *types0); +JL_DLLEXPORT jl_value_t *jl_argtype_without_function(jl_value_t *ftypes); JL_DLLEXPORT unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *field_type); -void register_eh_frames(uint8_t *Addr, size_t Size); -void deregister_eh_frames(uint8_t *Addr, size_t Size); +void register_eh_frames(uint8_t *Addr, size_t Size) JL_NOTSAFEPOINT; +void deregister_eh_frames(uint8_t *Addr, size_t Size) JL_NOTSAFEPOINT; -STATIC_INLINE void *jl_get_frame_addr(void) +STATIC_INLINE void *jl_get_frame_addr(void) JL_NOTSAFEPOINT { #ifdef __GNUC__ return __builtin_frame_address(0); diff --git a/src/julia_locks.h b/src/julia_locks.h index 5774ddada60c6..4d1345177f965 100644 --- a/src/julia_locks.h +++ b/src/julia_locks.h @@ -103,6 +103,33 @@ JL_DLLEXPORT void jl_unlock_field(jl_mutex_t *v) JL_NOTSAFEPOINT; #ifdef __cplusplus } + +#include +#include +// simple C++ shim around a std::unique_lock + gc-safe + disabled finalizers region +// since we nearly always want that combination together +class jl_unique_gcsafe_lock { +public: + int8_t gc_state; + std::unique_lock native; + explicit jl_unique_gcsafe_lock(std::mutex &native) JL_NOTSAFEPOINT_ENTER + { + jl_task_t *ct = jl_current_task; + gc_state = jl_gc_safe_enter(ct->ptls); + this->native = std::unique_lock(native); + ct->ptls->engine_nqueued++; // disables finalizers until inference is finished on this method graph + } + jl_unique_gcsafe_lock(jl_unique_gcsafe_lock &&native) = delete; + jl_unique_gcsafe_lock(jl_unique_gcsafe_lock &native) = delete; + ~jl_unique_gcsafe_lock() JL_NOTSAFEPOINT_LEAVE { + jl_task_t *ct = jl_current_task; + jl_gc_safe_leave(ct->ptls, gc_state); + ct->ptls->engine_nqueued--; // enable finalizers (but don't run them until the next gc) + } + void wait(std::condition_variable& cond) JL_NOTSAFEPOINT { + cond.wait(native); + } +}; #endif #endif diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 0bf3a729cbcb1..9fe36f32d2030 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -80,14 +80,16 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t if (!jl_subtype(rt_lb, selected_rt)) { // TODO: It would be better to try to get a specialization with the // correct rt check here (or we could codegen a wrapper). - specptr = NULL; invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + specptr = NULL; // this will force codegen of the unspecialized version + invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; jl_value_t *ts[2] = {rt_lb, (jl_value_t*)ci->rettype}; selected_rt = jl_type_union(ts, 2); } if (!jl_subtype(ci->rettype, rt_ub)) { // TODO: It would be better to try to get a specialization with the // correct rt check here (or we could codegen a wrapper). - specptr = NULL; invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + specptr = NULL; // this will force codegen of the unspecialized version + invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; selected_rt = jl_type_intersection(rt_ub, selected_rt); } @@ -108,8 +110,7 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t jl_value_t *oc_type JL_ALWAYS_LEAFTYPE = jl_apply_type2((jl_value_t*)jl_opaque_closure_type, (jl_value_t*)argt, selected_rt); JL_GC_PROMISE_ROOTED(oc_type); - if (!specptr) { - sigtype = jl_argtype_with_function_type((jl_value_t*)oc_type, (jl_value_t*)argt); + if (specptr == NULL) { jl_method_instance_t *mi_generic = jl_specializations_get_linfo(jl_opaque_closure_method, sigtype, jl_emptysvec); // OC wrapper methods are not world dependent @@ -197,7 +198,7 @@ int jl_tupletype_length_compat(jl_value_t *v, size_t nargs) JL_CALLABLE(jl_f_opaque_closure_call) { - jl_opaque_closure_t* oc = (jl_opaque_closure_t*)F; + jl_opaque_closure_t *oc = (jl_opaque_closure_t*)F; jl_value_t *argt = jl_tparam0(jl_typeof(oc)); if (!jl_tupletype_length_compat(argt, nargs)) jl_method_error(F, args, nargs + 1, oc->world); diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 09d51598ea8b7..f8935070bb001 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -980,3 +980,9 @@ extern "C" JL_DLLEXPORT_CODEGEN ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() JL_NOTSAFEPOINT { return {LLVM_PLUGIN_API_VERSION, "Julia", "1", registerCallbacks}; } + +void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIRAnalysis analysis) +{ + PM->add(new TargetLibraryInfoWrapperPass(triple)); + PM->add(createTargetTransformInfoWrapperPass(std::move(analysis))); +} diff --git a/src/stackwalk.c b/src/stackwalk.c index 6784e601bcfba..7c6f946fe73c5 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -642,13 +642,13 @@ void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT for (i = 0; i < n; i++) { jl_frame_t frame = frames[i]; if (!frame.func_name) { - jl_safe_printf("unknown function (ip: %p)\n", (void*)ip); + jl_safe_printf("unknown function (ip: %p) at %s\n", (void*)ip, frame.file_name ? frame.file_name : "(unknown file)"); } else { jl_safe_print_codeloc(frame.func_name, frame.file_name, frame.line, frame.inlined); free(frame.func_name); - free(frame.file_name); } + free(frame.file_name); } free(frames); } From aa51abe6400bc29c6093dca5a395fc03806ab511 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 19 Oct 2024 01:20:59 -0400 Subject: [PATCH 442/548] rename: invalid -> incompatible cache header (#56240) Falling back to the older serial precompilation process is basically a bug (except for if a manifest hasn't been resolved) so https://github.com/JuliaLang/julia/pull/52619 added more info on why it's been hit so we have a chance of fixing issues that are otherwise very difficult to recreate. However "invalid header" which usually just means it was made by a different julia version appears to sound too alarming to users. https://discourse.julialang.org/t/cache-misses-when-using-packages-since-upgrading-to-1-11/121445 So soften it there and in error messages, given it seems a better description. Suggested by @giordano in https://discourse.julialang.org/t/cache-misses-when-using-packages-since-upgrading-to-1-11/121445/4?u=ianshmean --- base/loading.jl | 22 +++++++++++----------- stdlib/Logging/docs/src/index.md | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 78c584d00852b..db6a681bb2a5b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1247,7 +1247,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No else io = open(path, "r") try - iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") + iszero(isvalid_cache_header(io)) && return ArgumentError("Incompatible header in cache file $path.") _, (includes, _, _), _, _, _, _, _, _ = parse_cache_header(io, path) ignore_native = pkg_tracked(includes) finally @@ -1887,7 +1887,7 @@ function isrelocatable(pkg::PkgId) isnothing(path) && return false io = open(path, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) _, (includes, includes_srcfiles, _), _... = _parse_cache_header(io, path) for inc in includes !startswith(inc.filename, "@depot") && return false @@ -1962,7 +1962,7 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union io = open(path, "r") ignore_native = false try - iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") + iszero(isvalid_cache_header(io)) && return ArgumentError("Incompatible header in cache file $path.") _, (includes, _, _), depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io, path) ignore_native = pkg_tracked(includes) @@ -3179,7 +3179,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in # append extra crc to the end of the .ji file: open(tmppath, "r+") do f if iszero(isvalid_cache_header(f)) - error("Invalid header for $(repr("text/plain", pkg)) in new cache file $(repr(tmppath)).") + error("Incompatible header for $(repr("text/plain", pkg)) in new cache file $(repr(tmppath)).") end seekend(f) write(f, crc_so) @@ -3503,7 +3503,7 @@ end function parse_cache_header(cachefile::String) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) ret = parse_cache_header(io, cachefile) return ret finally @@ -3516,7 +3516,7 @@ function preferences_hash(cachefile::String) io = open(cachefile, "r") try if iszero(isvalid_cache_header(io)) - throw(ArgumentError("Invalid header in cache file $cachefile.")) + throw(ArgumentError("Incompatible header in cache file $cachefile.")) end return preferences_hash(io, cachefile) finally @@ -3532,7 +3532,7 @@ end function cache_dependencies(cachefile::String) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) return cache_dependencies(io, cachefile) finally close(io) @@ -3572,7 +3572,7 @@ end function read_dependency_src(cachefile::String, filename::AbstractString) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) return read_dependency_src(io, cachefile, filename) finally close(io) @@ -3856,9 +3856,9 @@ end try checksum = isvalid_cache_header(io) if iszero(checksum) - @debug "Rejecting cache file $cachefile due to it containing an invalid cache header" - record_reason(reasons, "invalid header") - return true # invalid cache file + @debug "Rejecting cache file $cachefile due to it containing an incompatible cache header" + record_reason(reasons, "incompatible header") + return true # incompatible cache file end modules, (includes, _, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, actual_flags = parse_cache_header(io, cachefile) if isempty(modules) diff --git a/stdlib/Logging/docs/src/index.md b/stdlib/Logging/docs/src/index.md index 17d4e71328ac4..a2bfd499e4586 100644 --- a/stdlib/Logging/docs/src/index.md +++ b/stdlib/Logging/docs/src/index.md @@ -191,10 +191,10 @@ module. Loading julia with `JULIA_DEBUG=loading` will activate ``` $ JULIA_DEBUG=loading julia -e 'using OhMyREPL' -┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an invalid cache header +┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an incompatible cache header └ @ Base loading.jl:1328 [ Info: Recompiling stale cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji for module OhMyREPL -┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an invalid cache header +┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an incompatible cache header └ @ Base loading.jl:1328 ... ``` From b0c1525f1731186767ae42e7d625bf0909e49af8 Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Sat, 19 Oct 2024 09:27:56 +0100 Subject: [PATCH 443/548] Restore support for checking for UndefVarError variable name in at-test_throws (#56231) Fix https://github.com/JuliaLang/julia/issues/54082 Arguably this was a breaking change (as a consequence of https://github.com/JuliaLang/julia/pull/51979). But regardless, it seems like useful functionality to have a public API for testing that an `UndefVarError` was thrown for the expected variable name (regardless of scope). This is particularly useful if you don't know what the scope is (for example, in my use-case i want to test that a specific `UndefVarError` is thrown from a module with a `gensym`'d name). Pre-v1.11 the syntax for this was ```julia @test_throws UndefVarError(:x) foo() ``` but that stopped working in v1.11 when `UndefVarError` got a second field (in fact in v1.11.1 this is an error when before it would pass) This PR restores that functionality. We might want to backport it to v1.11.x so that v1.11 isn't the only version that doesn't support this. --- stdlib/Test/src/Test.jl | 6 +++++- stdlib/Test/test/runtests.jl | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 46bc2d8790cec..cf906591b9962 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -812,7 +812,11 @@ function do_test_throws(result::ExecutionResult, orig_expr, extype) if extype isa LoadError && !(exc isa LoadError) && typeof(extype.error) == typeof(exc) extype = extype.error # deprecated end - if isa(exc, typeof(extype)) + # Support `UndefVarError(:x)` meaning `UndefVarError(:x, scope)` for any `scope`. + # Retains the behaviour from pre-v1.11 when `UndefVarError` didn't have `scope`. + if isa(extype, UndefVarError) && !isdefined(extype, :scope) + success = exc isa UndefVarError && exc.var == extype.var + else isa(exc, typeof(extype)) success = true for fld in 1:nfields(extype) if !isequal(getfield(extype, fld), getfield(exc, fld)) diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 3ddcd7d5de0fd..0c08f78ef356f 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1736,3 +1736,20 @@ end This is deprecated and may error in the future.""" @test_deprecated msg2 @macroexpand @testset DefaultTestSet DefaultTestSet begin end end + +# Issue #54082 +module M54082 end +@testset "@test_throws UndefVarError(:var)" begin + # Single-arg `UndefVarError` should match all `UndefVarError` for the + # same variable name, regardless of scope, to keep pre-v1.11 behaviour. + f54082() = var + @test_throws UndefVarError(:var) f54082() + # But if scope is set, then it has to match. + @test_throws UndefVarError(:var, M54082) M54082.var + let result = @testset NoThrowTestSet begin + # Wrong module scope + @test_throws UndefVarError(:var, Main) M54082.var + end + @test only(result) isa Test.Fail + end +end From 877de9839809e769d4f9707a61df3400d087d8d3 Mon Sep 17 00:00:00 2001 From: Eduardo Souza Date: Sun, 20 Oct 2024 00:46:03 +1100 Subject: [PATCH 444/548] Refactoring to be considered before adding MMTk (#55608) This PR contains some refactoring of common functions that were moved to `gc-common.c` and should be shared between MMTk and Julia's stock GC. --- src/Makefile | 2 +- src/gc-common.c | 205 +++++++++++++++++++++++++++ src/gc-common.h | 12 ++ src/gc-debug.c | 42 ------ src/gc-interface.h | 37 ++--- src/gc-stacks.c | 22 +-- src/gc-stock.c | 325 ++++++++++++++----------------------------- src/gc-stock.h | 6 - src/gc-tls-common.h | 52 +++++++ src/gc-tls.h | 25 ---- src/julia_internal.h | 26 +--- src/julia_threads.h | 2 + src/scheduler.c | 4 - src/stackwalk.c | 12 +- 14 files changed, 402 insertions(+), 370 deletions(-) create mode 100644 src/gc-tls-common.h diff --git a/src/Makefile b/src/Makefile index 75635c2e6c062..3458f51fa5548 100644 --- a/src/Makefile +++ b/src/Makefile @@ -103,7 +103,7 @@ ifeq ($(USE_SYSTEM_LIBUV),0) UV_HEADERS += uv.h UV_HEADERS += uv/*.h endif -PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h) +PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h gc-tls-common.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h) ifeq ($(OS),WINNT) PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h) endif diff --git a/src/gc-common.c b/src/gc-common.c index ee461b576ea9e..b552afb8228f0 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -20,6 +20,11 @@ extern "C" { jl_gc_num_t gc_num = {0}; +JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void) +{ + return gc_num.total_time; +} + // =========================================================================== // // GC Callbacks // =========================================================================== // @@ -485,10 +490,210 @@ JL_DLLEXPORT void jl_finalize(jl_value_t *o) int gc_n_threads; jl_ptls_t* gc_all_tls_states; +// =========================================================================== // +// Allocation +// =========================================================================== // + +JL_DLLEXPORT void * jl_gc_alloc_typed(jl_ptls_t ptls, size_t sz, void *ty) +{ + return jl_gc_alloc(ptls, sz, ty); +} + +JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz) +{ + jl_ptls_t ptls = jl_current_task->ptls; + return jl_gc_alloc(ptls, sz, NULL); +} + +// allocation wrappers that save the size of allocations, to allow using +// jl_gc_counted_* functions with a libc-compatible API. + +JL_DLLEXPORT void *jl_malloc(size_t sz) +{ + int64_t *p = (int64_t *)jl_gc_counted_malloc(sz + JL_SMALL_BYTE_ALIGNMENT); + if (p == NULL) + return NULL; + p[0] = sz; + return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 +} + +//_unchecked_calloc does not check for potential overflow of nm*sz +STATIC_INLINE void *_unchecked_calloc(size_t nm, size_t sz) { + size_t nmsz = nm*sz; + int64_t *p = (int64_t *)jl_gc_counted_calloc(nmsz + JL_SMALL_BYTE_ALIGNMENT, 1); + if (p == NULL) + return NULL; + p[0] = nmsz; + return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 +} + +JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz) +{ + if (nm > SSIZE_MAX/sz - JL_SMALL_BYTE_ALIGNMENT) + return NULL; + return _unchecked_calloc(nm, sz); +} + +JL_DLLEXPORT void jl_free(void *p) +{ + if (p != NULL) { + int64_t *pp = (int64_t *)p - 2; + size_t sz = pp[0]; + jl_gc_counted_free_with_size(pp, sz + JL_SMALL_BYTE_ALIGNMENT); + } +} + +JL_DLLEXPORT void *jl_realloc(void *p, size_t sz) +{ + int64_t *pp; + size_t szold; + if (p == NULL) { + pp = NULL; + szold = 0; + } + else { + pp = (int64_t *)p - 2; + szold = pp[0] + JL_SMALL_BYTE_ALIGNMENT; + } + int64_t *pnew = (int64_t *)jl_gc_counted_realloc_with_old_size(pp, szold, sz + JL_SMALL_BYTE_ALIGNMENT); + if (pnew == NULL) + return NULL; + pnew[0] = sz; + return (void *)(pnew + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 +} + +// allocator entry points + +JL_DLLEXPORT jl_value_t *(jl_gc_alloc)(jl_ptls_t ptls, size_t sz, void *ty) +{ + return jl_gc_alloc_(ptls, sz, ty); +} + +// =========================================================================== // +// Generic Memory +// =========================================================================== // + +size_t jl_genericmemory_nbytes(jl_genericmemory_t *m) JL_NOTSAFEPOINT +{ + const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m))->layout; + size_t sz = layout->size * m->length; + if (layout->flags.arrayelem_isunion) + // account for isbits Union array selector bytes + sz += m->length; + return sz; +} + +// tracking Memorys with malloc'd storage +void jl_gc_track_malloced_genericmemory(jl_ptls_t ptls, jl_genericmemory_t *m, int isaligned){ + // This is **NOT** a GC safe point. + mallocmemory_t *ma; + if (ptls->gc_tls_common.heap.mafreelist == NULL) { + ma = (mallocmemory_t*)malloc_s(sizeof(mallocmemory_t)); + } + else { + ma = ptls->gc_tls_common.heap.mafreelist; + ptls->gc_tls_common.heap.mafreelist = ma->next; + } + ma->a = (jl_genericmemory_t*)((uintptr_t)m | !!isaligned); + ma->next = ptls->gc_tls_common.heap.mallocarrays; + ptls->gc_tls_common.heap.mallocarrays = ma; +} + +// =========================================================================== // +// GC Debug +// =========================================================================== // + +int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT +{ + int nf = (int)jl_datatype_nfields(vt); + for (int i = 1; i < nf; i++) { + if (slot < (void*)((char*)obj + jl_field_offset(vt, i))) + return i - 1; + } + return nf - 1; +} + +int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT +{ + char *slot = (char*)_slot; + jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj); + char *start = NULL; + size_t len = 0; + size_t elsize = sizeof(void*); + if (vt == jl_module_type) { + jl_module_t *m = (jl_module_t*)obj; + start = (char*)m->usings.items; + len = module_usings_length(m); + elsize = sizeof(struct _jl_module_using); + } + else if (vt == jl_simplevector_type) { + start = (char*)jl_svec_data(obj); + len = jl_svec_len(obj); + } + if (slot < start || slot >= start + elsize * len) + return -1; + return (slot - start) / elsize; +} + +// =========================================================================== // +// GC Control +// =========================================================================== // + +JL_DLLEXPORT uint32_t jl_get_gc_disable_counter(void) { + return jl_atomic_load_acquire(&jl_gc_disable_counter); +} + +JL_DLLEXPORT int jl_gc_is_enabled(void) +{ + jl_ptls_t ptls = jl_current_task->ptls; + return !ptls->disable_gc; +} + +int gc_logging_enabled = 0; + +JL_DLLEXPORT void jl_enable_gc_logging(int enable) { + gc_logging_enabled = enable; +} + +JL_DLLEXPORT int jl_is_gc_logging_enabled(void) { + return gc_logging_enabled; +} + + +// collector entry point and control +_Atomic(uint32_t) jl_gc_disable_counter = 1; + +JL_DLLEXPORT int jl_gc_enable(int on) +{ + jl_ptls_t ptls = jl_current_task->ptls; + int prev = !ptls->disable_gc; + ptls->disable_gc = (on == 0); + if (on && !prev) { + // disable -> enable + if (jl_atomic_fetch_add(&jl_gc_disable_counter, -1) == 1) { + gc_num.allocd += gc_num.deferred_alloc; + gc_num.deferred_alloc = 0; + } + } + else if (prev && !on) { + // enable -> disable + jl_atomic_fetch_add(&jl_gc_disable_counter, 1); + // check if the GC is running and wait for it to finish + jl_gc_safepoint_(ptls); + } + return prev; +} + // =========================================================================== // // MISC // =========================================================================== // +JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value) +{ + jl_ptls_t ptls = jl_current_task->ptls; + return jl_gc_new_weakref_th(ptls, value); +} + const uint64_t _jl_buff_tag[3] = {0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull}; // aka 0xHEADER00 JL_DLLEXPORT uintptr_t jl_get_buff_tag(void) JL_NOTSAFEPOINT { diff --git a/src/gc-common.h b/src/gc-common.h index 4d53830442a7d..32b7470b13a58 100644 --- a/src/gc-common.h +++ b/src/gc-common.h @@ -53,6 +53,12 @@ extern jl_gc_callback_list_t *gc_cblist_notify_gc_pressure; // malloc wrappers, aligned allocation // =========================================================================== // +// data structure for tracking malloc'd genericmemory. +typedef struct _mallocmemory_t { + jl_genericmemory_t *a; // lowest bit is tagged if this is aligned memory + struct _mallocmemory_t *next; +} mallocmemory_t; + #if defined(_OS_WINDOWS_) STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) { @@ -173,4 +179,10 @@ JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o); extern int gc_n_threads; extern jl_ptls_t* gc_all_tls_states; +// =========================================================================== // +// Logging +// =========================================================================== // + +extern int gc_logging_enabled; + #endif // JL_GC_COMMON_H diff --git a/src/gc-debug.c b/src/gc-debug.c index 5c150aba68e10..7c479484cde45 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -1105,48 +1105,6 @@ void gc_count_pool(void) jl_safe_printf("************************\n"); } -int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT -{ - int nf = (int)jl_datatype_nfields(vt); - for (int i = 1; i < nf; i++) { - if (slot < (void*)((char*)obj + jl_field_offset(vt, i))) - return i - 1; - } - return nf - 1; -} - -int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT -{ - char *slot = (char*)_slot; - jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj); - char *start = NULL; - size_t len = 0; - size_t elsize = sizeof(void*); - if (vt == jl_module_type) { - jl_module_t *m = (jl_module_t*)obj; - start = (char*)m->usings.items; - len = module_usings_length(m); - elsize = sizeof(struct _jl_module_using); - } - else if (vt == jl_simplevector_type) { - start = (char*)jl_svec_data(obj); - len = jl_svec_len(obj); - } - if (slot < start || slot >= start + elsize * len) - return -1; - return (slot - start) / elsize; -} - -static int gc_logging_enabled = 0; - -JL_DLLEXPORT void jl_enable_gc_logging(int enable) { - gc_logging_enabled = enable; -} - -JL_DLLEXPORT int jl_is_gc_logging_enabled(void) { - return gc_logging_enabled; -} - void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect, int64_t live_bytes) JL_NOTSAFEPOINT { if (!gc_logging_enabled) { return; diff --git a/src/gc-interface.h b/src/gc-interface.h index bb2abbe2d36ac..0b5df17a3b8c5 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -96,6 +96,8 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem); // should run a collection cycle again (e.g. a full mark right after a full sweep to ensure // we do a full heap traversal). JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection); +// Returns whether the thread with `tid` is a collector thread +JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT; // ========================================================================= // // Metrics @@ -130,6 +132,13 @@ JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void); // Allocation // ========================================================================= // +// On GCC, this function is inlined when sz is constant (see julia_internal.h) +// In general, this function should implement allocation and should use the specific GC's logic +// to decide whether to allocate a small or a large object. Finally, note that this function +// **must** also set the type of the returning object to be `ty`. The type `ty` may also be used to record +// an allocation of that type in the allocation profiler. +struct _jl_value_t *jl_gc_alloc_(struct _jl_tls_states_t * ptls, size_t sz, void *ty); + // Allocates small objects and increments Julia allocation counterst. Size of the object // header must be included in the object size. The (possibly unused in some implementations) // offset to the arena in which we're allocating is passed in the second parameter, and the @@ -157,26 +166,6 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz); JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz); // Wrapper around Libc realloc that updates Julia allocation counters. JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size_t sz); -// Wrapper around Libc malloc that allocates a memory region with a few additional machine -// words before the actual payload that are used to record the size of the requested -// allocation. Also updates Julia allocation counters. The function returns a pointer to the -// payload as a result of the allocation. -JL_DLLEXPORT void *jl_malloc(size_t sz); -// Wrapper around Libc calloc that allocates a memory region with a few additional machine -// words before the actual payload that are used to record the size of the requested -// allocation. Also updates Julia allocation counters. The function returns a pointer to the -// payload as a result of the allocation. -JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz); -// Wrapper around Libc free that takes a pointer to the payload of a memory region allocated -// with jl_malloc or jl_calloc, and uses the size information stored in the first machine -// words of the memory buffer update Julia allocation counters, and then frees the -// corresponding memory buffer. -JL_DLLEXPORT void jl_free(void *p); -// Wrapper around Libc realloc that takes a memory region allocated with jl_malloc or -// jl_calloc, and uses the size information stored in the first machine words of the memory -// buffer to update Julia allocation counters, reallocating the corresponding memory buffer -// in the end. -JL_DLLEXPORT void *jl_realloc(void *p, size_t sz); // Wrapper around Libc malloc that's used to dynamically allocate memory for Arrays and // Strings. It increments Julia allocation counters and should check whether we're close to // the Julia heap target, and therefore, whether we should run a collection. Note that this @@ -190,14 +179,6 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz); // thread-local allocator of the thread referenced by the first jl_ptls_t argument. JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref_th(struct _jl_tls_states_t *ptls, struct _jl_value_t *value); -// Allocates a new weak-reference, assigns its value and increments Julia allocation -// counters. If thread-local allocators are used, then this function should allocate in the -// thread-local allocator of the current thread. -JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref(struct _jl_value_t *value); -// Allocates an object whose size is specified by the function argument and increments Julia -// allocation counters. If thread-local allocators are used, then this function should -// allocate in the thread-local allocator of the current thread. -JL_DLLEXPORT struct _jl_value_t *jl_gc_allocobj(size_t sz); // Permanently allocates a memory slot of the size specified by the first parameter. This // block of memory is allocated in an immortal region that is never swept. The second // parameter specifies whether the memory should be filled with zeros. The third and fourth diff --git a/src/gc-stacks.c b/src/gc-stacks.c index f6e787a4c1d2d..a2d3862dc9501 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -47,7 +47,7 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT } -static void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT +void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { VirtualFree(stkbuf, 0, MEM_RELEASE); jl_atomic_fetch_add_relaxed(&num_stack_mappings, -1); @@ -82,7 +82,7 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT return stk; } -static void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT +void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { munmap(stkbuf, bufsz); jl_atomic_fetch_add_relaxed(&num_stack_mappings, -1); @@ -132,7 +132,7 @@ void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(bufsz); if (pool_sizes[pool_id] == bufsz) { - small_arraylist_push(&ptls->gc_tls.heap.free_stacks[pool_id], stkbuf); + small_arraylist_push(&ptls->gc_tls_common.heap.free_stacks[pool_id], stkbuf); return; } } @@ -161,7 +161,7 @@ void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task) #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); #endif - small_arraylist_push(&ptls->gc_tls.heap.free_stacks[pool_id], stkbuf); + small_arraylist_push(&ptls->gc_tls_common.heap.free_stacks[pool_id], stkbuf); } } } @@ -176,7 +176,7 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO if (ssize <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(ssize); ssize = pool_sizes[pool_id]; - small_arraylist_t *pool = &ptls->gc_tls.heap.free_stacks[pool_id]; + small_arraylist_t *pool = &ptls->gc_tls_common.heap.free_stacks[pool_id]; if (pool->len > 0) { stk = small_arraylist_pop(pool); } @@ -197,7 +197,7 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO } *bufsz = ssize; if (owner) { - small_arraylist_t *live_tasks = &ptls->gc_tls.heap.live_tasks; + small_arraylist_t *live_tasks = &ptls->gc_tls_common.heap.live_tasks; mtarraylist_push(live_tasks, owner); } return stk; @@ -228,7 +228,7 @@ void sweep_stack_pool_loop(void) JL_NOTSAFEPOINT // free half of stacks that remain unused since last sweep if (i == jl_atomic_load_relaxed(&gc_stack_free_idx)) { for (int p = 0; p < JL_N_STACK_POOLS; p++) { - small_arraylist_t *al = &ptls2->gc_tls.heap.free_stacks[p]; + small_arraylist_t *al = &ptls2->gc_tls_common.heap.free_stacks[p]; size_t n_to_free; if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { n_to_free = al->len; // not alive yet or dead, so it does not need these anymore @@ -251,10 +251,10 @@ void sweep_stack_pool_loop(void) JL_NOTSAFEPOINT } } if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { - small_arraylist_free(ptls2->gc_tls.heap.free_stacks); + small_arraylist_free(ptls2->gc_tls_common.heap.free_stacks); } - small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; + small_arraylist_t *live_tasks = &ptls2->gc_tls_common.heap.live_tasks; size_t n = 0; size_t ndel = 0; size_t l = live_tasks->len; @@ -306,7 +306,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) jl_ptls_t ptls2 = allstates[i]; if (ptls2 == NULL) continue; - small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; + small_arraylist_t *live_tasks = &ptls2->gc_tls_common.heap.live_tasks; size_t n = mtarraylist_length(live_tasks); l += n + (ptls2->root_task->ctx.stkbuf != NULL); } @@ -325,7 +325,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) goto restart; jl_array_data(a,void*)[j++] = t; } - small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; + small_arraylist_t *live_tasks = &ptls2->gc_tls_common.heap.live_tasks; size_t n = mtarraylist_length(live_tasks); for (size_t i = 0; i < n; i++) { jl_task_t *t = (jl_task_t*)mtarraylist_get(live_tasks, i); diff --git a/src/gc-stock.c b/src/gc-stock.c index f60aa89e6b11d..541c5b4ecc5c2 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -363,7 +363,7 @@ JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref_th(jl_ptls_t ptls, jl_value_t *valu jl_weakref_t *wr = (jl_weakref_t*)jl_gc_alloc(ptls, sizeof(void*), jl_weakref_type); wr->value = value; // NOTE: wb not needed here - small_arraylist_push(&ptls->gc_tls.heap.weak_refs, wr); + small_arraylist_push(&ptls->gc_tls_common.heap.weak_refs, wr); return wr; } @@ -373,8 +373,8 @@ static void clear_weak_refs(void) for (int i = 0; i < gc_n_threads; i++) { jl_ptls_t ptls2 = gc_all_tls_states[i]; if (ptls2 != NULL) { - size_t n, l = ptls2->gc_tls.heap.weak_refs.len; - void **lst = ptls2->gc_tls.heap.weak_refs.items; + size_t n, l = ptls2->gc_tls_common.heap.weak_refs.len; + void **lst = ptls2->gc_tls_common.heap.weak_refs.items; for (n = 0; n < l; n++) { jl_weakref_t *wr = (jl_weakref_t*)lst[n]; if (!gc_marked(jl_astaggedvalue(wr->value)->bits.gc)) @@ -392,8 +392,8 @@ static void sweep_weak_refs(void) if (ptls2 != NULL) { size_t n = 0; size_t ndel = 0; - size_t l = ptls2->gc_tls.heap.weak_refs.len; - void **lst = ptls2->gc_tls.heap.weak_refs.items; + size_t l = ptls2->gc_tls_common.heap.weak_refs.len; + void **lst = ptls2->gc_tls_common.heap.weak_refs.items; if (l == 0) continue; while (1) { @@ -408,7 +408,7 @@ static void sweep_weak_refs(void) lst[n] = lst[n + ndel]; lst[n + ndel] = tmp; } - ptls2->gc_tls.heap.weak_refs.len -= ndel; + ptls2->gc_tls_common.heap.weak_refs.len -= ndel; } } } @@ -416,18 +416,18 @@ static void sweep_weak_refs(void) STATIC_INLINE void jl_batch_accum_heap_size(jl_ptls_t ptls, uint64_t sz) JL_NOTSAFEPOINT { - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.alloc_acc) + sz; + uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc) + sz; if (alloc_acc < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.alloc_acc, alloc_acc); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc, alloc_acc); else { jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.alloc_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc, 0); } } STATIC_INLINE void jl_batch_accum_free_size(jl_ptls_t ptls, uint64_t sz) JL_NOTSAFEPOINT { - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.free_acc, jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.free_acc) + sz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.free_acc, jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.free_acc) + sz); } // big value list @@ -448,10 +448,10 @@ STATIC_INLINE jl_value_t *jl_gc_big_alloc_inner(jl_ptls_t ptls, size_t sz) jl_throw(jl_memory_exception); gc_invoke_callbacks(jl_gc_cb_notify_external_alloc_t, gc_cblist_notify_external_alloc, (v, allocsz)); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + allocsz); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.bigalloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.bigalloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + allocsz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.bigalloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.bigalloc) + 1); jl_batch_accum_heap_size(ptls, allocsz); #ifdef MEMDEBUG memset(v, 0xee, allocsz); @@ -561,29 +561,11 @@ static void sweep_big(jl_ptls_t ptls) JL_NOTSAFEPOINT gc_time_big_end(); } -// tracking Memorys with malloc'd storage - -void jl_gc_track_malloced_genericmemory(jl_ptls_t ptls, jl_genericmemory_t *m, int isaligned){ - // This is **NOT** a GC safe point. - mallocmemory_t *ma; - if (ptls->gc_tls.heap.mafreelist == NULL) { - ma = (mallocmemory_t*)malloc_s(sizeof(mallocmemory_t)); - } - else { - ma = ptls->gc_tls.heap.mafreelist; - ptls->gc_tls.heap.mafreelist = ma->next; - } - ma->a = (jl_genericmemory_t*)((uintptr_t)m | !!isaligned); - ma->next = ptls->gc_tls.heap.mallocarrays; - ptls->gc_tls.heap.mallocarrays = ma; -} - - void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT { jl_ptls_t ptls = jl_current_task->ptls; - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + sz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + sz); jl_batch_accum_heap_size(ptls, sz); } @@ -602,18 +584,18 @@ static void combine_thread_gc_counts(jl_gc_num_t *dest, int update_heap) JL_NOTS for (int i = 0; i < gc_n_threads; i++) { jl_ptls_t ptls = gc_all_tls_states[i]; if (ptls) { - dest->allocd += (jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + gc_num.interval); - dest->malloc += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.malloc); - dest->realloc += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.realloc); - dest->poolalloc += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.poolalloc); - dest->bigalloc += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.bigalloc); - dest->freed += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.free_acc); + dest->allocd += (jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + gc_num.interval); + dest->malloc += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.malloc); + dest->realloc += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.realloc); + dest->poolalloc += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.poolalloc); + dest->bigalloc += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.bigalloc); + dest->freed += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.free_acc); if (update_heap) { - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.alloc_acc); - freed_in_runtime += jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.free_acc); + uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc); + freed_in_runtime += jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.free_acc); jl_atomic_store_relaxed(&gc_heap_stats.heap_size, alloc_acc + jl_atomic_load_relaxed(&gc_heap_stats.heap_size)); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.alloc_acc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.free_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.free_acc, 0); } } } @@ -629,13 +611,13 @@ static void reset_thread_gc_counts(void) JL_NOTSAFEPOINT jl_ptls_t ptls = gc_all_tls_states[i]; if (ptls != NULL) { // don't reset `pool_live_bytes` here - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, -(int64_t)gc_num.interval); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.malloc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.realloc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.poolalloc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.bigalloc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.alloc_acc, 0); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.free_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, -(int64_t)gc_num.interval); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.malloc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.realloc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.poolalloc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.bigalloc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.alloc_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.free_acc, 0); } } } @@ -655,17 +637,6 @@ void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT reset_thread_gc_counts(); } -size_t jl_genericmemory_nbytes(jl_genericmemory_t *m) JL_NOTSAFEPOINT -{ - const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m))->layout; - size_t sz = layout->size * m->length; - if (layout->flags.arrayelem_isunion) - // account for isbits Union array selector bytes - sz += m->length; - return sz; -} - - static void jl_gc_free_memory(jl_value_t *v, int isaligned) JL_NOTSAFEPOINT { assert(jl_is_genericmemory(v)); @@ -689,8 +660,8 @@ static void sweep_malloced_memory(void) JL_NOTSAFEPOINT for (int t_i = 0; t_i < gc_n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; if (ptls2 != NULL) { - mallocmemory_t *ma = ptls2->gc_tls.heap.mallocarrays; - mallocmemory_t **pma = &ptls2->gc_tls.heap.mallocarrays; + mallocmemory_t *ma = ptls2->gc_tls_common.heap.mallocarrays; + mallocmemory_t **pma = &ptls2->gc_tls_common.heap.mallocarrays; while (ma != NULL) { mallocmemory_t *nxt = ma->next; jl_value_t *a = (jl_value_t*)((uintptr_t)ma->a & ~1); @@ -702,8 +673,8 @@ static void sweep_malloced_memory(void) JL_NOTSAFEPOINT *pma = nxt; int isaligned = (uintptr_t)ma->a & 1; jl_gc_free_memory(a, isaligned); - ma->next = ptls2->gc_tls.heap.mafreelist; - ptls2->gc_tls.heap.mafreelist = ma; + ma->next = ptls2->gc_tls_common.heap.mafreelist; + ptls2->gc_tls_common.heap.mafreelist = ma; } gc_time_count_mallocd_memory(bits); ma = nxt; @@ -764,12 +735,12 @@ STATIC_INLINE jl_value_t *jl_gc_small_alloc_inner(jl_ptls_t ptls, int offset, return jl_gc_big_alloc(ptls, osize, NULL); #endif maybe_collect(ptls); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + osize); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.pool_live_bytes, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.pool_live_bytes) + osize); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.poolalloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.poolalloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + osize); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.pool_live_bytes, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.pool_live_bytes) + osize); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.poolalloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.poolalloc) + 1); // first try to use the freelist jl_taggedvalue_t *v = p->freelist; if (v != NULL) { @@ -824,6 +795,29 @@ jl_value_t *jl_gc_small_alloc_noinline(jl_ptls_t ptls, int offset, int osize) { return jl_gc_small_alloc_inner(ptls, offset, osize); } +// Size does NOT include the type tag!! +inline jl_value_t *jl_gc_alloc_(jl_ptls_t ptls, size_t sz, void *ty) +{ + jl_value_t *v; + const size_t allocsz = sz + sizeof(jl_taggedvalue_t); + if (sz <= GC_MAX_SZCLASS) { + int pool_id = jl_gc_szclass(allocsz); + jl_gc_pool_t *p = &ptls->gc_tls.heap.norm_pools[pool_id]; + int osize = jl_gc_sizeclasses[pool_id]; + // We call `jl_gc_small_alloc_noinline` instead of `jl_gc_small_alloc` to avoid double-counting in + // the Allocations Profiler. (See https://github.com/JuliaLang/julia/pull/43868 for more details.) + v = jl_gc_small_alloc_noinline(ptls, (char*)p - (char*)ptls, osize); + } + else { + if (allocsz < sz) // overflow in adding offs, size was "negative" + jl_throw(jl_memory_exception); + v = jl_gc_big_alloc_noinline(ptls, allocsz); + } + jl_set_typeof(v, ty); + maybe_record_alloc_to_profile(v, sz, (jl_datatype_t*)ty); + return v; +} + int jl_gc_classify_pools(size_t sz, int *osize) { if (sz > GC_MAX_SZCLASS) @@ -983,8 +977,8 @@ static void gc_sweep_page(gc_page_profiler_serializer_t *s, jl_gc_pool_t *p, jl_ // instead of adding it to the thread that originally allocated the page, so we can avoid // an atomic-fetch-add here. size_t delta = (GC_PAGE_SZ - GC_PAGE_OFFSET - nfree * osize); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.pool_live_bytes, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.pool_live_bytes) + delta); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.pool_live_bytes, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.pool_live_bytes) + delta); jl_atomic_fetch_add_relaxed((_Atomic(int64_t) *)&gc_num.freed, (nfree - old_nfree) * osize); } @@ -1277,7 +1271,7 @@ static void gc_sweep_pool(void) } continue; } - jl_atomic_store_relaxed(&ptls2->gc_tls.gc_num.pool_live_bytes, 0); + jl_atomic_store_relaxed(&ptls2->gc_tls_common.gc_num.pool_live_bytes, 0); for (int i = 0; i < JL_GC_N_POOLS; i++) { jl_gc_pool_t *p = &ptls2->gc_tls.heap.norm_pools[i]; jl_taggedvalue_t *last = p->freelist; @@ -2841,34 +2835,8 @@ static void sweep_finalizer_list(arraylist_t *list) list->len = j; } -// collector entry point and control -_Atomic(uint32_t) jl_gc_disable_counter = 1; - -JL_DLLEXPORT int jl_gc_enable(int on) -{ - jl_ptls_t ptls = jl_current_task->ptls; - int prev = !ptls->disable_gc; - ptls->disable_gc = (on == 0); - if (on && !prev) { - // disable -> enable - if (jl_atomic_fetch_add(&jl_gc_disable_counter, -1) == 1) { - gc_num.allocd += gc_num.deferred_alloc; - gc_num.deferred_alloc = 0; - } - } - else if (prev && !on) { - // enable -> disable - jl_atomic_fetch_add(&jl_gc_disable_counter, 1); - // check if the GC is running and wait for it to finish - jl_gc_safepoint_(ptls); - } - return prev; -} - -JL_DLLEXPORT int jl_gc_is_enabled(void) -{ - jl_ptls_t ptls = jl_current_task->ptls; - return !ptls->disable_gc; +int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT { + return gc_is_parallel_collector_thread(tid) || gc_is_concurrent_collector_thread(tid); } JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) JL_NOTSAFEPOINT @@ -2879,11 +2847,6 @@ JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) JL_NOTSAFEPOINT *bytes = (num.total_allocd + num.deferred_alloc + num.allocd); } -JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void) -{ - return gc_num.total_time; -} - JL_DLLEXPORT jl_gc_num_t jl_gc_num(void) { jl_gc_num_t num = gc_num; @@ -2918,7 +2881,7 @@ JL_DLLEXPORT int64_t jl_gc_pool_live_bytes(void) for (int i = 0; i < n_threads; i++) { jl_ptls_t ptls2 = all_tls_states[i]; if (ptls2 != NULL) { - pool_live_bytes += jl_atomic_load_relaxed(&ptls2->gc_tls.gc_num.pool_live_bytes); + pool_live_bytes += jl_atomic_load_relaxed(&ptls2->gc_tls_common.gc_num.pool_live_bytes); } } return pool_live_bytes; @@ -3271,13 +3234,13 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // free empty GC state for threads that have exited if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) { // GC threads should never exit - assert(!gc_is_parallel_collector_thread(t_i)); - assert(!gc_is_concurrent_collector_thread(t_i)); + assert(!gc_is_collector_thread(t_i)); + jl_thread_heap_common_t *common_heap = &ptls2->gc_tls_common.heap; jl_thread_heap_t *heap = &ptls2->gc_tls.heap; - if (heap->weak_refs.len == 0) - small_arraylist_free(&heap->weak_refs); - if (heap->live_tasks.len == 0) - small_arraylist_free(&heap->live_tasks); + if (common_heap->weak_refs.len == 0) + small_arraylist_free(&common_heap->weak_refs); + if (common_heap->live_tasks.len == 0) + small_arraylist_free(&common_heap->live_tasks); if (heap->remset.len == 0) arraylist_free(&heap->remset); if (ptls2->finalizers.len == 0) @@ -3346,8 +3309,8 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; if (jl_atomic_load_acquire(&jl_gc_disable_counter)) { - size_t localbytes = jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + gc_num.interval; - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, -(int64_t)gc_num.interval); + size_t localbytes = jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + gc_num.interval; + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, -(int64_t)gc_num.interval); static_assert(sizeof(_Atomic(uint64_t)) == sizeof(gc_num.deferred_alloc), ""); jl_atomic_fetch_add_relaxed((_Atomic(uint64_t)*)&gc_num.deferred_alloc, localbytes); return; @@ -3449,16 +3412,10 @@ void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq) gc_mark_roots(mq); } -// allocator entry points - -JL_DLLEXPORT jl_value_t *(jl_gc_alloc)(jl_ptls_t ptls, size_t sz, void *ty) -{ - return jl_gc_alloc_(ptls, sz, ty); -} - // Per-thread initialization void jl_init_thread_heap(jl_ptls_t ptls) { + jl_thread_heap_common_t *common_heap = &ptls->gc_tls_common.heap; jl_thread_heap_t *heap = &ptls->gc_tls.heap; jl_gc_pool_t *p = heap->norm_pools; for (int i = 0; i < JL_GC_N_POOLS; i++) { @@ -3466,12 +3423,12 @@ void jl_init_thread_heap(jl_ptls_t ptls) p[i].freelist = NULL; p[i].newpages = NULL; } - small_arraylist_new(&heap->weak_refs, 0); - small_arraylist_new(&heap->live_tasks, 0); + small_arraylist_new(&common_heap->weak_refs, 0); + small_arraylist_new(&common_heap->live_tasks, 0); for (int i = 0; i < JL_N_STACK_POOLS; i++) - small_arraylist_new(&heap->free_stacks[i], 0); - heap->mallocarrays = NULL; - heap->mafreelist = NULL; + small_arraylist_new(&common_heap->free_stacks[i], 0); + common_heap->mallocarrays = NULL; + common_heap->mafreelist = NULL; heap->young_generation_of_bigvals = (bigval_t*)calloc_s(sizeof(bigval_t)); // sentinel assert(gc_bigval_sentinel_tag != 0); // make sure the sentinel is initialized heap->young_generation_of_bigvals->header = gc_bigval_sentinel_tag; @@ -3497,8 +3454,8 @@ void jl_init_thread_heap(jl_ptls_t ptls) jl_atomic_store_relaxed(&q->array, wsa2); arraylist_new(&mq->reclaim_set, 32); - memset(&ptls->gc_tls.gc_num, 0, sizeof(ptls->gc_tls.gc_num)); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, -(int64_t)gc_num.interval); + memset(&ptls->gc_tls_common.gc_num, 0, sizeof(ptls->gc_tls_common.gc_num)); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, -(int64_t)gc_num.interval); } void jl_free_thread_gc_state(jl_ptls_t ptls) @@ -3685,10 +3642,10 @@ JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz) if (data != NULL && pgcstack != NULL && ct->world_age) { jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + sz); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.malloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.malloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + sz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.malloc) + 1); jl_batch_accum_heap_size(ptls, sz); } return data; @@ -3702,10 +3659,10 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz) if (data != NULL && pgcstack != NULL && ct->world_age) { jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + nm*sz); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.malloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.malloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + nm*sz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.malloc) + 1); jl_batch_accum_heap_size(ptls, sz * nm); } return data; @@ -3730,10 +3687,10 @@ JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); if (!(sz < old)) - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + (sz - old)); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.realloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.realloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + (sz - old)); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.realloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.realloc) + 1); int64_t diff = sz - old; if (diff < 0) { @@ -3746,63 +3703,6 @@ JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size return data; } -// allocation wrappers that save the size of allocations, to allow using -// jl_gc_counted_* functions with a libc-compatible API. - -JL_DLLEXPORT void *jl_malloc(size_t sz) -{ - int64_t *p = (int64_t *)jl_gc_counted_malloc(sz + JL_SMALL_BYTE_ALIGNMENT); - if (p == NULL) - return NULL; - p[0] = sz; - return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 -} - -//_unchecked_calloc does not check for potential overflow of nm*sz -STATIC_INLINE void *_unchecked_calloc(size_t nm, size_t sz) { - size_t nmsz = nm*sz; - int64_t *p = (int64_t *)jl_gc_counted_calloc(nmsz + JL_SMALL_BYTE_ALIGNMENT, 1); - if (p == NULL) - return NULL; - p[0] = nmsz; - return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 -} - -JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz) -{ - if (nm > SSIZE_MAX/sz - JL_SMALL_BYTE_ALIGNMENT) - return NULL; - return _unchecked_calloc(nm, sz); -} - -JL_DLLEXPORT void jl_free(void *p) -{ - if (p != NULL) { - int64_t *pp = (int64_t *)p - 2; - size_t sz = pp[0]; - jl_gc_counted_free_with_size(pp, sz + JL_SMALL_BYTE_ALIGNMENT); - } -} - -JL_DLLEXPORT void *jl_realloc(void *p, size_t sz) -{ - int64_t *pp; - size_t szold; - if (p == NULL) { - pp = NULL; - szold = 0; - } - else { - pp = (int64_t *)p - 2; - szold = pp[0] + JL_SMALL_BYTE_ALIGNMENT; - } - int64_t *pnew = (int64_t *)jl_gc_counted_realloc_with_old_size(pp, szold, sz + JL_SMALL_BYTE_ALIGNMENT); - if (pnew == NULL) - return NULL; - pnew[0] = sz; - return (void *)(pnew + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16 -} - // allocating blocks for Arrays and Strings JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz) @@ -3821,10 +3721,10 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz) if (b == NULL) jl_throw(jl_memory_exception); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.allocd, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.allocd) + allocsz); - jl_atomic_store_relaxed(&ptls->gc_tls.gc_num.malloc, - jl_atomic_load_relaxed(&ptls->gc_tls.gc_num.malloc) + 1); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.allocd) + allocsz); + jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_tls_common.gc_num.malloc) + 1); jl_batch_accum_heap_size(ptls, allocsz); #ifdef _OS_WINDOWS_ SetLastError(last_error); @@ -3936,18 +3836,6 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT return jl_valueof(o); } -JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value) -{ - jl_ptls_t ptls = jl_current_task->ptls; - return jl_gc_new_weakref_th(ptls, value); -} - -JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz) -{ - jl_ptls_t ptls = jl_current_task->ptls; - return jl_gc_alloc(ptls, sz, NULL); -} - JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void) { if (jl_is_initialized()) { @@ -4075,11 +3963,6 @@ JL_DLLEXPORT size_t jl_gc_external_obj_hdr_size(void) } -JL_DLLEXPORT void * jl_gc_alloc_typed(jl_ptls_t ptls, size_t sz, void *ty) -{ - return jl_gc_alloc(ptls, sz, ty); -} - JL_DLLEXPORT void jl_gc_schedule_foreign_sweepfunc(jl_ptls_t ptls, jl_value_t *obj) { arraylist_push(&ptls->gc_tls.sweep_objs, obj); diff --git a/src/gc-stock.h b/src/gc-stock.h index 76cecf68067bf..b9a2e720f120a 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -106,12 +106,6 @@ JL_EXTENSION typedef struct _bigval_t { // must be 64-byte aligned here, in 32 & 64 bit modes } bigval_t; -// data structure for tracking malloc'd genericmemory. -typedef struct _mallocmemory_t { - jl_genericmemory_t *a; // lowest bit is tagged if this is aligned memory - struct _mallocmemory_t *next; -} mallocmemory_t; - // pool page metadata typedef struct _jl_gc_pagemeta_t { // next metadata structure in per-thread list diff --git a/src/gc-tls-common.h b/src/gc-tls-common.h new file mode 100644 index 0000000000000..ba36f5c1c238e --- /dev/null +++ b/src/gc-tls-common.h @@ -0,0 +1,52 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +// Meant to be included in "julia_threads.h" +#ifndef JL_GC_TLS_COMMON_H +#define JL_GC_TLS_COMMON_H + +#include "julia_atomics.h" + +// GC threading ------------------------------------------------------------------ + +#include "arraylist.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // variable for tracking weak references + small_arraylist_t weak_refs; + // live tasks started on this thread + // that are holding onto a stack from the pool + small_arraylist_t live_tasks; + + // variables for tracking malloc'd arrays + struct _mallocmemory_t *mallocarrays; + struct _mallocmemory_t *mafreelist; + +#define JL_N_STACK_POOLS 16 + small_arraylist_t free_stacks[JL_N_STACK_POOLS]; +} jl_thread_heap_common_t; + +typedef struct { + _Atomic(int64_t) allocd; + _Atomic(int64_t) pool_live_bytes; + _Atomic(uint64_t) malloc; + _Atomic(uint64_t) realloc; + _Atomic(uint64_t) poolalloc; + _Atomic(uint64_t) bigalloc; + _Atomic(int64_t) free_acc; + _Atomic(uint64_t) alloc_acc; +} jl_thread_gc_num_common_t; + +typedef struct { + jl_thread_heap_common_t heap; + jl_thread_gc_num_common_t gc_num; +} jl_gc_tls_states_common_t; + +#ifdef __cplusplus +} +#endif + +#endif // JL_GC_TLS_COMMON_H diff --git a/src/gc-tls.h b/src/gc-tls.h index 3c2cc029a6183..d82506383c501 100644 --- a/src/gc-tls.h +++ b/src/gc-tls.h @@ -21,16 +21,6 @@ typedef struct { } jl_gc_pool_t; typedef struct { - // variable for tracking weak references - small_arraylist_t weak_refs; - // live tasks started on this thread - // that are holding onto a stack from the pool - small_arraylist_t live_tasks; - - // variables for tracking malloc'd arrays - struct _mallocmemory_t *mallocarrays; - struct _mallocmemory_t *mafreelist; - // variable for tracking young (i.e. not in `GC_OLD_MARKED`/last generation) large objects struct _bigval_t *young_generation_of_bigvals; @@ -42,22 +32,8 @@ typedef struct { // variables for allocating objects from pools #define JL_GC_N_MAX_POOLS 51 // conservative. must be kept in sync with `src/julia_internal.h` jl_gc_pool_t norm_pools[JL_GC_N_MAX_POOLS]; - -#define JL_N_STACK_POOLS 16 - small_arraylist_t free_stacks[JL_N_STACK_POOLS]; } jl_thread_heap_t; -typedef struct { - _Atomic(int64_t) allocd; - _Atomic(int64_t) pool_live_bytes; - _Atomic(uint64_t) malloc; - _Atomic(uint64_t) realloc; - _Atomic(uint64_t) poolalloc; - _Atomic(uint64_t) bigalloc; - _Atomic(int64_t) free_acc; - _Atomic(uint64_t) alloc_acc; -} jl_thread_gc_num_t; - typedef struct { ws_queue_t chunk_queue; ws_queue_t ptr_queue; @@ -78,7 +54,6 @@ typedef struct { typedef struct { jl_thread_heap_t heap; jl_gc_page_stack_t page_metadata_allocd; - jl_thread_gc_num_t gc_num; jl_gc_markqueue_t mark_queue; jl_gc_mark_cache_t gc_cache; _Atomic(size_t) gc_sweeps_requested; diff --git a/src/julia_internal.h b/src/julia_internal.h index bb8169c6e5f9e..4f735029da444 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -372,6 +372,8 @@ extern jl_function_t *jl_typeinf_func JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT size_t jl_typeinf_world; extern _Atomic(jl_typemap_entry_t*) call_cache[N_CALL_CACHE] JL_GLOBALLY_ROOTED; +void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT; + JL_DLLEXPORT extern int jl_lineno; JL_DLLEXPORT extern const char *jl_filename; @@ -518,30 +520,6 @@ 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; - const size_t allocsz = sz + sizeof(jl_taggedvalue_t); - if (sz <= GC_MAX_SZCLASS) { - int pool_id = jl_gc_szclass(allocsz); - jl_gc_pool_t *p = &ptls->gc_tls.heap.norm_pools[pool_id]; - int osize = jl_gc_sizeclasses[pool_id]; - // We call `jl_gc_small_alloc_noinline` instead of `jl_gc_small_alloc` to avoid double-counting in - // the Allocations Profiler. (See https://github.com/JuliaLang/julia/pull/43868 for more details.) - v = jl_gc_small_alloc_noinline(ptls, (char*)p - (char*)ptls, osize); - } - else { - if (allocsz < sz) // overflow in adding offs, size was "negative" - jl_throw(jl_memory_exception); - v = jl_gc_big_alloc_noinline(ptls, allocsz); - } - jl_set_typeof(v, ty); - maybe_record_alloc_to_profile(v, sz, (jl_datatype_t*)ty); - return v; -} - /* Programming style note: When using jl_gc_alloc, do not JL_GC_PUSH it into a * gc frame, until it has been fully initialized. An uninitialized value in a * gc frame can crash upon encountering the first safepoint. By delaying use of diff --git a/src/julia_threads.h b/src/julia_threads.h index 17e8d7d466044..67da2978b4267 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -5,6 +5,7 @@ #define JL_THREADS_H #include "gc-tls.h" +#include "gc-tls-common.h" #include "julia_atomics.h" #ifndef _OS_WINDOWS_ #include "pthread.h" @@ -155,6 +156,7 @@ typedef struct _jl_tls_states_t { // Counter to disable finalizer **on the current thread** int finalizers_inhibited; jl_gc_tls_states_t gc_tls; // this is very large, and the offset of the first member is baked into codegen + jl_gc_tls_states_common_t gc_tls_common; // common tls for both GCs volatile sig_atomic_t defer_signal; _Atomic(struct _jl_task_t*) current_task; struct _jl_task_t *next_task; diff --git a/src/scheduler.c b/src/scheduler.c index bb2f85b52283f..7e23f654c2566 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -80,10 +80,6 @@ JL_DLLEXPORT int jl_set_task_threadpoolid(jl_task_t *task, int8_t tpid) JL_NOTSA return 1; } -// GC functions used -extern int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, - jl_gc_markqueue_t *mq, jl_value_t *obj) JL_NOTSAFEPOINT; - // initialize the threading infrastructure // (called only by the main thread) void jl_init_threadinginfra(void) diff --git a/src/stackwalk.c b/src/stackwalk.c index 7c6f946fe73c5..770daa8bf17a6 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -5,7 +5,7 @@ utilities for walking the stack and looking up information about code addresses */ #include -#include "gc-stock.h" +#include "gc-common.h" #include "julia.h" #include "julia_internal.h" #include "threading.h" @@ -1340,18 +1340,14 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); for (size_t i = 0; i < nthreads; i++) { jl_ptls_t ptls2 = allstates[i]; - if (gc_is_parallel_collector_thread(i)) { - jl_safe_printf("==== Skipping backtrace for parallel GC thread %zu\n", i + 1); - continue; - } - if (gc_is_concurrent_collector_thread(i)) { - jl_safe_printf("==== Skipping backtrace for concurrent GC thread %zu\n", i + 1); + if (gc_is_collector_thread(i)) { + jl_safe_printf("==== Skipping backtrace for parallel/concurrent GC thread %zu\n", i + 1); continue; } if (ptls2 == NULL) { continue; } - small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; + small_arraylist_t *live_tasks = &ptls2->gc_tls_common.heap.live_tasks; size_t n = mtarraylist_length(live_tasks); int t_state = JL_TASK_STATE_DONE; jl_task_t *t = ptls2->root_task; From e08280a24fba1b5f1ecf5dce7d8b974d880dae5a Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sat, 19 Oct 2024 21:04:19 -0400 Subject: [PATCH 445/548] Few more tests for AbstractChar (#56249) --- test/char.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/char.jl b/test/char.jl index 3100add0e81c5..5523125529031 100644 --- a/test/char.jl +++ b/test/char.jl @@ -288,6 +288,10 @@ Base.codepoint(c::ASCIIChar) = reinterpret(UInt8, c) @test string(ASCIIChar('x')) == "x" @test length(ASCIIChar('x')) == 1 @test !isempty(ASCIIChar('x')) + @test ndims(ASCIIChar('x')) == 0 + @test ndims(ASCIIChar) == 0 + @test firstindex(ASCIIChar('x')) == 1 + @test lastindex(ASCIIChar('x')) == 1 @test eltype(ASCIIChar) == ASCIIChar @test_throws MethodError write(IOBuffer(), ASCIIChar('x')) @test_throws MethodError read(IOBuffer('x'), ASCIIChar) From 1fd7ada972911c181750d104604487a35ae3bea9 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 20 Oct 2024 13:12:44 -0400 Subject: [PATCH 446/548] REPL: run repl hint generation for modeswitch chars when not switching (#56251) Fixes https://github.com/JuliaLang/julia/issues/56003 --- stdlib/REPL/src/REPL.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index b8f850c3e9ff9..ac791327e2d75 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1431,6 +1431,7 @@ function setup_interface( end else edit_insert(s, ';') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, '?' => function (s::MIState,o...) @@ -1441,6 +1442,7 @@ function setup_interface( end else edit_insert(s, '?') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, ']' => function (s::MIState,o...) @@ -1477,6 +1479,7 @@ function setup_interface( Base.errormonitor(t_replswitch) else edit_insert(s, ']') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, From a4a4b954450467d40e6d4dc22b17269da1eb5337 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:01:52 -0400 Subject: [PATCH 447/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=2027c1b1ee5=20to=20799dc2d54=20(#56257)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: 27c1b1ee5 New commit: 799dc2d54 Julia version: 1.12.0-DEV Pkg version: 1.12.0 Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/27c1b1ee5cf15571eb5e54707e812d646ac1dde3...799dc2d54c4e809b9779de8c604564a5b3befaa0 ``` $ git log --oneline 27c1b1ee5..799dc2d54 799dc2d54 REPLExt: use Base.isaccessibledir rather than isdir in completions (#4053) 3fde94ee9 REPLExt: run repl hint generation for modeswitch chars when not switching (#4054) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 | 1 - .../Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 | 1 - .../Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 | 1 + .../Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 create mode 100644 deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 diff --git a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 deleted file mode 100644 index 137460d1a05a1..0000000000000 --- a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -74d656c054c1406a7e88910d673019f7 diff --git a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 b/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 deleted file mode 100644 index 0b8463176a867..0000000000000 --- a/deps/checksums/Pkg-27c1b1ee5cf15571eb5e54707e812d646ac1dde3.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a8e589ce68cc14883a7a21f68862695bfaa9ab38dfa0e704c32aaa801667708af0d851a41199ad09ae81a4c0b928befb680d639c1eca3377ce2db2dcc34b98e5 diff --git a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 new file mode 100644 index 0000000000000..7c0bfbf62bd6e --- /dev/null +++ b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 @@ -0,0 +1 @@ +6fce8506a1701acdcbc4888250eeb86a diff --git a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 new file mode 100644 index 0000000000000..06e3ea9c8dfa7 --- /dev/null +++ b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 @@ -0,0 +1 @@ +e251745da221a82f3ec5e21a76c29df0b695dc4028ee2c719373c08637050318db7b543c9d40074314fc3495738d39fd8af5a7954e8b72695df44e25e395f883 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 470acefbc6c83..c29c83fce4046 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 27c1b1ee5cf15571eb5e54707e812d646ac1dde3 +PKG_SHA1 = 799dc2d54c4e809b9779de8c604564a5b3befaa0 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From fee8090fa86fb0529ae59fe3e3d339c60e2b90a8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 21 Oct 2024 02:45:47 -0300 Subject: [PATCH 448/548] Make isbitstypes use memmove instead of the runtime function in copyto! (#56237) This might help llvm understand whats going on. Also enzyme really wants this to look like this to trace through it better. --------- Co-authored-by: Jameson Nash --- base/genericmemory.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 89861444d9652..de1fc668333f5 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -118,7 +118,17 @@ function unsafe_copyto!(dest::MemoryRef{T}, src::MemoryRef{T}, n) where {T} @_terminates_globally_notaskstate_meta n == 0 && return dest @boundscheck memoryref(dest, n), memoryref(src, n) - ccall(:jl_genericmemory_copyto, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), dest.mem, dest.ptr_or_offset, src.mem, src.ptr_or_offset, Int(n)) + if isbitstype(T) + tdest = @_gc_preserve_begin dest + tsrc = @_gc_preserve_begin src + pdest = unsafe_convert(Ptr{Cvoid}, dest) + psrc = unsafe_convert(Ptr{Cvoid}, src) + memmove(pdest, psrc, aligned_sizeof(T) * n) + @_gc_preserve_end tdest + @_gc_preserve_end tsrc + else + ccall(:jl_genericmemory_copyto, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), dest.mem, dest.ptr_or_offset, src.mem, src.ptr_or_offset, Int(n)) + end return dest end From b01095e0274bf078e162379a9f243197710053ff Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 11:28:56 +0530 Subject: [PATCH 449/548] Fix kron indexing for types without a unique zero (#56229) This fixes a bug introduced in https://github.com/JuliaLang/julia/pull/55941. We may also take this opportunity to limit the scope of the `@inbounds` annotations, and also use `axes` to compute the bounds instead of hard-coding them. The real "fix" here is on line 767, where `l in 1:nA` should have been `l in 1:mB`. Using `axes` avoids such errors, and makes the operation safer as well. --- stdlib/LinearAlgebra/src/diagonal.jl | 50 +++++++++++++-------------- stdlib/LinearAlgebra/test/diagonal.jl | 4 +-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 17ff232f5b262..6e8ce96259fc1 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -700,16 +700,16 @@ end zerofilled = true end end - @inbounds for i = 1:nA, j = 1:nB + for i in eachindex(valA), j in eachindex(valB) idx = (i-1)*nB+j - C[idx, idx] = valA[i] * valB[j] + @inbounds C[idx, idx] = valA[i] * valB[j] end if !zerofilled - for j in 1:nA, i in 1:mA + for j in axes(A,2), i in axes(A,1) Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:mB + for k in axes(B,2), l in axes(B,1) i == j && k == l && continue - C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] + @inbounds C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] end end end @@ -749,24 +749,24 @@ end end end m = 1 - @inbounds for j = 1:nA - A_jj = A[j,j] - for k = 1:nB - for l = 1:mB - C[m] = A_jj * B[l,k] + for j in axes(A,2) + A_jj = @inbounds A[j,j] + for k in axes(B,2) + for l in axes(B,1) + @inbounds C[m] = A_jj * B[l,k] m += 1 end m += (nA - 1) * mB end if !zerofilled # populate the zero elements - for i in 1:mA + for i in axes(A,1) i == j && continue - A_ij = A[i, j] + A_ij = @inbounds A[i, j] Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:nA - B_lk = B[l, k] - C[Δrow + l, Δcol + k] = A_ij * B_lk + for k in axes(B,2), l in axes(B,1) + B_lk = @inbounds B[l, k] + @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk end end end @@ -792,23 +792,23 @@ end end end m = 1 - @inbounds for j = 1:nA - for l = 1:mB - Bll = B[l,l] - for i = 1:mA - C[m] = A[i,j] * Bll + for j in axes(A,2) + for l in axes(B,1) + Bll = @inbounds B[l,l] + for i in axes(A,1) + @inbounds C[m] = A[i,j] * Bll m += nB end m += 1 end if !zerofilled - for i in 1:mA - A_ij = A[i, j] + for i in axes(A,1) + A_ij = @inbounds A[i, j] Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:mB + for k in axes(B,2), l in axes(B,1) l == k && continue - B_lk = B[l, k] - C[Δrow + l, Δcol + k] = A_ij * B_lk + B_lk = @inbounds B[l, k] + @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk end end end diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 8b56ee15e56e3..1c3a9dfa676ac 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -353,7 +353,7 @@ Random.seed!(1) D3 = Diagonal(convert(Vector{elty}, rand(n÷2))) DM3= Matrix(D3) @test Matrix(kron(D, D3)) ≈ kron(DM, DM3) - M4 = rand(elty, n÷2, n÷2) + M4 = rand(elty, size(D3,1) + 1, size(D3,2) + 2) # choose a different size from D3 @test kron(D3, M4) ≈ kron(DM3, M4) @test kron(M4, D3) ≈ kron(M4, DM3) X = [ones(1,1) for i in 1:2, j in 1:2] @@ -1392,7 +1392,7 @@ end end @testset "zeros in kron with block matrices" begin - D = Diagonal(1:2) + D = Diagonal(1:4) B = reshape([ones(2,2), ones(3,2), ones(2,3), ones(3,3)], 2, 2) @test kron(D, B) == kron(Array(D), B) @test kron(B, D) == kron(B, Array(D)) From 04259daf8a339f99d7cd8503d9c1f154b247e4e1 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 11:31:36 +0530 Subject: [PATCH 450/548] Reroute` (Upper/Lower)Triangular * Diagonal` through `__muldiag` (#55984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, `::Diagonal * ::AbstractMatrix` calls the method `LinearAlgebra.__muldiag!` in general that scales the rows, and similarly for the diagonal on the right. The implementation of `__muldiag` was duplicating the logic in `LinearAlgebra.modify!` and the methods for `MulAddMul`. This PR replaces the various branches with calls to `modify!` instead. I've also extracted the multiplication logic into its own function `__muldiag_nonzeroalpha!` so that this may be specialized for matrix types, such as triangular ones. Secondly, `::Diagonal * ::UpperTriangular` (and similarly, other triangular matrices) was specialized to forward the multiplication to the parent of the triangular. For strided matrices, however, it makes more sense to use the structure and scale only the filled half of the matrix. Firstly, this improves performance, and secondly, this avoids errors in case the parent isn't fully initialized corresponding to the structural zero elements. Performance improvement: ```julia julia> D = Diagonal(1:400); julia> U = UpperTriangular(zeros(size(D))); julia> @btime $D * $U; 314.944 μs (3 allocations: 1.22 MiB) # v"1.12.0-DEV.1288" 195.960 μs (3 allocations: 1.22 MiB) # This PR ``` Fix: ```julia julia> M = Matrix{BigFloat}(undef, 2, 2); julia> M[1,1] = M[2,2] = M[1,2] = 3; julia> U = UpperTriangular(M) 2×2 UpperTriangular{BigFloat, Matrix{BigFloat}}: 3.0 3.0 ⋅ 3.0 julia> D = Diagonal(1:2); julia> U * D # works after this PR 2×2 UpperTriangular{BigFloat, Matrix{BigFloat}}: 3.0 6.0 ⋅ 6.0 ``` --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 + stdlib/LinearAlgebra/src/diagonal.jl | 158 +++++++++++++--------- stdlib/LinearAlgebra/test/diagonal.jl | 40 +++++- 3 files changed, 134 insertions(+), 66 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 15354603943c2..88fc3476c9d7f 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -655,6 +655,8 @@ matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = _matprod_dest_diag(A, TS) matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = _matprod_dest_diag(B, TS) matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) +_matprod_dest_diag(A::UnitUpperTriangular, TS) = UpperTriangular(similar(parent(A), TS)) +_matprod_dest_diag(A::UnitLowerTriangular, TS) = LowerTriangular(similar(parent(A), TS)) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) ev = similar(A, TS, max(0, n-1)) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 6e8ce96259fc1..8ba4c3d457e83 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -396,82 +396,120 @@ function lmul!(D::Diagonal, T::Tridiagonal) return T end -function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} +@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B, _add::MulAddMul) + @inbounds for j in axes(B, 2) + @simd for i in axes(B, 1) + _modify!(_add, D.diag[i] * B[i,j], out, (i,j)) + end + end + out +end +_maybe_unwrap_tri(out, A) = out, A +_maybe_unwrap_tri(out::UpperTriangular, A::UpperOrUnitUpperTriangular) = parent(out), parent(A) +_maybe_unwrap_tri(out::LowerTriangular, A::LowerOrUnitLowerTriangular) = parent(out), parent(A) +@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B::UpperOrLowerTriangular, _add::MulAddMul) + isunit = B isa Union{UnitUpperTriangular, UnitLowerTriangular} + # if both B and out have the same upper/lower triangular structure, + # we may directly read and write from the parents + out_maybeparent, B_maybeparent = _maybe_unwrap_tri(out, B) + for j in axes(B, 2) + if isunit + _modify!(_add, D.diag[j] * B[j,j], out, (j,j)) + end + rowrange = B isa UpperOrUnitUpperTriangular ? (1:min(j-isunit, size(B,1))) : (j+isunit:size(B,1)) + @inbounds @simd for i in rowrange + _modify!(_add, D.diag[i] * B_maybeparent[i,j], out_maybeparent, (i,j)) + end + end + out +end +function __muldiag!(out, D::Diagonal, B, _add::MulAddMul) require_one_based_indexing(out, B) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - if bis0 - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - out[i,j] = D.diag[i] * B[i,j] * alpha - end - end - else - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta - end - end - end + __muldiag_nonzeroalpha!(out, D, B, _add) end return out end -function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + +@inline function __muldiag_nonzeroalpha!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + beta = _add.beta + _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) + @inbounds for j in axes(A, 2) + dja = _add(D.diag[j]) + @simd for i in axes(A, 1) + _modify!(_add_aisone, A[i,j] * dja, out, (i,j)) + end + end + out +end +@inline function __muldiag_nonzeroalpha!(out, A::UpperOrLowerTriangular, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + isunit = A isa Union{UnitUpperTriangular, UnitLowerTriangular} + beta = _add.beta + # since alpha is multiplied to the diagonal element of D, + # we may skip alpha in the second multiplication by setting ais1 to true + _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) + # if both A and out have the same upper/lower triangular structure, + # we may directly read and write from the parents + out_maybeparent, A_maybeparent = _maybe_unwrap_tri(out, A) + @inbounds for j in axes(A, 2) + dja = _add(D.diag[j]) + if isunit + _modify!(_add_aisone, A[j,j] * dja, out, (j,j)) + end + rowrange = A isa UpperOrUnitUpperTriangular ? (1:min(j-isunit, size(A,1))) : (j+isunit:size(A,1)) + @simd for i in rowrange + _modify!(_add_aisone, A_maybeparent[i,j] * dja, out_maybeparent, (i,j)) + end + end + out +end +function __muldiag!(out, A, D::Diagonal, _add::MulAddMul) require_one_based_indexing(out, A) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - if bis0 - @inbounds for j in axes(A, 2) - dja = D.diag[j] * alpha - @simd for i in axes(A, 1) - out[i,j] = A[i,j] * dja - end - end - else - @inbounds for j in axes(A, 2) - dja = D.diag[j] * alpha - @simd for i in axes(A, 1) - out[i,j] = A[i,j] * dja + out[i,j] * beta - end - end - end + __muldiag_nonzeroalpha!(out, A, D, _add) end return out end -function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + +@inline function __muldiag_nonzeroalpha!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul) d1 = D1.diag d2 = D2.diag + outd = out.diag + @inbounds @simd for i in eachindex(d1, d2, outd) + _modify!(_add, d1[i] * d2[i], outd, i) + end + out +end +function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out.diag, beta) else - if bis0 - @inbounds @simd for i in eachindex(out.diag) - out.diag[i] = d1[i] * d2[i] * alpha - end - else - @inbounds @simd for i in eachindex(out.diag) - out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta - end - end + __muldiag_nonzeroalpha!(out, D1, D2, _add) end return out end -function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - require_one_based_indexing(out) - alpha, beta = _add.alpha, _add.beta - mA = size(D1, 1) +@inline function __muldiag_nonzeroalpha!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul) d1 = D1.diag d2 = D2.diag + @inbounds @simd for i in eachindex(d1, d2) + _modify!(_add, d1[i] * d2[i], out, (i,i)) + end + out +end +function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1}) where {ais1} + require_one_based_indexing(out) + alpha, beta = _add.alpha, _add.beta _rmul_or_fill!(out, beta) if !iszero(alpha) - @inbounds @simd for i in 1:mA - out[i,i] += d1[i] * d2[i] * alpha - end + _add_bis1 = MulAddMul{ais1,false,typeof(alpha),Bool}(alpha,true) + __muldiag_nonzeroalpha!(out, D1, D2, _add_bis1) end return out end @@ -658,31 +696,21 @@ for Tri in (:UpperTriangular, :LowerTriangular) @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D)) @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag)) end + @eval *(A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = + @invoke *(A::AbstractMatrix, D::Diagonal) + @eval *(A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = + @invoke *(A::AbstractMatrix, D::Diagonal) for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv)) @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data)) @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag)) end + @eval *(D::Diagonal, A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}) = + @invoke *(D::Diagonal, A::AbstractMatrix) + @eval *(D::Diagonal, A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}) = + @invoke *(D::Diagonal, A::AbstractMatrix) # 3-arg ldiv! @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) - # 3-arg mul! is disambiguated in special.jl - # 5-arg mul! - @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - α, β = _add.alpha, _add.beta - iszero(α) && return _rmul_or_fill!(C, β) - diag′ = bis0 ? nothing : diag(C) - data = mul!(C.data, D, A.data, α, β) - $Tri(_setdiag!(data, _add, D.diag, diag′)) - end - @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - α, β = _add.alpha, _add.beta - iszero(α) && return _rmul_or_fill!(C, β) - diag′ = bis0 ? nothing : diag(C) - data = mul!(C.data, A.data, D, α, β) - $Tri(_setdiag!(data, _add, D.diag, diag′)) - end end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 1c3a9dfa676ac..380a0465028d1 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1188,7 +1188,7 @@ end @test oneunit(D3) isa typeof(D3) end -@testset "AbstractTriangular" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) +@testset "$Tri" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) A = randn(4, 4) TriA = Tri(A) UTriA = UTri(A) @@ -1218,6 +1218,44 @@ end @test outTri === mul!(outTri, D, UTriA, 2, 1)::Tri == mul!(out, D, Matrix(UTriA), 2, 1) @test outTri === mul!(outTri, TriA, D, 2, 1)::Tri == mul!(out, Matrix(TriA), D, 2, 1) @test outTri === mul!(outTri, UTriA, D, 2, 1)::Tri == mul!(out, Matrix(UTriA), D, 2, 1) + + # we may write to a Unit triangular if the diagonal is preserved + ID = Diagonal(ones(size(UTriA,2))) + @test mul!(copy(UTriA), UTriA, ID) == UTriA + @test mul!(copy(UTriA), ID, UTriA) == UTriA + + @testset "partly filled parents" begin + M = Matrix{BigFloat}(undef, 2, 2) + M[1,1] = M[2,2] = 3 + isupper = Tri == UpperTriangular + M[1+!isupper, 1+isupper] = 3 + D = Diagonal(1:2) + T = Tri(M) + TA = Array(T) + @test T * D == TA * D + @test D * T == D * TA + @test mul!(copy(T), T, D, 2, 3) == 2T * D + 3T + @test mul!(copy(T), D, T, 2, 3) == 2D * T + 3T + + U = UTri(M) + UA = Array(U) + @test U * D == UA * D + @test D * U == D * UA + @test mul!(copy(T), U, D, 2, 3) == 2 * UA * D + 3TA + @test mul!(copy(T), D, U, 2, 3) == 2 * D * UA + 3TA + + M2 = Matrix{BigFloat}(undef, 2, 2) + M2[1+!isupper, 1+isupper] = 3 + U = UTri(M2) + UA = Array(U) + @test U * D == UA * D + @test D * U == D * UA + ID = Diagonal(ones(size(U,2))) + @test mul!(copy(U), U, ID) == U + @test mul!(copy(U), ID, U) == U + @test mul!(copy(U), U, ID, 2, -1) == U + @test mul!(copy(U), ID, U, 2, -1) == U + end end struct SMatrix1{T} <: AbstractArray{T,2} From e3f2f6b9c69293da68c1019366f8397d2049c6d8 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 18 Oct 2024 14:42:23 +0200 Subject: [PATCH 451/548] Revert "Extensions: make loading of extensions independent of what packages are in the sysimage (#52841)" This reverts commit 08d229f4a7cb0c3ef5becddd1b3bc4f8f178b8e4. --- base/Base.jl | 1 - base/loading.jl | 9 +++------ test/loading.jl | 19 ------------------- .../HasDepWithExtensions.jl/Manifest.toml | 7 +------ .../Extensions/HasExtensions.jl/Project.toml | 2 -- .../HasExtensions.jl/ext/LinearAlgebraExt.jl | 3 --- 6 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 test/project/Extensions/HasExtensions.jl/ext/LinearAlgebraExt.jl diff --git a/base/Base.jl b/base/Base.jl index 9800462f855f9..5fb764bd4cc01 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -646,7 +646,6 @@ function __init__() init_load_path() init_active_project() append!(empty!(_sysimage_modules), keys(loaded_modules)) - empty!(explicit_loaded_modules) empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty for (mod, key) in module_keys push!(get!(Vector{Module}, loaded_precompiles, key), mod) diff --git a/base/loading.jl b/base/loading.jl index db6a681bb2a5b..19fcaba388d11 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1554,7 +1554,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} # TODO: Better error message if this lookup fails? uuid_trigger = UUID(totaldeps[trigger]::String) trigger_id = PkgId(uuid_trigger, trigger) - if !haskey(explicit_loaded_modules, trigger_id) || haskey(package_locks, trigger_id) + if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id) push!(trigger1, gid) else @@ -2430,9 +2430,8 @@ function __require_prelocked(uuidkey::PkgId, env=nothing) insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) - elseif !haskey(explicit_loaded_modules, uuidkey) - explicit_loaded_modules[uuidkey] = m - run_package_callbacks(uuidkey) + else + newm = root_module(uuidkey) end return m end @@ -2445,7 +2444,6 @@ end PkgOrigin() = PkgOrigin(nothing, nothing, nothing) const pkgorigins = Dict{PkgId,PkgOrigin}() -const explicit_loaded_modules = Dict{PkgId,Module}() # Emptied on Julia start const loaded_modules = Dict{PkgId,Module}() # available to be explicitly loaded const loaded_precompiles = Dict{PkgId,Vector{Module}}() # extended (complete) list of modules, available to be loaded const loaded_modules_order = Vector{Module}() @@ -2485,7 +2483,6 @@ end end maybe_loaded_precompile(key, module_build_id(m)) === nothing && push!(loaded_modules_order, m) loaded_modules[key] = m - explicit_loaded_modules[key] = m module_keys[m] = key end nothing diff --git a/test/loading.jl b/test/loading.jl index ec4a0391a412a..ecba64ca45a73 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1129,25 +1129,6 @@ end run(cmd_proj_ext) end - # Sysimage extensions - # The test below requires that LinearAlgebra is in the sysimage and that it has not been loaded yet. - # if it gets moved out, this test will need to be updated. - # We run this test in a new process so we are not vulnerable to a previous test having loaded LinearAlgebra - sysimg_ext_test_code = """ - uuid_key = Base.PkgId(Base.UUID("37e2e46d-f89d-539d-b4ee-838fcccc9c8e"), "LinearAlgebra") - Base.in_sysimage(uuid_key) || error("LinearAlgebra not in sysimage") - haskey(Base.explicit_loaded_modules, uuid_key) && error("LinearAlgebra already loaded") - using HasExtensions - Base.get_extension(HasExtensions, :LinearAlgebraExt) === nothing || error("unexpectedly got an extension") - using LinearAlgebra - haskey(Base.explicit_loaded_modules, uuid_key) || error("LinearAlgebra not loaded") - Base.get_extension(HasExtensions, :LinearAlgebraExt) isa Module || error("expected extension to load") - """ - cmd = `$(Base.julia_cmd()) --startup-file=no -e $sysimg_ext_test_code` - cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([proj, "@stdlib"], sep)) - run(cmd) - - # Extensions in implicit environments old_load_path = copy(LOAD_PATH) try diff --git a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml index 5706aba59d1e0..f659a59e0910b 100644 --- a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml +++ b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml @@ -25,17 +25,12 @@ deps = ["ExtDep3"] path = "../HasExtensions.jl" uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" version = "0.1.0" +weakdeps = ["ExtDep", "ExtDep2"] [deps.HasExtensions.extensions] Extension = "ExtDep" ExtensionDep = "ExtDep3" ExtensionFolder = ["ExtDep", "ExtDep2"] - LinearAlgebraExt = "LinearAlgebra" - - [deps.HasExtensions.weakdeps] - ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" - ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" - LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[deps.SomePackage]] path = "../SomePackage" diff --git a/test/project/Extensions/HasExtensions.jl/Project.toml b/test/project/Extensions/HasExtensions.jl/Project.toml index fe21a1423f543..a02f5662d602d 100644 --- a/test/project/Extensions/HasExtensions.jl/Project.toml +++ b/test/project/Extensions/HasExtensions.jl/Project.toml @@ -8,10 +8,8 @@ ExtDep3 = "a5541f1e-a556-4fdc-af15-097880d743a1" [weakdeps] ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [extensions] Extension = "ExtDep" ExtensionDep = "ExtDep3" ExtensionFolder = ["ExtDep", "ExtDep2"] -LinearAlgebraExt = "LinearAlgebra" diff --git a/test/project/Extensions/HasExtensions.jl/ext/LinearAlgebraExt.jl b/test/project/Extensions/HasExtensions.jl/ext/LinearAlgebraExt.jl deleted file mode 100644 index 19f87cb849417..0000000000000 --- a/test/project/Extensions/HasExtensions.jl/ext/LinearAlgebraExt.jl +++ /dev/null @@ -1,3 +0,0 @@ -module LinearAlgebraExt - -end From ad1dc390e3123b65433ee06a651ca6de88c29914 Mon Sep 17 00:00:00 2001 From: KristofferC Date: Mon, 21 Oct 2024 14:18:48 +0200 Subject: [PATCH 452/548] fix lookup when extension is in `[deps]` --- base/loading.jl | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 19fcaba388d11..e08d03ad513c7 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -974,14 +974,14 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St entry = entry::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{String, Nothing} uuid === nothing && continue + # deps is either a list of names (deps = ["DepA", "DepB"]) or + # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."} + deps = get(entry, "deps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} if UUID(uuid) === where.uuid found_where = true - # deps is either a list of names (deps = ["DepA", "DepB"]) or - # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."} - deps = get(entry, "deps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} if deps isa Vector{String} found_name = name in deps - break + found_name && @goto done elseif deps isa Dict{String, Any} deps = deps::Dict{String, Any} for (dep, uuid) in deps @@ -1000,23 +1000,25 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St return PkgId(UUID(uuid), name) end exts = extensions[where.name]::Union{String, Vector{String}} + weakdeps = get(entry, "weakdeps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} if (exts isa String && name == exts) || (exts isa Vector{String} && name in exts) - weakdeps = get(entry, "weakdeps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} - if weakdeps !== nothing - if weakdeps isa Vector{String} - found_name = name in weakdeps - break - elseif weakdeps isa Dict{String, Any} - weakdeps = weakdeps::Dict{String, Any} - for (dep, uuid) in weakdeps - uuid::String - if dep === name - return PkgId(UUID(uuid), name) + for deps′ in [weakdeps, deps] + if deps′ !== nothing + if deps′ isa Vector{String} + found_name = name in deps′ + found_name && @goto done + elseif deps′ isa Dict{String, Any} + deps′ = deps′::Dict{String, Any} + for (dep, uuid) in deps′ + uuid::String + if dep === name + return PkgId(UUID(uuid), name) + end + end end end end end - end # `name` is not an ext, do standard lookup as if this was the parent return identify_package(PkgId(UUID(uuid), dep_name), name) end @@ -1024,6 +1026,7 @@ function explicit_manifest_deps_get(project_file::String, where::PkgId, name::St end end end + @label done found_where || return nothing found_name || return PkgId(name) # Only reach here if deps was not a dict which mean we have a unique name for the dep From 1c67d0cfdc8ab109120dc3f0720053e509a10131 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Oct 2024 08:21:44 -0400 Subject: [PATCH 453/548] REPL: fix brace detection when ' is used for transpose (#56252) --- stdlib/REPL/src/REPLCompletions.jl | 9 +++++++-- stdlib/REPL/test/replcompletions.jl | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 5e80e17036559..42480aea91605 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -480,6 +480,7 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') i = firstindex(r) braces = in_comment = 0 in_single_quotes = in_double_quotes = in_back_ticks = false + num_single_quotes_in_string = count('\'', s) while i <= ncodeunits(r) c, i = iterate(r, i) if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '=' @@ -502,7 +503,9 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') braces += 1 elseif c == c_end braces -= 1 - elseif c == '\'' + elseif c == '\'' && num_single_quotes_in_string % 2 == 0 + # ' can be a transpose too, so check if there are even number of 's in the string + # TODO: This probably needs to be more robust in_single_quotes = true elseif c == '"' in_double_quotes = true @@ -1197,7 +1200,9 @@ function complete_identifiers!(suggestions::Vector{Completion}, if !isinfix # Handle infix call argument completion of the form bar + foo(qux). frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)]) - isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == prefix.args[end] + if !isempty(frange) # if find_start_brace fails to find the brace just continue + isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == prefix.args[end] + end end if isinfix prefix = prefix.args[end] diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 8bee70226755f..cfb9a6137a287 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -340,6 +340,12 @@ end # inexistent completion inside a cmd @test_nocompletion("run(`lol") +# issue 55856: copy(A'). errors in the REPL +let + c, r = test_complete("copy(A').") + @test isempty(c) +end + # test latex symbol completions let s = "\\alpha" c, r = test_bslashcomplete(s) From 319ee70104fa4f50b08c57fc202d5ab8d26f0d6b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 21 Oct 2024 14:40:37 +0200 Subject: [PATCH 454/548] remove new references to explicit_loaded_modules --- base/loading.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index e08d03ad513c7..ef8837b36a1ca 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2517,9 +2517,6 @@ loaded_modules_array() = @lock require_lock copy(loaded_modules_order) # after unreference_module, a subsequent require call will try to load a new copy of it, if stale # reload(m) = (unreference_module(m); require(m)) function unreference_module(key::PkgId) - if haskey(explicit_loaded_modules, key) - m = pop!(explicit_loaded_modules, key) - end if haskey(loaded_modules, key) m = pop!(loaded_modules, key) # need to ensure all modules are GC rooted; will still be referenced @@ -3125,7 +3122,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in # build up the list of modules that we want the precompile process to preserve if keep_loaded_modules concrete_deps = copy(_concrete_dependencies) - for (pkgreq, modreq) in loaded_modules # TODO: convert all relevant staleness heuristics to use explicit_loaded_modules instead + for (pkgreq, modreq) in loaded_modules if !(pkgreq === Main || pkgreq === Core || pkgreq === Base) push!(concrete_deps, pkgreq => module_build_id(modreq)) end From 8bdacc341a740b73d5e11b3ba548cd97bebab6c6 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 21 Oct 2024 09:17:03 -0400 Subject: [PATCH 455/548] Add basic infrastructure for binding replacement (#56224) Now that I've had a few months to recover from the slog of adding `BindingPartition`, it's time to renew my quest to finish #54654. This adds the basic infrastructure for having multiple partitions, including making the lookup respect the `world` argument - on-demand allocation of missing partitions, `Base.delete_binding` and the `@world` macro. Not included is any inference or invalidation support, or any support for the runtime to create partitions itself (only `Base.delete_binding` does that for now), which will come in subsequent PRs. --- base/essentials.jl | 44 +++++++++++++++++ base/range.jl | 11 +++++ base/runtime_internals.jl | 27 +++++++++-- base/show.jl | 18 +++++++ src/clangsa/GCChecker.cpp | 2 + src/julia.h | 11 +++-- src/julia_internal.h | 15 ++---- src/module.c | 99 +++++++++++++++++++++++++++++++-------- src/staticdata.c | 9 ++-- src/toplevel.c | 2 + test/choosetests.jl | 2 +- test/rebinding.jl | 18 +++++++ 12 files changed, 214 insertions(+), 44 deletions(-) create mode 100644 test/rebinding.jl diff --git a/base/essentials.jl b/base/essentials.jl index 0e7be924c908c..a07aaa6769ed2 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1250,6 +1250,50 @@ function isiterable(T)::Bool return hasmethod(iterate, Tuple{T}) end +""" + @world(sym, world) + +Resolve the binding `sym` in world `world`. See [`invoke_in_world`](@ref) for running +arbitrary code in fixed worlds. `world` may be `UnitRange`, in which case the macro +will error unless the binding is valid and has the same value across the entire world +range. + +The `@world` macro is primarily used in the priniting of bindings that are no longer available +in the current world. + +## Example +``` +julia> struct Foo; a::Int; end +Foo + +julia> fold = Foo(1) + +julia> Int(Base.get_world_counter()) +26866 + +julia> struct Foo; a::Int; b::Int end +Foo + +julia> fold +@world(Foo, 26866)(1) +``` + +!!! compat "Julia 1.12" + This functionality requires at least Julia 1.12. +""" +macro world(sym, world) + if isa(sym, Symbol) + return :($(_resolve_in_world)($world, $(QuoteNode(GlobalRef(__module__, sym))))) + elseif isa(sym, GlobalRef) + return :($(_resolve_in_world)($world, $(QuoteNode(sym)))) + else + error("`@world` requires a symbol or GlobalRef") + end +end + +_resolve_in_world(world::Integer, gr::GlobalRef) = + invoke_in_world(UInt(world), Core.getglobal, gr.mod, gr.name) + # Special constprop heuristics for various binary opes typename(typeof(function + end)).constprop_heuristic = Core.SAMETYPE_HEURISTIC typename(typeof(function - end)).constprop_heuristic = Core.SAMETYPE_HEURISTIC diff --git a/base/range.jl b/base/range.jl index 4b5d076dcf436..3301335785878 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1680,3 +1680,14 @@ function show(io::IO, r::LogRange{T}) where {T} show(io, length(r)) print(io, ')') end + +# Implementation detail of @world +# The rest of this is defined in essentials.jl, but UnitRange is not available +function _resolve_in_world(worlds::UnitRange, gr::GlobalRef) + # Validate that this binding's reference covers the entire world range + bpart = lookup_binding_partition(first(worlds), gr) + if bpart.max_world < last(world) + error("Binding does not cover the full world range") + end + _resolve_in_world(last(world), gr) +end diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 645aa55c538b4..ab867f8fcae6d 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -218,9 +218,10 @@ function _fieldnames(@nospecialize t) return t.name.names end -const BINDING_KIND_GLOBAL = 0x0 -const BINDING_KIND_CONST = 0x1 -const BINDING_KIND_CONST_IMPORT = 0x2 +# N.B.: Needs to be synced with julia.h +const BINDING_KIND_CONST = 0x0 +const BINDING_KIND_CONST_IMPORT = 0x1 +const BINDING_KIND_GLOBAL = 0x2 const BINDING_KIND_IMPLICIT = 0x3 const BINDING_KIND_EXPLICIT = 0x4 const BINDING_KIND_IMPORTED = 0x5 @@ -228,6 +229,8 @@ const BINDING_KIND_FAILED = 0x6 const BINDING_KIND_DECLARED = 0x7 const BINDING_KIND_GUARD = 0x8 +is_some_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT) + function lookup_binding_partition(world::UInt, b::Core.Binding) ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) end @@ -236,9 +239,27 @@ function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) ccall(:jl_get_globalref_partition, Ref{Core.BindingPartition}, (Any, UInt), gr, world) end +partition_restriction(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_restriction_value, Any, (Any,), bpart) + binding_kind(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_kind, UInt8, (Any,), bpart) binding_kind(m::Module, s::Symbol) = binding_kind(lookup_binding_partition(tls_world_age(), GlobalRef(m, s))) +""" + delete_binding(mod::Module, sym::Symbol) + +Force the binding `mod.sym` to be undefined again, allowing it be redefined. +Note that this operation is very expensive, requirinig a full scan of all code in the system, +as well as potential recompilation of any methods that (may) have used binding +information. + +!!! warning + The implementation of this functionality is currently incomplete. Do not use + this method on versions that contain this disclaimer except for testing. +""" +function delete_binding(mod::Module, sym::Symbol) + ccall(:jl_disable_binding, Cvoid, (Any,), GlobalRef(mod, sym)) +end + """ fieldname(x::DataType, i::Integer) diff --git a/base/show.jl b/base/show.jl index 25ed99f50b5b0..3aeb267b4a696 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1035,6 +1035,21 @@ function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing}) return false end +function check_world_bounded(tn::Core.TypeName) + bnd = ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), tn.module, tn.name, true) + isdefined(bnd, :partitions) || return nothing + partition = @atomic bnd.partitions + while true + if is_some_const_binding(binding_kind(partition)) && partition_restriction(partition) <: tn.wrapper + max_world = @atomic partition.max_world + max_world == typemax(UInt) && return nothing + return Int(partition.min_world):Int(max_world) + end + isdefined(partition, :next) || return nothing + partition = @atomic partition.next + end +end + function show_type_name(io::IO, tn::Core.TypeName) if tn === UnionAll.name # by coincidence, `typeof(Type)` is a valid representation of the UnionAll type. @@ -1063,7 +1078,10 @@ function show_type_name(io::IO, tn::Core.TypeName) end end end + world = check_world_bounded(tn) + world !== nothing && print(io, "@world(") show_sym(io, sym) + world !== nothing && print(io, ", ", world, ")") quo && print(io, ")") globfunc && print(io, ")") nothing diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 4892ebdabd110..830fe322a0a38 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -824,6 +824,7 @@ bool GCChecker::isGCTrackedType(QualType QT) { Name.ends_with_insensitive("jl_tupletype_t") || Name.ends_with_insensitive("jl_gc_tracked_buffer_t") || Name.ends_with_insensitive("jl_binding_t") || + Name.ends_with_insensitive("jl_binding_partition_t") || Name.ends_with_insensitive("jl_ordereddict_t") || Name.ends_with_insensitive("jl_tvar_t") || Name.ends_with_insensitive("jl_typemap_t") || @@ -847,6 +848,7 @@ bool GCChecker::isGCTrackedType(QualType QT) { Name.ends_with_insensitive("jl_stenv_t") || Name.ends_with_insensitive("jl_varbinding_t") || Name.ends_with_insensitive("set_world") || + Name.ends_with_insensitive("jl_ptr_kind_union_t") || Name.ends_with_insensitive("jl_codectx_t")) { return true; } diff --git a/src/julia.h b/src/julia.h index 168ba0deff1ec..dd79dbb82c28d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -620,6 +620,7 @@ typedef struct _jl_weakref_t { jl_value_t *value; } jl_weakref_t; +// N.B: Needs to be synced with runtime_internals.jl enum jl_partition_kind { // Constant: This binding partition is a constant declared using `const` // ->restriction holds the constant value @@ -684,7 +685,7 @@ typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { _Atomic(jl_ptr_kind_union_t) restriction; size_t min_world; _Atomic(size_t) max_world; - _Atomic(struct _jl_binding_partition_t*) next; + _Atomic(struct _jl_binding_partition_t *) next; size_t reserved; // Reserved for ->kind. Currently this holds the low bits of ->restriction during serialization } jl_binding_partition_t; @@ -1845,8 +1846,8 @@ JL_DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, size_t len) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_gensym(void); JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, size_t len); JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void); -JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; -JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b JL_PROPAGATES_ROOT); +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b JL_PROPAGATES_ROOT); JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world, jl_code_instance_t **cache); @@ -2008,8 +2009,8 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs); JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *op, jl_value_t *rhs); JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED); -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind); JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from); JL_DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s); JL_DLLEXPORT void jl_module_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname); diff --git a/src/julia_internal.h b/src/julia_internal.h index 4f735029da444..8c4ee9fca36e0 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -888,13 +888,10 @@ EXTERN_INLINE_DECLARE enum jl_partition_kind decode_restriction_kind(jl_ptr_kind #endif } -STATIC_INLINE jl_value_t *decode_restriction_value(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT +STATIC_INLINE jl_value_t *decode_restriction_value(jl_ptr_kind_union_t JL_PROPAGATES_ROOT pku) JL_NOTSAFEPOINT { #ifdef _P64 jl_value_t *val = (jl_value_t*)(pku & ~0x7); - // This is a little bit of a lie at the moment - it is one of the things that - // can go wrong with binding replacement. - JL_GC_PROMISE_ROOTED(val); return val; #else return pku.val; @@ -928,14 +925,8 @@ STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFE return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED; } -EXTERN_INLINE_DECLARE jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) JL_NOTSAFEPOINT { - if (!b) - return NULL; - assert(jl_is_binding(b)); - return jl_atomic_load_relaxed(&b->partitions); -} - -JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr, size_t world); +JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world); +JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr JL_PROPAGATES_ROOT, size_t world); EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); diff --git a/src/module.c b/src/module.c index 8dbac950235ee..f1098e22ff522 100644 --- a/src/module.c +++ b/src/module.c @@ -13,10 +13,51 @@ extern "C" { #endif // In this translation unit and this translation unit only emit this symbol `extern` for use by julia -EXTERN_INLINE_DEFINE jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) JL_NOTSAFEPOINT; EXTERN_INLINE_DEFINE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT; extern inline enum jl_partition_kind decode_restriction_kind(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT; +static jl_binding_partition_t *new_binding_partition(void) +{ + jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_gc_alloc(jl_current_task->ptls, sizeof(jl_binding_partition_t), jl_binding_partition_type); + jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); + bpart->min_world = 0; + jl_atomic_store_relaxed(&bpart->max_world, (size_t)-1); + jl_atomic_store_relaxed(&bpart->next, NULL); +#ifdef _P64 + bpart->reserved = 0; +#endif + return bpart; +} + +jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) { + if (!b) + return NULL; + assert(jl_is_binding(b)); + jl_value_t *parent = (jl_value_t*)b; + _Atomic(jl_binding_partition_t *)*insert = &b->partitions; + jl_binding_partition_t *bpart = jl_atomic_load_relaxed(insert); + size_t max_world = (size_t)-1; + while (1) { + while (bpart && world < bpart->min_world) { + insert = &bpart->next; + max_world = bpart->min_world - 1; + parent = (jl_value_t *)bpart; + bpart = jl_atomic_load_relaxed(&bpart->next); + } + if (bpart && world <= jl_atomic_load_relaxed(&bpart->max_world)) + return bpart; + jl_binding_partition_t *new_bpart = new_binding_partition(); + jl_atomic_store_relaxed(&new_bpart->next, bpart); + if (bpart) + new_bpart->min_world = jl_atomic_load_relaxed(&bpart->max_world) + 1; + jl_atomic_store_relaxed(&new_bpart->max_world, max_world); + if (jl_atomic_cmpswap(insert, &bpart, new_bpart)) { + jl_gc_wb(parent, new_bpart); + return new_bpart; + } + } +} + JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr, size_t world) { if (!gr) @@ -188,19 +229,6 @@ static jl_globalref_t *jl_new_globalref(jl_module_t *mod, jl_sym_t *name, jl_bin return g; } -static jl_binding_partition_t *new_binding_partition(void) -{ - jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_gc_alloc(jl_current_task->ptls, sizeof(jl_binding_partition_t), jl_binding_partition_type); - jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); - bpart->min_world = 0; - jl_atomic_store_relaxed(&bpart->max_world, (size_t)-1); - jl_atomic_store_relaxed(&bpart->next, NULL); -#ifdef _P64 - bpart->reserved = 0; -#endif - return bpart; -} - static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) { jl_task_t *ct = jl_current_task; @@ -215,9 +243,7 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) JL_GC_PUSH1(&b); b->globalref = jl_new_globalref(mod, name, b); jl_gc_wb(b, b->globalref); - jl_binding_partition_t *bpart = new_binding_partition(); - jl_atomic_store_relaxed(&b->partitions, bpart); - jl_gc_wb(b, bpart); + jl_atomic_store_relaxed(&b->partitions, NULL); JL_GC_POP(); return b; } @@ -324,6 +350,12 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b) return decode_restriction_value(pku); } +JL_DLLEXPORT jl_value_t *jl_bpart_get_restriction_value(jl_binding_partition_t *bpart) +{ + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + return decode_restriction_value(pku); +} + typedef struct _modstack_t { jl_module_t *m; jl_sym_t *var; @@ -947,6 +979,28 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var jl_gc_wb(bpart, val); } +extern jl_mutex_t world_counter_lock; +JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) +{ + jl_binding_t *b = gr->binding; + b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + + if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GUARD) { + // Already guard + return; + } + + JL_LOCK(&world_counter_lock); + jl_task_t *ct = jl_current_task; + size_t new_max_world = jl_atomic_load_acquire(&jl_world_counter); + // TODO: Trigger invalidation here + (void)ct; + jl_atomic_store_release(&bpart->max_world, new_max_world); + jl_atomic_store_release(&jl_world_counter, new_max_world + 1); + JL_UNLOCK(&world_counter_lock); +} + JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; @@ -1018,13 +1072,17 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, int reassign) { + JL_GC_PUSH1(&rhs); // callee-rooted jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { jl_value_t *old = decode_restriction_value(pku); - if (jl_egal(rhs, old)) + JL_GC_PROMISE_ROOTED(old); + if (jl_egal(rhs, old)) { + JL_GC_POP(); return NULL; + } if (jl_typeof(rhs) == jl_typeof(old)) jl_errorf("invalid redefinition of constant %s.%s. This redefinition may be permitted using the `const` keyword.", jl_symbol_name(mod->name), jl_symbol_name(var)); @@ -1033,13 +1091,13 @@ jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t jl_symbol_name(mod->name), jl_symbol_name(var)); } jl_value_t *old_ty = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(old_ty); if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { - JL_GC_PUSH1(&rhs); // callee-rooted if (!jl_isa(rhs, old_ty)) jl_errorf("cannot assign an incompatible value to the global %s.%s.", jl_symbol_name(mod->name), jl_symbol_name(var)); - JL_GC_POP(); } + JL_GC_POP(); return old_ty; } @@ -1076,6 +1134,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl jl_errorf("invalid redefinition of constant %s.%s", jl_symbol_name(mod->name), jl_symbol_name(var)); jl_value_t *ty = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(ty); return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var); } diff --git a/src/staticdata.c b/src/staticdata.c index af4527cbc143f..af3477a25128e 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3883,9 +3883,12 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl if ((jl_value_t*)b == jl_nothing) continue; jl_binding_partition_t *bpart = jl_atomic_load_relaxed(&b->partitions); - jl_atomic_store_relaxed(&bpart->restriction, - encode_restriction((jl_value_t*)jl_atomic_load_relaxed(&bpart->restriction), bpart->reserved)); - bpart->reserved = 0; + while (bpart) { + jl_atomic_store_relaxed(&bpart->restriction, + encode_restriction((jl_value_t*)jl_atomic_load_relaxed(&bpart->restriction), bpart->reserved)); + bpart->reserved = 0; + bpart = jl_atomic_load_relaxed(&bpart->next); + } } #endif } diff --git a/src/toplevel.c b/src/toplevel.c index 8caa8b086ec00..6f2e0cf77568a 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -318,6 +318,7 @@ void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_va jl_symbol_name(mod->name), jl_symbol_name(sym)); } jl_value_t *old_ty = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(old_ty); if (!jl_types_equal(ty, old_ty)) { jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", jl_symbol_name(mod->name), jl_symbol_name(sym)); @@ -738,6 +739,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, j if (!val) return bpart; jl_value_t *old = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(old); if (jl_egal(val, old)) break; if (!did_warn) { diff --git a/test/choosetests.jl b/test/choosetests.jl index 96d230d185c71..affdee412bd86 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -29,7 +29,7 @@ const TESTNAMES = [ "channels", "iostream", "secretbuffer", "specificity", "reinterpretarray", "syntax", "corelogging", "missing", "asyncmap", "smallarrayshrink", "opaque_closure", "filesystem", "download", - "scopedvalues", "compileall" + "scopedvalues", "compileall", "rebinding" ] const INTERNET_REQUIRED_LIST = [ diff --git a/test/rebinding.jl b/test/rebinding.jl new file mode 100644 index 0000000000000..4066d91bc4b9b --- /dev/null +++ b/test/rebinding.jl @@ -0,0 +1,18 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Rebinding + using Test + + @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD + struct Foo + x::Int + end + x = Foo(1) + + @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_CONST + @test !contains(repr(x), "@world") + Base.delete_binding(@__MODULE__, :Foo) + + @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD + @test contains(repr(x), "@world") +end From 82e0e28d76621888f6e501033c59549cb0104bac Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 19:48:49 +0530 Subject: [PATCH 456/548] Specialize `haszero` for `Union{Missing,<:Number}` (#56169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `zero(::Union{Missing,T})` calls `zero(T)` internally, we may use the same logic for `LinearAlgebra.haszero`. This helps with certain structured matrix operations: ```julia julia> M = Matrix{Union{Int,Missing}}(missing,2,2) 2×2 Matrix{Union{Missing, Int64}}: missing missing missing missing julia> triu(M) 2×2 Matrix{Union{Missing, Int64}}: missing missing 0 missing ``` whereas previously, this would have been ```julia julia> triu(M) 2×2 Matrix{Union{Missing, Int64}}: missing missing missing missing ``` --- stdlib/LinearAlgebra/src/dense.jl | 1 + stdlib/LinearAlgebra/test/triangular.jl | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index aacc5479bfa9d..d8f2513f5bfc8 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -110,6 +110,7 @@ norm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} = # Conservative assessment of types that have zero(T) defined for themselves haszero(::Type) = false haszero(::Type{T}) where {T<:Number} = isconcretetype(T) +haszero(::Type{Union{Missing,T}}) where {T<:Number} = haszero(T) @propagate_inbounds _zero(M::AbstractArray{T}, inds...) where {T} = haszero(T) ? zero(T) : zero(M[inds...]) """ diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 7acb3cbfc0c57..2ceda735dfd0a 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1284,6 +1284,14 @@ end @test istril(U, k) == istril(A, k) end end + + @testset "Union eltype" begin + M = Matrix{Union{Int,Missing}}(missing,2,2) + U = triu(M) + @test iszero(U[2,1]) + U = tril(M) + @test iszero(U[1,2]) + end end @testset "indexing with a BandIndex" begin From 6d7e29f33018b8750ac1f2cf446a51910d25a1d7 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 21 Oct 2024 11:24:39 -0300 Subject: [PATCH 457/548] Add small tweaks to juliac script and misc fixes to juliac (#56119) This comments out an assert thats currently faulty and also marks apply_iterate as safe when we can special case it to not dispatch --- contrib/juliac-buildscript.jl | 22 ++++++++++++------ contrib/juliac.jl | 6 +++-- src/codegen.cpp | 42 ++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/contrib/juliac-buildscript.jl b/contrib/juliac-buildscript.jl index 490bca86e1cba..0303e95f448b5 100644 --- a/contrib/juliac-buildscript.jl +++ b/contrib/juliac-buildscript.jl @@ -27,6 +27,7 @@ end (f::Base.RedirectStdStream)(io::Core.CoreSTDOUT) = Base._redirect_io_global(io, f.unix_fd) @eval Base begin + depwarn(msg, funcsym; force::Bool=false) = nothing _assert_tostring(msg) = "" reinit_stdio() = nothing JuliaSyntax.enable_in_core!() = nothing @@ -229,20 +230,15 @@ let loaded = Symbol.(Base.loaded_modules_array()) # TODO better way to do this using Artifacts @eval Artifacts begin function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, _::Val{lazyartifacts}) where lazyartifacts - moduleroot = Base.moduleroot(__module__) - if haskey(Base.module_keys, moduleroot) - # Process overrides for this UUID, if we know what it is - process_overrides(artifact_dict, Base.module_keys[moduleroot].uuid) - end - # If the artifact exists, we're in the happy path and we can immediately # return the path to the artifact: - dirs = artifact_paths(hash; honor_overrides=true) + dirs = artifacts_dirs(bytes2hex(hash.bytes)) for dir in dirs if isdir(dir) return jointail(dir, path_tail) end end + error("Artifact not found") end end end @@ -258,6 +254,18 @@ let loaded = Symbol.(Base.loaded_modules_array()) # TODO better way to do this __init__() = rand() end end + if :Markdown in loaded + using Markdown + @eval Markdown begin + __init__() = rand() + end + end + if :JuliaSyntaxHighlighting in loaded + using JuliaSyntaxHighlighting + @eval JuliaSyntaxHighlighting begin + __init__() = rand() + end + end end empty!(Core.ARGS) diff --git a/contrib/juliac.jl b/contrib/juliac.jl index 61e0e91958667..0f008976d2b4f 100644 --- a/contrib/juliac.jl +++ b/contrib/juliac.jl @@ -8,6 +8,7 @@ trim = nothing outname = nothing file = nothing add_ccallables = false +verbose = false help = findfirst(x->x == "--help", ARGS) if help !== nothing @@ -39,6 +40,8 @@ let i = 1 end elseif arg == "--compile-ccallable" global add_ccallables = true + elseif arg == "--verbose" + global verbose = true else if arg[1] == '-' || !isnothing(file) println("Unexpected argument `$arg`") @@ -77,9 +80,8 @@ open(initsrc_path, "w") do io end static_call_graph_arg() = isnothing(trim) ? `` : `--trim=$(trim)` -is_verbose() = verbose ? `--verbose-compilation=yes` : `` cmd = addenv(`$cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $(static_call_graph_arg()) $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) - +verbose && println("Running: $cmd") if !success(pipeline(cmd; stdout, stderr)) println(stderr, "\nFailed to compile $file") exit(1) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0ab26a65fcaaa..eaa3cc8176ad5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2247,7 +2247,10 @@ static jl_array_t* build_stack_crumbs(jl_codectx_t &ctx) JL_NOTSAFEPOINT break; } if (caller) { - assert(ctx.emission_context.enqueuers.count(caller) == 1); + + // assert(ctx.emission_context.enqueuers.count(caller) == 1); + // Each enqueuer should only be enqueued at least once and only once. Check why this assert is triggering + // This isn't a fatal error, just means that we may get a wrong backtrace if (jl_is_method_instance(caller)) { //TODO: Use a subrange when C++20 is a thing for (auto it2 = std::get(it->second).begin(); it2 != (std::prev(std::get(it->second).end())); ++it2) { @@ -5732,10 +5735,34 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo // special case for some known builtin not handled by emit_builtin_call auto it = builtin_func_map().find(builtin_fptr); if (it != builtin_func_map().end()) { - if (trim_may_error(ctx.params->trim) && may_dispatch_builtins().count(builtin_fptr)) { - errs() << "ERROR: Dynamic call to builtin" << jl_symbol_name(((jl_datatype_t*)jl_typeof(f.constant))->name->name); - errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; - print_stacktrace(ctx, ctx.params->trim); + if (trim_may_error(ctx.params->trim)) { + bool may_dispatch = may_dispatch_builtins().count(builtin_fptr); + if (may_dispatch && f.constant == jl_builtin__apply_iterate && nargs >= 4) { + if (jl_subtype(argv[2].typ, (jl_value_t*)jl_builtin_type)) { + static jl_value_t *jl_dispatchfree_apply_iterate_type = NULL; + if (!jl_dispatchfree_apply_iterate_type) { + jl_value_t *types[5] = { + (jl_value_t *)jl_simplevector_type, + (jl_value_t *)jl_genericmemory_type, + (jl_value_t *)jl_array_type, + (jl_value_t *)jl_tuple_type, + (jl_value_t *)jl_namedtuple_type, + }; + jl_dispatchfree_apply_iterate_type = jl_as_global_root(jl_type_union(types, 5), 1); + } + for (size_t i = 3; i < nargs; i++) { + auto ai = argv[i].typ; + if (!jl_subtype(ai, jl_dispatchfree_apply_iterate_type)) + break; + } + may_dispatch = false; + } + } + if (may_dispatch) { + errs() << "ERROR: Dynamic call to builtin " << jl_symbol_name(((jl_datatype_t*)jl_typeof(f.constant))->name->name); + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } } Value *ret = emit_jlcall(ctx, it->second, Constant::getNullValue(ctx.types().T_prjlvalue), ArrayRef(argv).drop_front(), nargs - 1, julia_call); setName(ctx.emission_context, ret, it->second->name + "_ret"); @@ -5752,6 +5779,11 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo fptr = FunctionCallee(get_func_sig(ctx.builder.getContext()), ctx.builder.CreateCall(prepare_call(jlgetbuiltinfptr_func), {emit_typeof(ctx, f)})); cc = julia_call; } + if (trim_may_error(ctx.params->trim)) { + errs() << "ERROR: Dynamic call to unknown builtin"; + errs() << "In " << ctx.builder.getCurrentDebugLocation()->getFilename() << ":" << ctx.builder.getCurrentDebugLocation()->getLine() << "\n"; + print_stacktrace(ctx, ctx.params->trim); + } Value *ret = emit_jlcall(ctx, fptr, nullptr, argv, nargs, cc); setName(ctx.emission_context, ret, "Builtin_ret"); return mark_julia_type(ctx, ret, true, rt); From 11ef8eb0be7b411ab8c2813789adf63af4793798 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 20:13:53 +0530 Subject: [PATCH 458/548] Warn about negative size in array construction (#56262) After this, ```julia julia> zeros(-1) ERROR: ArgumentError: invalid GenericMemory size: the number of elements is either negative or too large for system address width [...] ``` The error message is updated to warn about possible negative sizes when creating arrays. Fixes https://github.com/JuliaLang/julia/issues/55446 --- src/genericmemory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/genericmemory.c b/src/genericmemory.c index ea52fca66ba48..5c48e3202493e 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -45,7 +45,7 @@ jl_genericmemory_t *_new_genericmemory_(jl_value_t *mtype, size_t nel, int8_t is prod += nel; } if (nel >= MAXINTVAL || prod >= (wideint_t) MAXINTVAL) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: too large for system address width"); + jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); size_t tot = (size_t)prod + LLT_ALIGN(sizeof(jl_genericmemory_t),JL_SMALL_BYTE_ALIGNMENT); int pooled = tot <= GC_MAX_SZCLASS; From cba1cc022b05eef93f7145aeb363a6ab2c9c3e6c Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 20:28:39 +0530 Subject: [PATCH 459/548] Revert "Reroute` (Upper/Lower)Triangular * Diagonal` through `__muldiag`" (#56267) Reverts JuliaLang/julia#55984 This PR was buggy, but the test failures as seen in https://buildkite.com/julialang/julia-master/builds/41300#0192adab-9d07-4900-8592-2d46aff26905 were not caught in the CI run for the PR as the tests are run randomly. Let's revert this for now. --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 - stdlib/LinearAlgebra/src/diagonal.jl | 158 +++++++++------------- stdlib/LinearAlgebra/test/diagonal.jl | 40 +----- 3 files changed, 66 insertions(+), 134 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 88fc3476c9d7f..15354603943c2 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -655,8 +655,6 @@ matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = _matprod_dest_diag(A, TS) matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = _matprod_dest_diag(B, TS) matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) -_matprod_dest_diag(A::UnitUpperTriangular, TS) = UpperTriangular(similar(parent(A), TS)) -_matprod_dest_diag(A::UnitLowerTriangular, TS) = LowerTriangular(similar(parent(A), TS)) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) ev = similar(A, TS, max(0, n-1)) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 8ba4c3d457e83..6e8ce96259fc1 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -396,120 +396,82 @@ function lmul!(D::Diagonal, T::Tridiagonal) return T end -@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B, _add::MulAddMul) - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - _modify!(_add, D.diag[i] * B[i,j], out, (i,j)) - end - end - out -end -_maybe_unwrap_tri(out, A) = out, A -_maybe_unwrap_tri(out::UpperTriangular, A::UpperOrUnitUpperTriangular) = parent(out), parent(A) -_maybe_unwrap_tri(out::LowerTriangular, A::LowerOrUnitLowerTriangular) = parent(out), parent(A) -@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B::UpperOrLowerTriangular, _add::MulAddMul) - isunit = B isa Union{UnitUpperTriangular, UnitLowerTriangular} - # if both B and out have the same upper/lower triangular structure, - # we may directly read and write from the parents - out_maybeparent, B_maybeparent = _maybe_unwrap_tri(out, B) - for j in axes(B, 2) - if isunit - _modify!(_add, D.diag[j] * B[j,j], out, (j,j)) - end - rowrange = B isa UpperOrUnitUpperTriangular ? (1:min(j-isunit, size(B,1))) : (j+isunit:size(B,1)) - @inbounds @simd for i in rowrange - _modify!(_add, D.diag[i] * B_maybeparent[i,j], out_maybeparent, (i,j)) - end - end - out -end -function __muldiag!(out, D::Diagonal, B, _add::MulAddMul) +function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out, B) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - __muldiag_nonzeroalpha!(out, D, B, _add) - end - return out -end - -@inline function __muldiag_nonzeroalpha!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - beta = _add.beta - _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) - @inbounds for j in axes(A, 2) - dja = _add(D.diag[j]) - @simd for i in axes(A, 1) - _modify!(_add_aisone, A[i,j] * dja, out, (i,j)) - end - end - out -end -@inline function __muldiag_nonzeroalpha!(out, A::UpperOrLowerTriangular, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - isunit = A isa Union{UnitUpperTriangular, UnitLowerTriangular} - beta = _add.beta - # since alpha is multiplied to the diagonal element of D, - # we may skip alpha in the second multiplication by setting ais1 to true - _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) - # if both A and out have the same upper/lower triangular structure, - # we may directly read and write from the parents - out_maybeparent, A_maybeparent = _maybe_unwrap_tri(out, A) - @inbounds for j in axes(A, 2) - dja = _add(D.diag[j]) - if isunit - _modify!(_add_aisone, A[j,j] * dja, out, (j,j)) - end - rowrange = A isa UpperOrUnitUpperTriangular ? (1:min(j-isunit, size(A,1))) : (j+isunit:size(A,1)) - @simd for i in rowrange - _modify!(_add_aisone, A_maybeparent[i,j] * dja, out_maybeparent, (i,j)) + if bis0 + @inbounds for j in axes(B, 2) + @simd for i in axes(B, 1) + out[i,j] = D.diag[i] * B[i,j] * alpha + end + end + else + @inbounds for j in axes(B, 2) + @simd for i in axes(B, 1) + out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta + end + end end end - out + return out end -function __muldiag!(out, A, D::Diagonal, _add::MulAddMul) +function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out, A) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - __muldiag_nonzeroalpha!(out, A, D, _add) + if bis0 + @inbounds for j in axes(A, 2) + dja = D.diag[j] * alpha + @simd for i in axes(A, 1) + out[i,j] = A[i,j] * dja + end + end + else + @inbounds for j in axes(A, 2) + dja = D.diag[j] * alpha + @simd for i in axes(A, 1) + out[i,j] = A[i,j] * dja + out[i,j] * beta + end + end + end end return out end - -@inline function __muldiag_nonzeroalpha!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul) +function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} d1 = D1.diag d2 = D2.diag - outd = out.diag - @inbounds @simd for i in eachindex(d1, d2, outd) - _modify!(_add, d1[i] * d2[i], outd, i) - end - out -end -function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out.diag, beta) else - __muldiag_nonzeroalpha!(out, D1, D2, _add) + if bis0 + @inbounds @simd for i in eachindex(out.diag) + out.diag[i] = d1[i] * d2[i] * alpha + end + else + @inbounds @simd for i in eachindex(out.diag) + out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta + end + end end return out end -@inline function __muldiag_nonzeroalpha!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul) - d1 = D1.diag - d2 = D2.diag - @inbounds @simd for i in eachindex(d1, d2) - _modify!(_add, d1[i] * d2[i], out, (i,i)) - end - out -end -function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1}) where {ais1} +function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out) alpha, beta = _add.alpha, _add.beta + mA = size(D1, 1) + d1 = D1.diag + d2 = D2.diag _rmul_or_fill!(out, beta) if !iszero(alpha) - _add_bis1 = MulAddMul{ais1,false,typeof(alpha),Bool}(alpha,true) - __muldiag_nonzeroalpha!(out, D1, D2, _add_bis1) + @inbounds @simd for i in 1:mA + out[i,i] += d1[i] * d2[i] * alpha + end end return out end @@ -696,21 +658,31 @@ for Tri in (:UpperTriangular, :LowerTriangular) @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D)) @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag)) end - @eval *(A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = - @invoke *(A::AbstractMatrix, D::Diagonal) - @eval *(A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = - @invoke *(A::AbstractMatrix, D::Diagonal) for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv)) @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data)) @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag)) end - @eval *(D::Diagonal, A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}) = - @invoke *(D::Diagonal, A::AbstractMatrix) - @eval *(D::Diagonal, A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}) = - @invoke *(D::Diagonal, A::AbstractMatrix) # 3-arg ldiv! @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) + # 3-arg mul! is disambiguated in special.jl + # 5-arg mul! + @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) + @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + α, β = _add.alpha, _add.beta + iszero(α) && return _rmul_or_fill!(C, β) + diag′ = bis0 ? nothing : diag(C) + data = mul!(C.data, D, A.data, α, β) + $Tri(_setdiag!(data, _add, D.diag, diag′)) + end + @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) + @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + α, β = _add.alpha, _add.beta + iszero(α) && return _rmul_or_fill!(C, β) + diag′ = bis0 ? nothing : diag(C) + data = mul!(C.data, A.data, D, α, β) + $Tri(_setdiag!(data, _add, D.diag, diag′)) + end end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 380a0465028d1..1c3a9dfa676ac 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1188,7 +1188,7 @@ end @test oneunit(D3) isa typeof(D3) end -@testset "$Tri" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) +@testset "AbstractTriangular" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) A = randn(4, 4) TriA = Tri(A) UTriA = UTri(A) @@ -1218,44 +1218,6 @@ end @test outTri === mul!(outTri, D, UTriA, 2, 1)::Tri == mul!(out, D, Matrix(UTriA), 2, 1) @test outTri === mul!(outTri, TriA, D, 2, 1)::Tri == mul!(out, Matrix(TriA), D, 2, 1) @test outTri === mul!(outTri, UTriA, D, 2, 1)::Tri == mul!(out, Matrix(UTriA), D, 2, 1) - - # we may write to a Unit triangular if the diagonal is preserved - ID = Diagonal(ones(size(UTriA,2))) - @test mul!(copy(UTriA), UTriA, ID) == UTriA - @test mul!(copy(UTriA), ID, UTriA) == UTriA - - @testset "partly filled parents" begin - M = Matrix{BigFloat}(undef, 2, 2) - M[1,1] = M[2,2] = 3 - isupper = Tri == UpperTriangular - M[1+!isupper, 1+isupper] = 3 - D = Diagonal(1:2) - T = Tri(M) - TA = Array(T) - @test T * D == TA * D - @test D * T == D * TA - @test mul!(copy(T), T, D, 2, 3) == 2T * D + 3T - @test mul!(copy(T), D, T, 2, 3) == 2D * T + 3T - - U = UTri(M) - UA = Array(U) - @test U * D == UA * D - @test D * U == D * UA - @test mul!(copy(T), U, D, 2, 3) == 2 * UA * D + 3TA - @test mul!(copy(T), D, U, 2, 3) == 2 * D * UA + 3TA - - M2 = Matrix{BigFloat}(undef, 2, 2) - M2[1+!isupper, 1+isupper] = 3 - U = UTri(M2) - UA = Array(U) - @test U * D == UA * D - @test D * U == D * UA - ID = Diagonal(ones(size(U,2))) - @test mul!(copy(U), U, ID) == U - @test mul!(copy(U), ID, U) == U - @test mul!(copy(U), U, ID, 2, -1) == U - @test mul!(copy(U), ID, U, 2, -1) == U - end end struct SMatrix1{T} <: AbstractArray{T,2} From 2188ba4d70a349594484c927bc7a6e71edaa5902 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 21 Oct 2024 11:57:10 -0400 Subject: [PATCH 460/548] precompile: add error for using require_stdlib during precompile (#56233) This function could accidentally add a dependency on the stdlib in the user's package, which would make it immediately stale. As pointed out to me by topolarity --- base/loading.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index db6a681bb2a5b..190aca66f91ff 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1564,7 +1564,6 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} end end -precompiling_package::Bool = false loading_extension::Bool = false precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) @@ -2288,11 +2287,6 @@ For more details regarding code loading, see the manual sections on [modules](@r [parallel computing](@ref code-availability). """ function require(into::Module, mod::Symbol) - if into === Base.__toplevel__ && precompiling_package - # this error type needs to match the error type compilecache throws for non-125 errors. - error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ - is not allowed during package precompilation.") - end if _require_world_age[] != typemax(UInt) Base.invoke_in_world(_require_world_age[], __require, into, mod) else @@ -2301,6 +2295,10 @@ function require(into::Module, mod::Symbol) end function __require(into::Module, mod::Symbol) + if into === Base.__toplevel__ && generating_output(#=incremental=#true) + error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ + is not allowed during package precompilation.") + end @lock require_lock begin LOADING_CACHE[] = LoadingCache() try @@ -2709,6 +2707,10 @@ end [2] https://github.com/JuliaLang/StyledStrings.jl/issues/91#issuecomment-2379602914 """ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) + if generating_output(#=incremental=#true) + # Otherwise this would lead to awkward dependency issues by loading a package that isn't in the Project/Manifest + error("This interactive function requires a stdlib to be loaded, and package code should instead use it directly from that stdlib.") + end @lock require_lock begin # 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 @@ -3048,7 +3050,6 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated Base.track_nested_precomp($precomp_stack) Base.precompiling_extension = $(loading_extension | isext) - Base.precompiling_package = true Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) """) From f9765410821dce0f2e5ce8a625fbdf9a52f02462 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 22:26:09 +0530 Subject: [PATCH 461/548] Fix indexing for block triangular matrices (#56168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assuming that block matrices contain tiled elements (i.e. all elements along a row have the same number of rows and similarly, all elements along a column have the same number of columns), we may generalize `diagzero` to accept arbitrary matrices. We may therefore use only the diagonal elements to generate the structural zeros. This was being assumed anyway in the individual methods. We also now use `diagzero` in indexing triangular matrices, so the following would work correctly: ```julia julia> M = reshape([ones(2,2), fill(2,4,2), fill(3,2,3), fill(4,4,3)],2,2) 2×2 Matrix{Matrix{Float64}}: [1.0 1.0; 1.0 1.0] … [3.0 3.0 3.0; 3.0 3.0 3.0] [2.0 2.0; 2.0 2.0; 2.0 2.0; 2.0 2.0] [4.0 4.0 4.0; 4.0 4.0 4.0; 4.0 4.0 4.0; 4.0 4.0 4.0] julia> U = UpperTriangular(M) 2×2 UpperTriangular{Matrix{Float64}, Matrix{Matrix{Float64}}}: [1.0 1.0; 1.0 1.0] … [3.0 3.0 3.0; 3.0 3.0 3.0] ⋅ [4.0 4.0 4.0; 4.0 4.0 4.0; 4.0 4.0 4.0; 4.0 4.0 4.0] julia> U[2,1] 4×2 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 julia> U[2,1] == zero(M[2,1]) true ``` This also changes ```julia julia> M = Matrix{Union{Int,Missing}}(missing,4,4) 4×4 Matrix{Union{Missing, Int64}}: missing missing missing missing missing missing missing missing missing missing missing missing missing missing missing missing julia> U = UpperTriangular(M) 4×4 UpperTriangular{Union{Missing, Int64}, Matrix{Union{Missing, Int64}}}: missing missing missing missing ⋅ missing missing missing ⋅ ⋅ missing missing ⋅ ⋅ ⋅ missing julia> U[3,1] # v"1.12.0-DEV.1373" missing ``` to ```julia julia> U[3,1] # this PR 0 ``` --- stdlib/LinearAlgebra/src/bidiag.jl | 11 ----------- stdlib/LinearAlgebra/src/diagonal.jl | 5 +++-- stdlib/LinearAlgebra/src/triangular.jl | 4 ++-- stdlib/LinearAlgebra/test/triangular.jl | 13 +++++++++++++ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index a34df37153cd2..b38a983296065 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -118,17 +118,6 @@ Bidiagonal(A::Bidiagonal) = A Bidiagonal{T}(A::Bidiagonal{T}) where {T} = A Bidiagonal{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A.dv, A.ev, A.uplo) -function diagzero(A::Bidiagonal{<:AbstractMatrix}, i, j) - Tel = eltype(A) - if i < j && A.uplo == 'U' #= top right zeros =# - return zeroslike(Tel, axes(A.ev[i], 1), axes(A.ev[j-1], 2)) - elseif j < i && A.uplo == 'L' #= bottom left zeros =# - return zeroslike(Tel, axes(A.ev[i-1], 1), axes(A.ev[j], 2)) - else - return zeroslike(Tel, axes(A.dv[i], 1), axes(A.dv[j], 2)) - end -end - _offdiagind(uplo) = uplo == 'U' ? 1 : -1 @inline function Base.isassigned(A::Bidiagonal, i::Int, j::Int) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 6e8ce96259fc1..417bcfa5715b1 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -191,8 +191,9 @@ end Return the appropriate zero element `A[i, j]` corresponding to a banded matrix `A`. """ diagzero(A::AbstractMatrix, i, j) = zero(eltype(A)) -diagzero(D::Diagonal{M}, i, j) where {M<:AbstractMatrix} = - zeroslike(M, axes(D.diag[i], 1), axes(D.diag[j], 2)) +diagzero(A::AbstractMatrix{M}, i, j) where {M<:AbstractMatrix} = + zeroslike(M, axes(A[i,i], 1), axes(A[j,j], 2)) +diagzero(A::AbstractMatrix, inds...) = diagzero(A, to_indices(A, inds)...) # dispatching on the axes permits specializing on the axis types to return something other than an Array zeroslike(M::Type, ax::Vararg{Union{AbstractUnitRange, Integer}}) = zeroslike(M, ax) """ diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 83ef221329d33..d6994f4b4dd58 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -233,7 +233,7 @@ Base.isstored(A::UpperOrLowerTriangular, i::Int, j::Int) = @propagate_inbounds getindex(A::Union{UnitLowerTriangular{T}, UnitUpperTriangular{T}}, i::Int, j::Int) where {T} = _shouldforwardindex(A, i, j) ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) @propagate_inbounds getindex(A::Union{LowerTriangular, UpperTriangular}, i::Int, j::Int) = - _shouldforwardindex(A, i, j) ? A.data[i,j] : _zero(A.data,j,i) + _shouldforwardindex(A, i, j) ? A.data[i,j] : diagzero(A,i,j) _shouldforwardindex(U::UpperTriangular, b::BandIndex) = b.band >= 0 _shouldforwardindex(U::LowerTriangular, b::BandIndex) = b.band <= 0 @@ -245,7 +245,7 @@ Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{UnitL _shouldforwardindex(A, b) ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) end Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{LowerTriangular, UpperTriangular}, b::BandIndex) - _shouldforwardindex(A, b) ? A.data[b] : _zero(A.data, b) + _shouldforwardindex(A, b) ? A.data[b] : diagzero(A.data, b) end _zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 2ceda735dfd0a..678827ceac720 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1330,6 +1330,19 @@ end end end +@testset "indexing uses diagzero" begin + @testset "block matrix" begin + M = reshape([zeros(2,2), zeros(4,2), zeros(2,3), zeros(4,3)],2,2) + U = UpperTriangular(M) + @test [size(x) for x in U] == [size(x) for x in M] + end + @testset "Union eltype" begin + M = Matrix{Union{Int,Missing}}(missing,4,4) + U = UpperTriangular(M) + @test iszero(U[3,1]) + end +end + @testset "addition/subtraction of mixed triangular" begin for A in (Hermitian(rand(4, 4)), Diagonal(rand(5))) for T in (UpperTriangular, LowerTriangular, From 1ba035da42110313b84e8edf2dccd3aa9a2a5082 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:16:53 -0400 Subject: [PATCH 462/548] trimming: don't abort where we used to resolve dynamic calls (#56271) This call resolution code was deleted in #56179 (rightfully so), but it should be a no-op until we implement this in inference. --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index eaa3cc8176ad5..b0d5038024900 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5806,7 +5806,8 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo } int failed_dispatch = !argv[0].constant; if (ctx.params->trim != JL_TRIM_NO) { - abort(); // this code path is unsound, unsafe, and probably bad + // TODO: Implement the last-minute call resolution that used to be here + // in inference instead. } if (failed_dispatch && trim_may_error(ctx.params->trim)) { From 08d11d041b22fe90380e56be4fb4d44aaf46ec85 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 22 Oct 2024 05:23:04 +0900 Subject: [PATCH 463/548] inference: fix inference error from constructing invalid `TypeVar` (#56264) - fixes JuliaLang/julia#56248 --- base/compiler/tfuncs.jl | 12 ++++++++++-- test/compiler/inference.jl | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a6b7e53c6f320..450cfdcfadf82 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -601,8 +601,16 @@ add_tfunc(svec, 0, INT_INF, @nospecs((𝕃::AbstractLattice, args...)->SimpleVec return TypeVar end end - tv = TypeVar(nval, lb, ub) - return PartialTypeVar(tv, lb_certain, ub_certain) + lb_valid = lb isa Type || lb isa TypeVar + ub_valid = ub isa Type || ub isa TypeVar + if lb_valid && ub_valid + tv = TypeVar(nval, lb, ub) + return PartialTypeVar(tv, lb_certain, ub_certain) + elseif !lb_valid && lb_certain + return Union{} + elseif !ub_valid && ub_certain + return Union{} + end end return TypeVar end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 71f9da04baa4a..dd62e329962c6 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6055,3 +6055,11 @@ f55916(::Vararg{T,T}) where {T} = "2" g55916(x) = f55916(x) # this shouldn't error @test only(code_typed(g55916, (Any,); optimize=false))[2] == Int + +# JuliaLang/julia#56248 +@test Base.infer_return_type() do + TypeVar(:Issue56248, 1) +end === Union{} +@test Base.infer_return_type() do + TypeVar(:Issue56248, Any, 1) +end === Union{} From 36593fdd3a4e8112798059bb2310a39bbfcf96ed Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Oct 2024 22:41:34 -0400 Subject: [PATCH 464/548] add Pkg 1.11 news to HISTORY.md (#56277) Backport already on https://github.com/JuliaLang/julia/pull/56228 --- HISTORY.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index aa7f9f0ccdad6..c3ca212453d07 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -136,6 +136,14 @@ Standard library changes `AnnotatedString` with various faces or other attributes applied ([#49586]). #### Package Manager +* It is now possible to specify "sources" for packages in a `[sources]` section in Project.toml. + This can be used to add non-registered normal or test dependencies. +* Pkg now obeys `[compat]` bounds for `julia` and raises an error if the version of the running Julia binary is incompatible with the bounds in `Project.toml`. + Pkg has always obeyed this compat when working with Registry packages. This change affects mostly local packages +* `pkg> add` and `Pkg.add` will now add compat entries for new direct dependencies if the active environment is a + package (has a `name` and `uuid` entry). +* Dependencies can now be directly added as weak deps or extras via the `pkg> add --weak/extra Foo` or + `Pkg.add("Foo", target=:weakdeps/:extras)` forms. #### LinearAlgebra * `cbrt(::AbstractMatrix{<:Real})` is now defined and returns real-valued matrix cube roots of real-valued matrices ([#50661]). From 31f7df648f750897e245d169639cb1264ebc7404 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 22 Oct 2024 07:36:17 +0200 Subject: [PATCH 465/548] Remove NewPM pass exports. (#56269) All ecosystem consumers have switched to the string-based API. --- src/codegen-stubs.c | 16 ---------------- src/jl_exported_funcs.inc | 15 --------------- src/llvm-julia-passes.inc | 30 +++++++++++++++--------------- src/llvm_api.cpp | 32 -------------------------------- src/pipeline.cpp | 22 +++++++++++----------- 5 files changed, 26 insertions(+), 89 deletions(-) diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 7ddb68fd6b036..98ac063ba36d6 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -110,22 +110,6 @@ JL_DLLEXPORT uint64_t jl_getUnwindInfo_fallback(uint64_t dwAddr) JL_DLLEXPORT void jl_register_passbuilder_callbacks_fallback(void *PB) { } -#define MODULE_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT void LLVMExtraMPMAdd##CLASS##_fallback(void *PM) UNAVAILABLE -#define CGSCC_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT void LLVMExtraCGPMAdd##CLASS##_fallback(void *PM) UNAVAILABLE -#define FUNCTION_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT void LLVMExtraFPMAdd##CLASS##_fallback(void *PM) UNAVAILABLE -#define LOOP_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT void LLVMExtraLPMAdd##CLASS##_fallback(void *PM) UNAVAILABLE - -#include "llvm-julia-passes.inc" - -#undef MODULE_PASS -#undef CGSCC_PASS -#undef FUNCTION_PASS -#undef LOOP_PASS - //LLVM C api to the julia JIT JL_DLLEXPORT void* JLJITGetLLVMOrcExecutionSession_fallback(void* JIT) UNAVAILABLE diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index f712f154ed896..71a78b1c20fc7 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -547,21 +547,6 @@ YY(jl_getUnwindInfo) \ YY(jl_get_libllvm) \ YY(jl_register_passbuilder_callbacks) \ - YY(LLVMExtraMPMAddCPUFeaturesPass) \ - YY(LLVMExtraMPMAddRemoveNIPass) \ - YY(LLVMExtraMPMAddMultiVersioningPass) \ - YY(LLVMExtraMPMAddRemoveJuliaAddrspacesPass) \ - YY(LLVMExtraMPMAddRemoveAddrspacesPass) \ - YY(LLVMExtraMPMAddLowerPTLSPass) \ - YY(LLVMExtraFPMAddDemoteFloat16Pass) \ - YY(LLVMExtraFPMAddLateLowerGCPass) \ - YY(LLVMExtraFPMAddAllocOptPass) \ - YY(LLVMExtraFPMAddPropagateJuliaAddrspacesPass) \ - YY(LLVMExtraFPMAddLowerExcHandlersPass) \ - YY(LLVMExtraFPMAddGCInvariantVerifierPass) \ - YY(LLVMExtraFPMAddFinalLowerGCPass) \ - YY(LLVMExtraLPMAddJuliaLICMPass) \ - YY(LLVMExtraLPMAddLowerSIMDLoopPass) \ YY(JLJITGetLLVMOrcExecutionSession) \ YY(JLJITGetJuliaOJIT) \ YY(JLJITGetExternalJITDylib) \ diff --git a/src/llvm-julia-passes.inc b/src/llvm-julia-passes.inc index c41ecbba87b6a..523c9fbcd3402 100644 --- a/src/llvm-julia-passes.inc +++ b/src/llvm-julia-passes.inc @@ -1,26 +1,26 @@ //Module passes #ifdef MODULE_PASS -MODULE_PASS("CPUFeatures", CPUFeaturesPass, CPUFeaturesPass()) -MODULE_PASS("RemoveNI", RemoveNIPass, RemoveNIPass()) -MODULE_PASS("JuliaMultiVersioning", MultiVersioningPass, MultiVersioningPass()) -MODULE_PASS("RemoveJuliaAddrspaces", RemoveJuliaAddrspacesPass, RemoveJuliaAddrspacesPass()) -MODULE_PASS("RemoveAddrspaces", RemoveAddrspacesPass, RemoveAddrspacesPass()) -MODULE_PASS("LowerPTLSPass", LowerPTLSPass, LowerPTLSPass()) +MODULE_PASS("CPUFeatures", CPUFeaturesPass()) +MODULE_PASS("RemoveNI", RemoveNIPass()) +MODULE_PASS("JuliaMultiVersioning", MultiVersioningPass()) +MODULE_PASS("RemoveJuliaAddrspaces", RemoveJuliaAddrspacesPass()) +MODULE_PASS("RemoveAddrspaces", RemoveAddrspacesPass()) +MODULE_PASS("LowerPTLSPass", LowerPTLSPass()) #endif //Function passes #ifdef FUNCTION_PASS -FUNCTION_PASS("DemoteFloat16", DemoteFloat16Pass, DemoteFloat16Pass()) -FUNCTION_PASS("LateLowerGCFrame", LateLowerGCPass, LateLowerGCPass()) -FUNCTION_PASS("AllocOpt", AllocOptPass, AllocOptPass()) -FUNCTION_PASS("PropagateJuliaAddrspaces", PropagateJuliaAddrspacesPass, PropagateJuliaAddrspacesPass()) -FUNCTION_PASS("LowerExcHandlers", LowerExcHandlersPass, LowerExcHandlersPass()) -FUNCTION_PASS("GCInvariantVerifier", GCInvariantVerifierPass, GCInvariantVerifierPass()) -FUNCTION_PASS("FinalLowerGC", FinalLowerGCPass, FinalLowerGCPass()) +FUNCTION_PASS("DemoteFloat16", DemoteFloat16Pass()) +FUNCTION_PASS("LateLowerGCFrame", LateLowerGCPass()) +FUNCTION_PASS("AllocOpt", AllocOptPass()) +FUNCTION_PASS("PropagateJuliaAddrspaces", PropagateJuliaAddrspacesPass()) +FUNCTION_PASS("LowerExcHandlers", LowerExcHandlersPass()) +FUNCTION_PASS("GCInvariantVerifier", GCInvariantVerifierPass()) +FUNCTION_PASS("FinalLowerGC", FinalLowerGCPass()) #endif //Loop passes #ifdef LOOP_PASS -LOOP_PASS("JuliaLICM", JuliaLICMPass, JuliaLICMPass()) -LOOP_PASS("LowerSIMDLoop", LowerSIMDLoopPass, LowerSIMDLoopPass()) +LOOP_PASS("JuliaLICM", JuliaLICMPass()) +LOOP_PASS("LowerSIMDLoop", LowerSIMDLoopPass()) #endif diff --git a/src/llvm_api.cpp b/src/llvm_api.cpp index e98c375b711b3..8c48b5661f984 100644 --- a/src/llvm_api.cpp +++ b/src/llvm_api.cpp @@ -10,7 +10,6 @@ #endif #include "jitlayers.h" -#include "passes.h" #include #include @@ -58,14 +57,6 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::IRCompileLayer, LLVMOrcIRCompileLayerRef DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::MaterializationResponsibility, LLVMOrcMaterializationResponsibilityRef) -typedef struct LLVMOpaqueModulePassManager *LLVMModulePassManagerRef; -typedef struct LLVMOpaqueFunctionPassManager *LLVMFunctionPassManagerRef; -typedef struct LLVMOpaqueLoopPassManager *LLVMLoopPassManagerRef; - -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(llvm::ModulePassManager, LLVMModulePassManagerRef) -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(llvm::FunctionPassManager, LLVMFunctionPassManagerRef) -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(llvm::LoopPassManager, LLVMLoopPassManagerRef) - extern "C" { JL_DLLEXPORT_CODEGEN JuliaOJITRef JLJITGetJuliaOJIT_impl(void) @@ -150,27 +141,4 @@ JLJITGetIRCompileLayer_impl(JuliaOJITRef JIT) return wrap(&unwrap(JIT)->getIRCompileLayer()); } -#define MODULE_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT_CODEGEN void LLVMExtraMPMAdd##CLASS##_impl(LLVMModulePassManagerRef PM) \ - { \ - unwrap(PM)->addPass(CREATE_PASS); \ - } -#define FUNCTION_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT_CODEGEN void LLVMExtraFPMAdd##CLASS##_impl(LLVMFunctionPassManagerRef PM) \ - { \ - unwrap(PM)->addPass(CREATE_PASS); \ - } -#define LOOP_PASS(NAME, CLASS, CREATE_PASS) \ - JL_DLLEXPORT_CODEGEN void LLVMExtraLPMAdd##CLASS##_impl(LLVMLoopPassManagerRef PM) \ - { \ - unwrap(PM)->addPass(CREATE_PASS); \ - } - -#include "llvm-julia-passes.inc" - -#undef MODULE_PASS -#undef CGSCC_PASS -#undef FUNCTION_PASS -#undef LOOP_PASS - } // extern "C" diff --git a/src/pipeline.cpp b/src/pipeline.cpp index f8935070bb001..f8976099ee53c 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -617,29 +617,29 @@ namespace { void adjustPIC(PassInstrumentationCallbacks &PIC) JL_NOTSAFEPOINT { //Borrowed from LLVM PassBuilder.cpp:386 -#define MODULE_PASS(NAME, CLASS, CREATE_PASS) \ +#define MODULE_PASS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define MODULE_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \ +#define MODULE_PASS_WITH_PARAMS(NAME, CREATE_PASS, PARSER, PARAMS) \ PIC.addClassToPassName(CLASS, NAME); #define MODULE_ANALYSIS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define FUNCTION_PASS(NAME, CLASS, CREATE_PASS) \ +#define FUNCTION_PASS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \ +#define FUNCTION_PASS_WITH_PARAMS(NAME, CREATE_PASS, PARSER, PARAMS) \ PIC.addClassToPassName(CLASS, NAME); #define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); #define LOOPNEST_PASS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define LOOP_PASS(NAME, CLASS, CREATE_PASS) \ +#define LOOP_PASS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define LOOP_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \ +#define LOOP_PASS_WITH_PARAMS(NAME, CREATE_PASS, PARSER, PARAMS) \ PIC.addClassToPassName(CLASS, NAME); #define LOOP_ANALYSIS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define CGSCC_PASS(NAME, CLASS, CREATE_PASS) \ +#define CGSCC_PASS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); -#define CGSCC_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \ +#define CGSCC_PASS_WITH_PARAMS(NAME, CREATE_PASS, PARSER, PARAMS) \ PIC.addClassToPassName(CLASS, NAME); #define CGSCC_ANALYSIS(NAME, CREATE_PASS) \ PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME); @@ -899,7 +899,7 @@ static void registerCallbacks(PassBuilder &PB) JL_NOTSAFEPOINT { PB.registerPipelineParsingCallback( [](StringRef Name, FunctionPassManager &PM, ArrayRef InnerPipeline) { -#define FUNCTION_PASS(NAME, CLASS, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } +#define FUNCTION_PASS(NAME, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } #include "llvm-julia-passes.inc" #undef FUNCTION_PASS if (Name.consume_front("GCInvariantVerifier")) { @@ -921,7 +921,7 @@ static void registerCallbacks(PassBuilder &PB) JL_NOTSAFEPOINT { PB.registerPipelineParsingCallback( [](StringRef Name, ModulePassManager &PM, ArrayRef InnerPipeline) { -#define MODULE_PASS(NAME, CLASS, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } +#define MODULE_PASS(NAME, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } #include "llvm-julia-passes.inc" #undef MODULE_PASS if (Name.consume_front("LowerPTLSPass")) { @@ -964,7 +964,7 @@ static void registerCallbacks(PassBuilder &PB) JL_NOTSAFEPOINT { PB.registerPipelineParsingCallback( [](StringRef Name, LoopPassManager &PM, ArrayRef InnerPipeline) { -#define LOOP_PASS(NAME, CLASS, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } +#define LOOP_PASS(NAME, CREATE_PASS) if (Name == NAME) { PM.addPass(CREATE_PASS); return true; } #include "llvm-julia-passes.inc" #undef LOOP_PASS return false; From 7d4b2b78e66045a7249a96d195621f2b7d20c1fd Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Tue, 22 Oct 2024 15:27:42 +0200 Subject: [PATCH 466/548] jitlayers: use std::make_tuple instead of tuple constructor (#56287) this should be safer for the type deduction and fixes a build error for macos on Yggdrasil (https://github.com/JuliaPackaging/Yggdrasil/pull/9660): ``` src/jitlayers.cpp:665:54: error: no viable constructor or deduction guide for deduction of template arguments of 'tuple' 665 | incompletemodules.insert(std::pair(codeinst, std::tuple(std::move(params), waiting))); ``` The Yggdrasil environment is a bit special with a rather new clang (version 17) but an old macos sdk and I don't know exactly in which circumstances this triggers. But I think `std::make_tuple` should be more reliable when the tuple types are not specified. cc: @fingolfin --- src/jitlayers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 8b8004af03616..c8d8356687dcf 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -662,7 +662,7 @@ static void jl_emit_codeinst_to_jit( int waiting = jl_analyze_workqueue(codeinst, params); if (waiting) { auto release = std::move(params.tsctx_lock); // unlock again before moving from it - incompletemodules.insert(std::pair(codeinst, std::tuple(std::move(params), waiting))); + incompletemodules.insert(std::pair(codeinst, std::make_tuple(std::move(params), waiting))); } else { finish_params(result_m.getModuleUnlocked(), params); From ab22f982427184b0a50ba407e4f1cbedbc862ced Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 22 Oct 2024 09:49:42 -0400 Subject: [PATCH 467/548] move time_imports and trace_* macros to Base but remain owned by InteractiveUtils (#56276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way all packages can be timed including InteractiveUtils and its deps (Base64, JuliaSyntaxHighlighting, Markdown, StyledStrings). With this PR ``` % ./julia --start=no -e "@time Base.@time_imports using REPL" 41.8 ms StyledStrings ┌ 0.1 ms JuliaSyntaxHighlighting.__init__() 14.2 ms JuliaSyntaxHighlighting 1.0 ms Base64 ┌ 0.0 ms Markdown.__init__() 9.6 ms Markdown 2.2 ms InteractiveUtils 0.3 ms Unicode ┌ 0.0 ms REPL.REPLCompletions.__init__() ├ 0.0 ms REPL.__init__() 95.7 ms REPL 0.225907 seconds (290.95 k allocations: 16.761 MiB) ``` Otherwise ``` % ./julia --start=no -e "using InteractiveUtils; @time @time_imports using REPL" 0.5 ms Unicode ┌ 0.0 ms REPL.REPLCompletions.__init__() ├ 0.1 ms REPL.__init__() 107.5 ms REPL 0.127016 seconds (164.18 k allocations: 9.199 MiB) ``` Also the `@trace_compile` and `@trace_dispatch` macros for the same reason. --- base/timing.jl | 35 +++++++++++++++++++++++++ stdlib/InteractiveUtils/src/macros.jl | 37 +++------------------------ 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 4880951f0a32d..1de3727756829 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -628,3 +628,38 @@ macro timed(ex) ) end end + +# Exported, documented, and tested in InteractiveUtils +# here so it's possible to time/trace all imports, including InteractiveUtils and its deps +macro time_imports(ex) + quote + try + Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) + $(esc(ex)) + finally + Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) + end + end +end + +macro trace_compile(ex) + quote + try + ccall(:jl_force_trace_compile_timing_enable, Cvoid, ()) + $(esc(ex)) + finally + ccall(:jl_force_trace_compile_timing_disable, Cvoid, ()) + end + end +end + +macro trace_dispatch(ex) + quote + try + ccall(:jl_force_trace_dispatch_enable, Cvoid, ()) + $(esc(ex)) + finally + ccall(:jl_force_trace_dispatch_disable, Cvoid, ()) + end + end +end diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 211687df47954..e338d8626fb0f 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -4,6 +4,10 @@ import Base: typesof, insert!, replace_ref_begin_end!, infer_effects +# defined in Base so it's possible to time all imports, including InteractiveUtils and its deps +# via. `Base.@time_imports` etc. +import Base: @time_imports, @trace_compile, @trace_dispatch + separate_kwargs(args...; kwargs...) = (args, values(kwargs)) """ @@ -245,39 +249,6 @@ macro code_lowered(ex0...) end end -macro time_imports(ex) - quote - try - Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) - $(esc(ex)) - finally - Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) - end - end -end - -macro trace_compile(ex) - quote - try - ccall(:jl_force_trace_compile_timing_enable, Cvoid, ()) - $(esc(ex)) - finally - ccall(:jl_force_trace_compile_timing_disable, Cvoid, ()) - end - end -end - -macro trace_dispatch(ex) - quote - try - ccall(:jl_force_trace_dispatch_enable, Cvoid, ()) - $(esc(ex)) - finally - ccall(:jl_force_trace_dispatch_disable, Cvoid, ()) - end - end -end - """ @functionloc From 6de6b46b7e5f5438c04ced8510296e0a63507264 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:07:54 -0400 Subject: [PATCH 468/548] lowering: split `finally` blocks for exceptional control-flow (#55876) This change duplicates `finally` blocks in lowered IR, so that they can have a static nesting depth in the `try-catch` hierarchy. Previously, `finally` control-flow looked like this: ``` error non-error \ / \ / | finally block | / \ / \ error non-error ``` This kind of flow is a problem, because in a couple places the compiler assumes that it can actually "color" the CFG such that there is a static nesting depth at each BasicBlock (i.e. each BasicBlock can be labeled w/ a unique enclosing `try` / `catch` scope). The above `finally` pattern violates that assumption. In an upcoming PR, I want to extend the lifetimes of our Event Handlers (`jl_handler_t`) until the end of a `catch` block (rather than the start) which noticeably breaks `llvm-lower-handlers.cpp`. (@keno was very clear about this assumption in the comments for that pass.) Behaviorally this was _mostly_ benign, except for some mis-handling of an erroring entry that turns into a non-erroring exit. That could be fixed by banning `break` and `return` within `finally` blocks or making the lowering more complicated, but this PR instead splits the `finally` block into an erroring and non-erroring path so that we can attach the `catch` handler appropriately. --- src/julia-syntax.scm | 10 +++++++--- test/exceptions.jl | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4b3e6ae96898b..b48cb48bf0b79 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4854,10 +4854,14 @@ f(x) = yt(x) ;; separate trycatch and tryfinally blocks earlier. (mark-label catch) (if finally - (begin (enter-finally-block catchcode #f) ;; enter block via exception + (begin (set! finally-handler last-finally-handler) + (set! catch-token-stack (cons handler-token catch-token-stack)) + (compile (caddr e) break-labels #f #f) ;; enter block via exception + (emit '(call (top rethrow))) + (emit-return tail '(null)) ; unreachable + (set! catch-token-stack (cdr catch-token-stack)) (mark-label endl) ;; non-exceptional control flow enters here - (set! finally-handler last-finally-handler) - (compile (caddr e) break-labels #f #f) + (compile (renumber-assigned-ssavalues (caddr e)) break-labels #f #f) ;; emit actions to be taken at exit of finally ;; block, depending on the tag variable `finally` (let loop ((actions (caddr my-finally-handler))) diff --git a/test/exceptions.jl b/test/exceptions.jl index eb0bbaec35090..1e52c7a2fe2c3 100644 --- a/test/exceptions.jl +++ b/test/exceptions.jl @@ -241,6 +241,18 @@ end end end)() @test length(Base.current_exceptions()) == 0 + + (()-> begin + while true + try + error("foo") + finally + break + end + end + @test length(Base.current_exceptions()) == 0 + end)() + @test length(Base.current_exceptions()) == 0 end @testset "Deep exception stacks" begin From e4101b71dbcd766b2e4f162320d1d64c0f03c6f3 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 22 Oct 2024 12:26:07 -0400 Subject: [PATCH 469/548] add recompile comment in --trace-compile in terminal color mode too (#56275) Update: Just adds the comment in color terminal mode too --- I didn't think adding the `# recompile` text to the end in the repl was a good idea as it increases likelihood of text wrapping. And the color should be sufficient for local review, but when people copy from a color terminal we lose the recompile info. So this just adds a zero-length change indicator, for people to look out for. --- src/gf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gf.c b/src/gf.c index e77c950c38ae4..285942cd157c5 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2566,12 +2566,10 @@ static void record_precompile_statement(jl_method_instance_t *mi, double compila jl_static_show(s_precompile, mi->specTypes); jl_printf(s_precompile, ")"); if (is_recompile) { + jl_printf(s_precompile, " # recompile"); if (s_precompile == JL_STDERR && jl_options.color != JL_OPTIONS_COLOR_OFF) { jl_printf(s_precompile, "\e[0m"); } - else { - jl_printf(s_precompile, " # recompile"); - } } jl_printf(s_precompile, "\n"); if (s_precompile != JL_STDERR) From 7c1935d5bf2a7b13008a9494f626febbccd8bf7c Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 22 Oct 2024 17:32:46 -0400 Subject: [PATCH 470/548] Some usability follow-ups and fixes for the world macro (#56273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Allow fully qualified module names: `@world(Foo.Bar.baz, 1)` 2. Correct the printing order of the world macro and module qualifier 3. Add pretty printing for Binding(Partition). Example of the test: ``` julia> GlobalRef(Rebinding, :Foo).binding Binding Main.Rebinding.Foo 27497:∞ - undefined binding - guard entry 0:27496 - constant binding to @world(Main.Rebinding.Foo, 0:27496) ``` --------- Co-authored-by: Shuhei Kadowaki --- base/essentials.jl | 11 +++++--- base/range.jl | 6 ++--- base/show.jl | 63 ++++++++++++++++++++++++++++++++++++++++++++-- test/rebinding.jl | 6 +++++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index a07aaa6769ed2..750ee0f9c434c 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1258,8 +1258,8 @@ arbitrary code in fixed worlds. `world` may be `UnitRange`, in which case the ma will error unless the binding is valid and has the same value across the entire world range. -The `@world` macro is primarily used in the priniting of bindings that are no longer available -in the current world. +The `@world` macro is primarily used in the printing of bindings that are no longer +available in the current world. ## Example ``` @@ -1283,9 +1283,12 @@ julia> fold """ macro world(sym, world) if isa(sym, Symbol) - return :($(_resolve_in_world)($world, $(QuoteNode(GlobalRef(__module__, sym))))) + return :($(_resolve_in_world)($(esc(world)), $(QuoteNode(GlobalRef(__module__, sym))))) elseif isa(sym, GlobalRef) - return :($(_resolve_in_world)($world, $(QuoteNode(sym)))) + return :($(_resolve_in_world)($(esc(world)), $(QuoteNode(sym)))) + elseif isa(sym, Expr) && sym.head === :(.) && + length(sym.args) == 2 && isa(sym.args[2], QuoteNode) && isa(sym.args[2].value, Symbol) + return :($(_resolve_in_world)($(esc(world)), $(GlobalRef)($(esc(sym.args[1])), $(sym.args[2])))) else error("`@world` requires a symbol or GlobalRef") end diff --git a/base/range.jl b/base/range.jl index 3301335785878..cee15db39b911 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1685,9 +1685,9 @@ end # The rest of this is defined in essentials.jl, but UnitRange is not available function _resolve_in_world(worlds::UnitRange, gr::GlobalRef) # Validate that this binding's reference covers the entire world range - bpart = lookup_binding_partition(first(worlds), gr) - if bpart.max_world < last(world) + bpart = lookup_binding_partition(UInt(first(worlds)), gr) + if bpart.max_world < last(worlds) error("Binding does not cover the full world range") end - _resolve_in_world(last(world), gr) + _resolve_in_world(UInt(last(worlds)), gr) end diff --git a/base/show.jl b/base/show.jl index 3aeb267b4a696..93f0a58b7cdd6 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1061,6 +1061,8 @@ function show_type_name(io::IO, tn::Core.TypeName) sym = (globfunc ? globname : tn.name)::Symbol globfunc && print(io, "typeof(") quo = false + world = check_world_bounded(tn) + world !== nothing && print(io, "@world(") if !(get(io, :compact, false)::Bool) # Print module prefix unless type is visible from module passed to # IOContext If :module is not set, default to Main. @@ -1078,8 +1080,6 @@ function show_type_name(io::IO, tn::Core.TypeName) end end end - world = check_world_bounded(tn) - world !== nothing && print(io, "@world(") show_sym(io, sym) world !== nothing && print(io, ", ", world, ")") quo && print(io, ")") @@ -3359,3 +3359,62 @@ end function show(io::IO, ::MIME"text/plain", oc::Core.OpaqueClosure{A, R}) where {A, R} show(io, oc) end + +# printing bindings and partitions +function print_partition(io::IO, partition::Core.BindingPartition) + print(io, partition.min_world) + print(io, ":") + max_world = @atomic partition.max_world + if max_world == typemax(UInt) + print(io, '∞') + else + print(io, max_world) + end + print(io, " - ") + kind = binding_kind(partition) + if is_some_const_binding(kind) + print(io, "constant binding to ") + print(io, partition_restriction(partition)) + elseif kind == BINDING_KIND_GUARD + print(io, "undefined binding - guard entry") + elseif kind == BINDING_KIND_FAILED + print(io, "ambiguous binding - guard entry") + elseif kind == BINDING_KIND_DECLARED + print(io, "undefined, but declared using `global` - guard entry") + elseif kind == BINDING_KIND_IMPLICIT + print(io, "implicit `using` from ") + print(io, partition_restriction(partition)) + elseif kind == BINDING_KIND_EXPLICIT + print(io, "explicit `using` from ") + print(io, partition_restriction(partition)) + elseif kind == BINDING_KIND_IMPORTED + print(io, "explicit `import` from ") + print(io, partition_restriction(partition)) + else + @assert kind == BINDING_KIND_GLOBAL + print(io, "global variable with type ") + print(io, partition_restriction(partition)) + end +end + +function show(io::IO, ::MIME"text/plain", partition::Core.BindingPartition) + print(io, "BindingPartition ") + print_partition(io, partition) +end + +function show(io::IO, ::MIME"text/plain", bnd::Core.Binding) + print(io, "Binding ") + print(io, bnd.globalref) + if !isdefined(bnd, :partitions) + print(io, "No partitions") + else + partition = @atomic bnd.partitions + while true + println(io) + print(io, " ") + print_partition(io, partition) + isdefined(partition, :next) || break + partition = @atomic partition.next + end + end +end diff --git a/test/rebinding.jl b/test/rebinding.jl index 4066d91bc4b9b..564be70e44913 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -7,6 +7,7 @@ module Rebinding struct Foo x::Int end + const defined_world_age = Base.tls_world_age() x = Foo(1) @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_CONST @@ -15,4 +16,9 @@ module Rebinding @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD @test contains(repr(x), "@world") + + # Tests for @world syntax + @test Base.@world(Foo, defined_world_age) == typeof(x) + @test Base.@world(Rebinding.Foo, defined_world_age) == typeof(x) + @test Base.@world((@__MODULE__).Foo, defined_world_age) == typeof(x) end From 049d92a2ac506316ca2413e103647f72ce847b56 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 22 Oct 2024 18:14:39 -0400 Subject: [PATCH 471/548] REPL: Don't search for ?( completions when hinting (#56278) --- stdlib/REPL/src/REPLCompletions.jl | 50 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 42480aea91605..3188a6ca42a12 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1225,33 +1225,35 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif partial = string[1:pos] inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false)) - # ?(x, y)TAB lists methods you can call with these objects - # ?(x, y TAB lists methods that take these objects as the first two arguments - # MyModule.?(x, y)TAB restricts the search to names in MyModule - rexm = match(r"(\w+\.|)\?\((.*)$", partial) - if rexm !== nothing - # Get the module scope - if isempty(rexm.captures[1]) - callee_module = context_module - else - modname = Symbol(rexm.captures[1][1:end-1]) - if isdefined(context_module, modname) - callee_module = getfield(context_module, modname) - if !isa(callee_module, Module) + if !hint # require a tab press for completion of these + # ?(x, y)TAB lists methods you can call with these objects + # ?(x, y TAB lists methods that take these objects as the first two arguments + # MyModule.?(x, y)TAB restricts the search to names in MyModule + rexm = match(r"(\w+\.|)\?\((.*)$", partial) + if rexm !== nothing + # Get the module scope + if isempty(rexm.captures[1]) + callee_module = context_module + else + modname = Symbol(rexm.captures[1][1:end-1]) + if isdefined(context_module, modname) + callee_module = getfield(context_module, modname) + if !isa(callee_module, Module) + callee_module = context_module + end + else callee_module = context_module end - else - callee_module = context_module end - end - moreargs = !endswith(rexm.captures[2], ')') - callstr = "_(" * rexm.captures[2] - if moreargs - callstr *= ')' - end - ex_org = Meta.parse(callstr, raise=false, depwarn=false) - if isa(ex_org, Expr) - return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false + moreargs = !endswith(rexm.captures[2], ')') + callstr = "_(" * rexm.captures[2] + if moreargs + callstr *= ')' + end + ex_org = Meta.parse(callstr, raise=false, depwarn=false) + if isa(ex_org, Expr) + return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false + end end end From 73b85cfc04d83cb4b630dbd36ad2c270cf548330 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 22 Oct 2024 21:12:50 -0400 Subject: [PATCH 472/548] Use a curried helper for module-local eval/include (#55949) In https://github.com/JuliaLang/julia/pull/55466, the automatically added `include` method for non-bare modules was adjusted to conform the signature to the version of those methods in Main (defined in sysimg.jl, since `Main` is technically a bare module). Unfortunately, this broke some downstream packages which overload Base.include with additional functionality and got broken by the additional type restriction. The motivation in https://github.com/JuliaLang/julia/pull/55466 was to give a slightly nicer MethodError. While I don't think this is per-se a particularly strong justification, I do agree that it's awkward for the `Main` version of these functions to have (marginally) different behavior than the version of these functions that gets introduced automatically in new modules (Which has been the case ever since [1], which added the AbstractString restriction in `Main`, but not in the auto-generated versions). This is particularly true, because we use the `Main` version to document the auto-introduction of these methods, which has regularly been a point of confusion. This PR tries to address this problem once and for all, but just not generating special methods into every new module. Instead, there are curried helpers for eval and include in Core and Base (respectively), which can be added to a module simply by doing `const include = IncludeInto(MyModule)` (and similarly for `eval`). As before, this happens automatically for non-bare modules. It thus conforms the behavior of the `Main` version of these functions and the auto-generated versions by construction. Additionally, it saves us having to generate all the additional code/types/objects, etc associated with having extra generic functions in each new module. The impact of this isn't huge, because there aren't that many modules, but it feels conceptually nicer. There is a little bit of extra work in this PR because we have special snowflake backtrace printing code for the `include` machinery, which needs adjusting, but other than that the change is straightforward. [1] https://github.com/JuliaLang/julia/commit/957848b899c7b5389af34cf815aa7bd2b6e2bf82 --------- Co-authored-by: Jameson Nash --- base/Base.jl | 14 ++++++++++++-- base/boot.jl | 8 ++++++-- base/docs/basedocs.jl | 2 +- base/errorshow.jl | 5 ++++- base/loading.jl | 8 ++++---- base/show.jl | 19 +++++++++++++++++++ base/sysimg.jl | 8 ++------ src/jlfrontend.scm | 22 ---------------------- src/toplevel.c | 14 ++++++++++---- test/docs.jl | 2 +- test/reflection.jl | 4 ++-- 11 files changed, 61 insertions(+), 45 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 5fb764bd4cc01..bfac74e5d7bab 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -25,6 +25,11 @@ function include(mod::Module, path::String) end include(path::String) = include(Base, path) +struct IncludeInto <: Function + m::Module +end +(this::IncludeInto)(fname::AbstractString) = include(this.m, fname) + # from now on, this is now a top-module for resolving syntax const is_primary_base_module = ccall(:jl_module_parent, Ref{Module}, (Any,), Base) === Core.Main ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Base, is_primary_base_module) @@ -572,6 +577,9 @@ include("precompilation.jl") for m in methods(include) delete_method(m) end +for m in methods(IncludeInto(Base)) + delete_method(m) +end # This method is here only to be overwritten during the test suite to test # various sysimg related invalidation scenarios. @@ -579,8 +587,10 @@ a_method_to_overwrite_in_test() = inferencebarrier(1) # These functions are duplicated in client.jl/include(::String) for # nicer stacktraces. Modifications here have to be backported there -include(mod::Module, _path::AbstractString) = _include(identity, mod, _path) -include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexpr, mod, _path) +@noinline include(mod::Module, _path::AbstractString) = _include(identity, mod, _path) +@noinline include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexpr, mod, _path) +(this::IncludeInto)(fname::AbstractString) = include(identity, this.m, fname) +(this::IncludeInto)(mapexpr::Function, fname::AbstractString) = include(mapexpr, this.m, fname) # External libraries vendored into Base Core.println("JuliaSyntax/src/JuliaSyntax.jl") diff --git a/base/boot.jl b/base/boot.jl index 861c83a2edac5..ed3e22391f215 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -454,9 +454,13 @@ Nothing() = nothing # This should always be inlined getptls() = ccall(:jl_get_ptls_states, Ptr{Cvoid}, ()) -include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) +include(m::Module, fname::String) = (@noinline; ccall(:jl_load_, Any, (Any, Any), m, fname)) +eval(m::Module, @nospecialize(e)) = (@noinline; ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e)) -eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) +struct EvalInto <: Function + m::Module +end +(this::EvalInto)(@nospecialize(e)) = eval(this.m, e) mutable struct Box contents::Any diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 0d5d5ac00e8d0..b080bf51e5e98 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2580,7 +2580,7 @@ cases. See also [`setproperty!`](@ref Base.setproperty!) and [`getglobal`](@ref) # Examples -```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*\\n.*)*" julia> module M; global a; end; julia> M.a # same as `getglobal(M, :a)` diff --git a/base/errorshow.jl b/base/errorshow.jl index 20bdee1de6ec0..7225a024f529e 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -850,7 +850,10 @@ function _simplify_include_frames(trace) for i in length(trace):-1:1 frame::StackFrame, _ = trace[i] mod = parentmodule(frame) - if first_ignored === nothing + if mod === Base && frame.func === :IncludeInto || + mod === Core && frame.func === :EvalInto + kept_frames[i] = false + elseif first_ignored === nothing if mod === Base && frame.func === :_include # Hide include() machinery by default first_ignored = i diff --git a/base/loading.jl b/base/loading.jl index dfcf22b2b0751..69bb332193519 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2890,12 +2890,12 @@ julia> rm("testfile.jl") ``` """ function evalfile(path::AbstractString, args::Vector{String}=String[]) - return Core.eval(Module(:__anon__), + m = Module(:__anon__) + return Core.eval(m, Expr(:toplevel, :(const ARGS = $args), - :(eval(x) = $(Expr(:core, :eval))(__anon__, x)), - :(include(x::AbstractString) = $(Expr(:top, :include))(__anon__, x)), - :(include(mapexpr::Function, x::AbstractString) = $(Expr(:top, :include))(mapexpr, __anon__, x)), + :(const include = $(Base.IncludeInto(m))), + :(const eval = $(Core.EvalInto(m))), :(include($path)))) end evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...]) diff --git a/base/show.jl b/base/show.jl index 93f0a58b7cdd6..ee467ae90ff50 100644 --- a/base/show.jl +++ b/base/show.jl @@ -3418,3 +3418,22 @@ function show(io::IO, ::MIME"text/plain", bnd::Core.Binding) end end end + +# Special pretty printing for EvalInto/IncludeInto +function show(io::IO, ii::IncludeInto) + if getglobal(ii.m, :include) === ii + print(io, ii.m) + print(io, ".include") + else + show_default(io, ii) + end +end + +function show(io::IO, ei::Core.EvalInto) + if getglobal(ei.m, :eval) === ei + print(io, ei.m) + print(io, ".eval") + else + show_default(io, ei) + end +end diff --git a/base/sysimg.jl b/base/sysimg.jl index 966ed76751f28..ccc8ef38e81bc 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -32,11 +32,7 @@ Use [`Base.include`](@ref) to evaluate a file into another module. !!! compat "Julia 1.5" Julia 1.5 is required for passing the `mapexpr` argument. """ -include(mapexpr::Function, fname::AbstractString) = Base._include(mapexpr, Main, fname) -function include(fname::AbstractString) - isa(fname, String) || (fname = Base.convert(String, fname)::String) - Base._include(identity, Main, fname) -end +const include = Base.IncludeInto(Main) """ eval(expr) @@ -45,7 +41,7 @@ Evaluate an expression in the global scope of the containing module. Every `Module` (except those defined with `baremodule`) has its own 1-argument definition of `eval`, which evaluates expressions in that module. """ -eval(x) = Core.eval(Main, x) +const eval = Core.EvalInto(Main) # Ensure this file is also tracked pushfirst!(Base._included_files, (@__MODULE__, abspath(@__FILE__))) diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 463e39c41d00a..808af18ebfdbd 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -199,28 +199,6 @@ (error-wrap (lambda () (julia-expand-macroscope expr)))) -;; construct default definitions of `eval` for non-bare modules -;; called by jl_eval_module_expr -(define (module-default-defs name file line) - (jl-expand-to-thunk - (let* ((loc (if (and (eq? file 'none) (eq? line 0)) '() `((line ,line ,file)))) - (x (if (eq? name 'x) 'y 'x)) - (mex (if (eq? name 'mapexpr) 'map_expr 'mapexpr))) - `(block - (= (call eval ,x) - (block - ,@loc - (call (core eval) ,name ,x))) - (= (call include (:: ,x (top AbstractString))) - (block - ,@loc - (call (core _call_latest) (top include) ,name ,x))) - (= (call include (:: ,mex (top Function)) (:: ,x (top AbstractString))) - (block - ,@loc - (call (core _call_latest) (top include) ,mex ,name ,x))))) - file line)) - ; run whole frontend on a string. useful for testing. (define (fe str) (expand-toplevel-expr (julia-parse str) 'none 0)) diff --git a/src/toplevel.c b/src/toplevel.c index 6f2e0cf77568a..c2fbc38d067eb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -206,11 +206,17 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex if (std_imports) { if (jl_base_module != NULL) { jl_add_standard_imports(newm); + jl_datatype_t *include_into = (jl_datatype_t *)jl_get_global(jl_base_module, jl_symbol("IncludeInto")); + if (include_into) { + form = jl_new_struct(include_into, newm); + jl_set_const(newm, jl_symbol("include"), form); + } + } + jl_datatype_t *eval_into = (jl_datatype_t *)jl_get_global(jl_core_module, jl_symbol("EvalInto")); + if (eval_into) { + form = jl_new_struct(eval_into, newm); + jl_set_const(newm, jl_symbol("eval"), form); } - // add `eval` function - form = jl_call_scm_on_ast_and_loc("module-default-defs", (jl_value_t*)name, newm, filename, lineno); - jl_toplevel_eval_flex(newm, form, 0, 1, &filename, &lineno); - form = NULL; } newm->file = jl_symbol(filename); diff --git a/test/docs.jl b/test/docs.jl index 92d45fe05e397..8db9db30b8463 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -101,7 +101,7 @@ end @test Docs.undocumented_names(_ModuleWithUndocumentedNames) == [Symbol("@foo"), :f, :⨳] @test isempty(Docs.undocumented_names(_ModuleWithSomeDocumentedNames)) -@test Docs.undocumented_names(_ModuleWithSomeDocumentedNames; private=true) == [:eval, :g, :include] +@test Docs.undocumented_names(_ModuleWithSomeDocumentedNames; private=true) == [:g] # issue #11548 diff --git a/test/reflection.jl b/test/reflection.jl index 634390e0680d1..8c701acb9c09d 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -179,7 +179,7 @@ let @test Base.binding_module(TestMod7648.TestModSub9475, :b9475) == TestMod7648.TestModSub9475 defaultset = Set(Symbol[:Foo7648, :TestMod7648, :a9475, :c7648, :f9475, :foo7648, :foo7648_nomethods]) allset = defaultset ∪ Set(Symbol[ - Symbol("#eval"), Symbol("#foo7648"), Symbol("#foo7648_nomethods"), Symbol("#include"), + Symbol("#foo7648"), Symbol("#foo7648_nomethods"), :TestModSub9475, :d7648, :eval, :f7648, :include]) imported = Set(Symbol[:convert, :curmod_name, :curmod]) usings_from_Test = Set(Symbol[ @@ -265,7 +265,7 @@ let defaultset = Set((:A,)) imported = Set((:M2,)) usings_from_Base = delete!(Set(names(Module(); usings=true)), :anonymous) # the name of the anonymous module itself usings = Set((:A, :f, :C, :y, :M1, :m1_x)) ∪ usings_from_Base - allset = Set((:A, :B, :C, :eval, :include, Symbol("#eval"), Symbol("#include"))) + allset = Set((:A, :B, :C, :eval, :include)) @test Set(names(TestMod54609.A)) == defaultset @test Set(names(TestMod54609.A, imported=true)) == defaultset ∪ imported @test Set(names(TestMod54609.A, usings=true)) == defaultset ∪ usings From be0ce9dbf0597c1ff50fc73f3d197a19708c4cd3 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 23 Oct 2024 01:14:02 -0400 Subject: [PATCH 473/548] Don't try to allocate new binding partitions from static show (#56298) In particular static show is used inside the GC for profiling, which showed up as a segfault on CI, e.g. in https://buildkite.com/julialang/julia-master/builds/41407#0192b628-47f3-49f9-a081-cd2708eb6121. GC check didn't catch it because that file is explicitly exempt: https://github.com/JuliaLang/julia/blob/master/src/Makefile#L504 --- src/julia.h | 1 + src/module.c | 21 +++++++++++++++++++++ src/rtutils.c | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/julia.h b/src/julia.h index dd79dbb82c28d..1d36dba519700 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1848,6 +1848,7 @@ JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, size_t len); JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void); JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b JL_PROPAGATES_ROOT); JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b JL_PROPAGATES_ROOT); +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world, jl_code_instance_t **cache); diff --git a/src/module.c b/src/module.c index f1098e22ff522..9b4d26cc7b000 100644 --- a/src/module.c +++ b/src/module.c @@ -48,6 +48,7 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) return bpart; jl_binding_partition_t *new_bpart = new_binding_partition(); jl_atomic_store_relaxed(&new_bpart->next, bpart); + jl_gc_wb_fresh(new_bpart, bpart); if (bpart) new_bpart->min_world = jl_atomic_load_relaxed(&bpart->max_world) + 1; jl_atomic_store_relaxed(&new_bpart->max_world, max_world); @@ -350,6 +351,26 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b) return decode_restriction_value(pku); } +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t *b) +{ + // Unlike jl_get_binding_value_if_const this doesn't try to allocate new binding partitions if they + // don't already exist, making this JL_NOTSAFEPOINT. + if (!b) + return NULL; + jl_binding_partition_t *bpart = jl_atomic_load_relaxed(&b->partitions); + if (!bpart) + return NULL; + size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); + if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) + return NULL; + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return NULL; + if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return NULL; + return decode_restriction_value(pku); +} + JL_DLLEXPORT jl_value_t *jl_bpart_get_restriction_value(jl_binding_partition_t *bpart) { jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); diff --git a/src/rtutils.c b/src/rtutils.c index 85a9be5e0b1da..faa087dcb077d 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -661,7 +661,7 @@ static int is_globname_binding(jl_value_t *v, jl_datatype_t *dv) JL_NOTSAFEPOINT jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL; if (globname && dv->name->module) { jl_binding_t *b = jl_get_module_binding(dv->name->module, globname, 0); - jl_value_t *bv = jl_get_binding_value_if_const(b); + jl_value_t *bv = jl_get_binding_value_if_resolved_and_const(b); // The `||` makes this function work for both function instances and function types. if (bv && (bv == v || jl_typeof(bv) == v)) return 1; From 5e4fb519b5955b2f9ff9a26bd1dc5454561ecef7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 23 Oct 2024 15:26:38 +0530 Subject: [PATCH 474/548] Fix fetching parent in symmetric algebra (#56286) We only need the `parent` of the result if it is a triangular matrix. For other structurally triangular matrices, such as `Diagonal`s, we may use these directly in the `Hermitian` constructor. The operation proceeds as (assuming `H isa Hermitian` with `H.uplo == 'U'`): ```julia function +(H::Hermitian, H::Hermitian) U = uppertriangular(parent(H)) Ures = U + U data = Ures isa UpperTriangular ? parent(Ures) : Ures # this PR Hermitian(data, :U) end ``` This accounts for the fact that `Ures` may not be an `UpperTriangular`, as `uppertriangular` might be specialized by the parent. In such cases we should not extract the parent. Currently, only `Diagonal` specializes `uppertriangular`, so this issue only exists for a `Hermitian` or a `Symmetric` that wraps a `Diagonal`. Fixes https://github.com/JuliaLang/julia/issues/56283 --- stdlib/LinearAlgebra/src/symmetric.jl | 4 +++- stdlib/LinearAlgebra/test/symmetric.jl | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index e17eb80d25453..265995d9e7806 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -307,7 +307,9 @@ function applytri(f, A::HermOrSym, B::HermOrSym) f(uppertriangular(_conjugation(A)(A.data)), uppertriangular(B.data)) end end -parentof_applytri(f, args...) = applytri(parent ∘ f, args...) +_parent_tri(U::UpperOrLowerTriangular) = parent(U) +_parent_tri(U) = U +parentof_applytri(f, args...) = _parent_tri(applytri(f, args...)) isdiag(A::HermOrSym) = applytri(isdiag, A) diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 7a51ab9d454af..3aef23617b942 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1160,4 +1160,11 @@ end @test symT-s == Array(symT) - Array(s) end +@testset "issue #56283" begin + a = 1.0 + D = Diagonal(randn(10)) + H = Hermitian(D*D') + @test a*H == H +end + end # module TestSymmetric From 133051f20f5995d4128f9c7973efff62ed25e919 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 23 Oct 2024 07:59:06 -0400 Subject: [PATCH 475/548] REPL: fix closing quote on completing files in a ~ path (#56253) --- stdlib/REPL/src/REPLCompletions.jl | 18 +++++++----------- stdlib/REPL/test/replcompletions.jl | 27 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 3188a6ca42a12..d230b7b5fd232 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -935,17 +935,11 @@ function get_import_mode(s::String) return nothing end -function close_path_completion(dir, paths, str, pos) - length(paths) == 1 || return false # Only close if there's a single choice... - path = (paths[1]::PathCompletion).path +function close_path_completion(dir, path, str, pos) path = unescape_string(replace(path, "\\\$"=>"\$")) path = joinpath(dir, path) # ...except if it's a directory... - try - isdir(path) - catch e - e isa Base.IOError || rethrow() # `path` cannot be determined to be a file - end && return false + Base.isaccessibledir(path) && return false # ...and except if there's already a " at the cursor. return lastindex(str) <= pos || str[nextind(str, pos)] != '"' end @@ -1358,10 +1352,12 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif if !isnothing(path) paths, dir, success = complete_path(path::String, string_escape=true) - if close_path_completion(dir, paths, path, pos) - p = (paths[1]::PathCompletion).path * "\"" + if length(paths) == 1 + p = (paths[1]::PathCompletion).path hint && was_expanded && (p = contractuser(p)) - paths[1] = PathCompletion(p) + if close_path_completion(dir, p, path, pos) + paths[1] = PathCompletion(p * "\"") + end end if success && !isempty(dir) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index cfb9a6137a287..4fe32f47bc80c 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1236,7 +1236,7 @@ let current_dir, forbidden e isa Base.IOError && occursin("ELOOP", e.msg) end c, r = test_complete("\"$(escape_string(path))/selfsym") - @test c == ["selfsymlink"] + @test c == ["selfsymlink\""] end end @@ -1357,6 +1357,31 @@ let (c, r, res) = test_complete("\"~/julia") c, r, res = test_complete("\"foo~bar") @test !res end +if !Sys.iswindows() + # create a dir and file temporarily in the home directory + path = mkpath(joinpath(homedir(), "Zx6Wa0GkC0")) + touch(joinpath(path, "my_file")) + try + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC") + @test res + @test c == String["Zx6Wa0GkC0/"] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0") + @test res + @test c == String[homedir() * "/Zx6Wa0GkC0"] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0/my_") + @test res + @test c == String["my_file\""] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0/my_file") + @test res + @test c == String[homedir() * "/Zx6Wa0GkC0/my_file"] + end + finally + rm(path, recursive=true) + end +end # Test the completion returns nothing when the folder do not exist let (c, r) = test_complete("cd(\"folder_do_not_exist_77/file") From 0b9fcb5db93e1fdd2ec0f319a8cd155b9f9508b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=8C=E7=90=86?= Date: Wed, 23 Oct 2024 07:17:08 -0700 Subject: [PATCH 476/548] Implement faster `issubset` for `CartesianIndices{N}` (#56282) Co-authored-by: xili --- base/multidimensional.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 99f41f2404e47..c82f1c1ba75d7 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -615,6 +615,8 @@ module IteratorsMD # array operations Base.intersect(a::CartesianIndices{N}, b::CartesianIndices{N}) where N = CartesianIndices(intersect.(a.indices, b.indices)) + Base.issubset(a::CartesianIndices{N}, b::CartesianIndices{N}) where N = + isempty(a) || all(map(issubset, a.indices, b.indices)) # Views of reshaped CartesianIndices are used for partitions — ensure these are fast const CartesianPartition{T<:CartesianIndex, P<:CartesianIndices, R<:ReshapedArray{T,1,P}} = SubArray{T,1,R,<:Tuple{AbstractUnitRange{Int}},false} From 6c70bf784999b478bc176fe594738f9746a1dcfd Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Wed, 23 Oct 2024 16:18:58 +0200 Subject: [PATCH 477/548] Improve doc example: Extracting the type parameter from a super-type (#55983) Documentation describes the correct way of extracting the element type of a supertype: https://docs.julialang.org/en/v1/manual/methods/#Extracting-the-type-parameter-from-a-super-type However, one of the examples to showcase this is nonsensical since it is a union of multiple element types. I have replaced this example with a union over the dimension. Now, the `eltype_wrong` function still gives a similar error, yet the correct way returns the unambiguous answer. --------- Co-authored-by: Lilith Orion Hafner --- doc/src/manual/methods.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 6be44dcf4fa13..3c234b17f10d8 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -698,11 +698,14 @@ While this works for declared types, it fails for types without supertypes: ```julia-repl -julia> eltype_wrong(Union{AbstractArray{Int}, AbstractArray{Float64}}) -ERROR: MethodError: no method matching supertype(::Type{Union{AbstractArray{Float64,N} where N, AbstractArray{Int64,N} where N}}) +julia> eltype_wrong(Union{Vector{Int}, Matrix{Int}}) +ERROR: MethodError: no method matching supertype(::Type{VecOrMat{Int64}}) + Closest candidates are: - supertype(::DataType) at operators.jl:43 - supertype(::UnionAll) at operators.jl:48 + supertype(::UnionAll) + @ Base operators.jl:44 + supertype(::DataType) + @ Base operators.jl:43 ``` ### Building a similar type with a different type parameter From 005608af2e134b274eeb1224b610b3004e2a832f Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 23 Oct 2024 18:46:31 +0200 Subject: [PATCH 478/548] llvmpasses: force vector width for compatibility with non-x86 hosts. (#56300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pipeline-prints test currently fails when running on an aarch64-macos device: ``` /Users/tim/Julia/src/julia/test/llvmpasses/pipeline-prints.ll:309:23: error: AFTERVECTORIZATION: expected string not found in input ; AFTERVECTORIZATION: vector.body ^ :2:40: note: scanning from here ; *** IR Dump Before AfterVectorizationMarkerPass on julia_f_199 *** ^ :47:27: note: possible intended match here ; *** IR Dump Before AfterVectorizationMarkerPass on jfptr_f_200 *** ^ Input file: Check file: /Users/tim/Julia/src/julia/test/llvmpasses/pipeline-prints.ll -dump-input=help explains the following input dump. Input was: <<<<<< 1: opt: WARNING: failed to create target machine for 'x86_64-unknown-linux-gnu': unable to get target for 'x86_64-unknown-linux-gnu', see --version and --triple. 2: ; *** IR Dump Before AfterVectorizationMarkerPass on julia_f_199 *** check:309'0 X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found 3: define i64 @julia_f_199(ptr addrspace(10) noundef nonnull align 16 dereferenceable(40) %0) #0 !dbg !4 { check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4: top: check:309'0 ~~~~~ 5: %1 = call ptr @julia.get_pgcstack() check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6: %ptls_field = getelementptr inbounds ptr, ptr %1, i64 2 check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7: %ptls_load45 = load ptr, ptr %ptls_field, align 8, !tbaa !8 check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ . . . 42: check:309'0 ~ 43: L41: ; preds = %L41.loopexit, %L17, %top check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 44: %value_phi10 = phi i64 [ 0, %top ], [ %7, %L17 ], [ %.lcssa, %L41.loopexit ] check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45: ret i64 %value_phi10, !dbg !52 check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 46: } check:309'0 ~~ 47: ; *** IR Dump Before AfterVectorizationMarkerPass on jfptr_f_200 *** check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ check:309'1 ? possible intended match 48: ; Function Attrs: noinline optnone check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 49: define nonnull ptr addrspace(10) @jfptr_f_200(ptr addrspace(10) %0, ptr noalias nocapture noundef readonly %1, i32 %2) #1 { check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 50: top: check:309'0 ~~~~~ 51: %3 = call ptr @julia.get_pgcstack() check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52: %4 = getelementptr inbounds ptr addrspace(10), ptr %1, i32 0 check:309'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ . . . >>>>>> -- ******************** Failed Tests (1): Julia :: pipeline-prints.ll ``` The problem is that these tests assume x86_64, which fails because the target isn't available, so it presumably uses the native target which has different vectorization characteristics: ``` ❯ ./usr/tools/opt --load-pass-plugin=libjulia-codegen.dylib -passes='julia' --print-before=AfterVectorization -o /dev/null ../../test/llvmpasses/pipeline-prints.ll ./usr/tools/opt: WARNING: failed to create target machine for 'x86_64-unknown-linux-gnu': unable to get target for 'x86_64-unknown-linux-gnu', see --version and --triple. ``` There's other tests that assume this (e.g. the `fma` cpufeatures one), but they don't fail, so I've left them as is. --- test/llvmpasses/pipeline-prints.ll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/llvmpasses/pipeline-prints.ll b/test/llvmpasses/pipeline-prints.ll index ecb70953026c2..9c27885c5ca45 100644 --- a/test/llvmpasses/pipeline-prints.ll +++ b/test/llvmpasses/pipeline-prints.ll @@ -14,7 +14,7 @@ ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=BeforeScalarOptimization -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=BEFORESCALAROPTIMIZATION ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=AfterScalarOptimization -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=AFTERSCALAROPTIMIZATION ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=BeforeVectorization -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=BEFOREVECTORIZATION -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=AfterVectorization -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=AFTERVECTORIZATION +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=AfterVectorization -force-vector-width=2 -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=AFTERVECTORIZATION ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=BeforeIntrinsicLowering -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=BEFOREINTRINSICLOWERING ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=AfterIntrinsicLowering -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=AFTERINTRINSICLOWERING ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='julia' --print-before=BeforeCleanup -o /dev/null %s 2>&1 | FileCheck %s --check-prefixes=BEFORECLEANUP @@ -311,4 +311,4 @@ attributes #2 = { inaccessiblemem_or_argmemonly } ; COM: Intrinsics are lowered and cleaned up by the time optimization is finished ; AFTEROPTIMIZATION-NOT: call void @julia.safepoint -; AFTEROPTIMIZATION: load volatile i64{{.*}}%safepoint \ No newline at end of file +; AFTEROPTIMIZATION: load volatile i64{{.*}}%safepoint From b9b4dfa072ddb8bbbda2a7c04889465e57dd4259 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 24 Oct 2024 00:04:08 +0530 Subject: [PATCH 479/548] Reduce generic matrix*vector latency (#56289) ```julia julia> using LinearAlgebra julia> A = rand(Int,4,4); x = rand(Int,4); y = similar(x); julia> @time mul!(y, A, x, 2, 2); 0.330489 seconds (792.22 k allocations: 41.519 MiB, 8.75% gc time, 99.99% compilation time) # master 0.134212 seconds (339.89 k allocations: 17.103 MiB, 15.23% gc time, 99.98% compilation time) # This PR ``` Main changes: - `generic_matvecmul!` and `_generic_matvecmul!` now accept `alpha` and `beta` arguments instead of `MulAddMul(alpha, beta)`. The methods that accept a `MulAddMul(alpha, beta)` are also retained for backward compatibility, but these now forward `alpha` and `beta`, instead of the other way around. - Narrow the scope of the `@stable_muladdmul` applications. We now construct the `MulAddMul(alpha, beta)` object only where it is needed in a function call, and we annotate the call site with `@stable_muladdmul`. This leads to smaller branches. - Create a new internal function with methods for the `'N'`, `'T'` and `'C'` cases, so that firstly, there's less code duplication, and secondly, the `_generic_matvecmul!` method is now simple enough to enable constant propagation. This eliminates the unnecessary branches, and only the one that is taken is compiled. Together, this reduces the TTFX substantially. --- stdlib/LinearAlgebra/src/matmul.jl | 126 ++++++++++++------------- stdlib/LinearAlgebra/src/triangular.jl | 2 +- 2 files changed, 61 insertions(+), 67 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index f64422fd9cb8a..a8205a1dde808 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -78,17 +78,13 @@ _mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, generic_matvecmul!(y::StridedVector{T}, tA, A::StridedVecOrMat{T}, x::StridedVector{T}, alpha::Number, beta::Number) where {T<:BlasFloat} = gemv!(y, tA, A, x, alpha, beta) -generic_matvecmul!(y::StridedVector{T}, tA, A::StridedVecOrMat{T}, x::StridedVector{T}, - _add::MulAddMul = MulAddMul()) where {T<:BlasFloat} = - gemv!(y, tA, A, x, _add.alpha, _add.beta) + # Real (possibly transposed) matrix times complex vector. # Multiply the matrix with the real and imaginary parts separately generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, alpha::Number, beta::Number) where {T<:BlasReal} = gemv!(y, tA, A, x, alpha, beta) -generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, - _add::MulAddMul = MulAddMul()) where {T<:BlasReal} = - gemv!(y, tA, A, x, _add.alpha, _add.beta) + # Complex matrix times real vector. # Reinterpret the matrix as a real matrix and do real matvec computation. # works only in cooperation with BLAS when A is untransposed (tA == 'N') @@ -96,9 +92,6 @@ generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::S generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, alpha::Number, beta::Number) where {T<:BlasReal} = gemv!(y, tA, A, x, alpha, beta) -generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, - _add::MulAddMul = MulAddMul()) where {T<:BlasReal} = - gemv!(y, tA, A, x, _add.alpha, _add.beta) # Vector-Matrix multiplication (*)(x::AdjointAbsVec, A::AbstractMatrix) = (A'*x')' @@ -539,9 +532,9 @@ Base.@constprop :aggressive function gemv!(y::StridedVector{T}, tA::AbstractChar if tA_uc in ('S', 'H') # re-wrap again and use plain ('N') matvec mul algorithm, # because _generic_matvecmul! can't handle the HermOrSym cases specifically - return @stable_muladdmul _generic_matvecmul!(y, 'N', wrap(A, tA), x, MulAddMul(α, β)) + return _generic_matvecmul!(y, 'N', wrap(A, tA), x, α, β) else - return @stable_muladdmul _generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, tA, A, x, α, β) end end @@ -564,7 +557,7 @@ Base.@constprop :aggressive function gemv!(y::StridedVector{Complex{T}}, tA::Abs return y else Anew, ta = tA_uc in ('S', 'H') ? (wrap(A, tA), oftype(tA, 'N')) : (A, tA) - return @stable_muladdmul _generic_matvecmul!(y, ta, Anew, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, ta, Anew, x, α, β) end end @@ -591,9 +584,9 @@ Base.@constprop :aggressive function gemv!(y::StridedVector{Complex{T}}, tA::Abs elseif tA_uc in ('S', 'H') # re-wrap again and use plain ('N') matvec mul algorithm, # because _generic_matvecmul! can't handle the HermOrSym cases specifically - return @stable_muladdmul _generic_matvecmul!(y, 'N', wrap(A, tA), x, MulAddMul(α, β)) + return _generic_matvecmul!(y, 'N', wrap(A, tA), x, α, β) else - return @stable_muladdmul _generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, tA, A, x, α, β) end end @@ -825,82 +818,83 @@ end # NOTE: the generic version is also called as fallback for # strides != 1 cases -Base.@constprop :aggressive generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, alpha::Number, beta::Number) = - @stable_muladdmul generic_matvecmul!(C, tA, A, B, MulAddMul(alpha, beta)) +# legacy method, retained for backward compatibility +generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) = + generic_matvecmul!(C, tA, A, B, _add.alpha, _add.beta) @inline function generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, - _add::MulAddMul = MulAddMul()) + alpha::Number, beta::Number) tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char Anew, ta = tA_uc in ('S', 'H') ? (wrap(A, tA), oftype(tA, 'N')) : (A, tA) - return _generic_matvecmul!(C, ta, Anew, B, _add) + return _generic_matvecmul!(C, ta, Anew, B, alpha, beta) end -function _generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, - _add::MulAddMul = MulAddMul()) - require_one_based_indexing(C, A, B) - @assert tA in ('N', 'T', 'C') - mB = length(B) - mA, nA = lapack_size(tA, A) - if mB != nA - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), vector B has length $mB")) - end - if mA != length(C) - throw(DimensionMismatch(lazy"result C has length $(length(C)), needs length $mA")) - end - +# legacy method, retained for backward compatibility +_generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) = + _generic_matvecmul!(C, tA, A, B, _add.alpha, _add.beta) +function __generic_matvecmul!(f::F, C::AbstractVector, A::AbstractVecOrMat, B::AbstractVector, + alpha::Number, beta::Number) where {F} Astride = size(A, 1) - @inbounds begin - if tA == 'T' # fastest case - if nA == 0 - for k = 1:mA - _modify!(_add, false, C, k) - end - else - for k = 1:mA - aoffs = (k-1)*Astride - firstterm = transpose(A[aoffs + 1])*B[1] - s = zero(firstterm + firstterm) - for i = 1:nA - s += transpose(A[aoffs+i]) * B[i] - end - _modify!(_add, s, C, k) - end - end - elseif tA == 'C' - if nA == 0 - for k = 1:mA - _modify!(_add, false, C, k) + if length(B) == 0 + for k = eachindex(C) + @stable_muladdmul _modify!(MulAddMul(alpha,beta), false, C, k) end else - for k = 1:mA + for k = eachindex(C) aoffs = (k-1)*Astride - firstterm = A[aoffs + 1]'B[1] + firstterm = f(A[aoffs + 1]) * B[1] s = zero(firstterm + firstterm) - for i = 1:nA - s += A[aoffs + i]'B[i] + for i = eachindex(B) + s += f(A[aoffs+i]) * B[i] end - _modify!(_add, s, C, k) + @stable_muladdmul _modify!(MulAddMul(alpha,beta), s, C, k) end end - else # tA == 'N' - for i = 1:mA - if !iszero(_add.beta) - C[i] *= _add.beta - elseif mB == 0 + end +end +function __generic_matvecmul!(::typeof(identity), C::AbstractVector, A::AbstractVecOrMat, B::AbstractVector, + alpha::Number, beta::Number) + Astride = size(A, 1) + @inbounds begin + for i = eachindex(C) + if !iszero(beta) + C[i] *= beta + elseif length(B) == 0 C[i] = false else C[i] = zero(A[i]*B[1] + A[i]*B[1]) end end - for k = 1:mB + for k = eachindex(B) aoffs = (k-1)*Astride - b = _add(B[k]) - for i = 1:mA + b = @stable_muladdmul MulAddMul(alpha,beta)(B[k]) + for i = eachindex(C) C[i] += A[aoffs + i] * b end end end - end # @inbounds + return C +end +function _generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, + alpha::Number, beta::Number) + require_one_based_indexing(C, A, B) + @assert tA in ('N', 'T', 'C') + mB = length(B) + mA, nA = lapack_size(tA, A) + if mB != nA + throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), vector B has length $mB")) + end + if mA != length(C) + throw(DimensionMismatch(lazy"result C has length $(length(C)), needs length $mA")) + end + + if tA == 'T' # fastest case + __generic_matvecmul!(transpose, C, A, B, alpha, beta) + elseif tA == 'C' + __generic_matvecmul!(adjoint, C, A, B, alpha, beta) + else # tA == 'N' + __generic_matvecmul!(identity, C, A, B, alpha, beta) + end C end diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index d6994f4b4dd58..1a7d04115c97d 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -1066,7 +1066,7 @@ for TC in (:AbstractVector, :AbstractMatrix) if isone(alpha) && iszero(beta) return _trimul!(C, A, B) else - return @stable_muladdmul generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta)) + return _generic_matvecmul!(C, 'N', A, B, alpha, beta) end end end From 28b0abd95064370cf5d7ef08beb5adb33710c18f Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Wed, 23 Oct 2024 16:33:40 -0500 Subject: [PATCH 480/548] Type `Base.is_interactive` as `Bool` (#56303) Before, typing `Base.is_interactive = 7` would cause weird internal REPL failures down the line. Now, it throws an InexactError and has no impact. --- base/initdefs.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/initdefs.jl b/base/initdefs.jl index 85b708433c0ef..f7693813239c6 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -30,14 +30,14 @@ exit() = exit(0) const roottask = current_task() -is_interactive = false +is_interactive::Bool = false """ isinteractive() -> Bool Determine whether Julia is running an interactive session. """ -isinteractive() = (is_interactive::Bool) +isinteractive() = is_interactive ## package depots (registries, packages, environments) ## From 4236a33bc5a33dd123a8ffaf2ed2b4fe5641bb87 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 23 Oct 2024 19:14:02 -0400 Subject: [PATCH 481/548] REPL: don't complete str and cmd macros when the input matches the internal name like `r_` to `r"` (#56254) --- stdlib/REPL/src/REPLCompletions.jl | 13 +++++++++++++ stdlib/REPL/test/replcompletions.jl | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index d230b7b5fd232..d59a18e6d4f16 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -141,6 +141,19 @@ function append_filtered_mod_names!(ffunc::Function, suggestions::Vector{Complet ssyms = names(mod; all=true, imported, usings) filter!(ffunc, ssyms) macros = filter(x -> startswith(String(x), "@" * name), ssyms) + + # don't complete string and command macros when the input matches the internal name like `r_` to `r"` + if !startswith(name, "@") + filter!(macros) do m + s = String(m) + if endswith(s, "_str") || endswith(s, "_cmd") + occursin(name, first(s, length(s)-4)) + else + true + end + end + end + syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)] appendmacro!(syms, macros, "_str", "\"") appendmacro!(syms, macros, "_cmd", "`") diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 4fe32f47bc80c..7ee03cee940b8 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1546,6 +1546,16 @@ end @test "testcmd`" in c c, r, res = test_complete("CompletionFoo.tϵsτc") @test "tϵsτcmδ`" in c + + # Issue #56071: don't complete string and command macros when the input matches the internal name like `r_` to `r"` + c, r, res = test_complete("CompletionFoo.teststr_") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.teststr_s") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.testcmd_") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.testcmd_c") + @test isempty(c) end @testset "Keyword-argument completion" begin From 0a6277d4876b3d9cdf9e37de0812acf41ae1c1b3 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:14:51 -0400 Subject: [PATCH 482/548] fix REPL test if a "juliadev" directory exists in home (#56218) --- stdlib/REPL/test/replcompletions.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 7ee03cee940b8..1355f74c9bfff 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1347,9 +1347,9 @@ mktempdir() do path end # Test tilde path completion -let (c, r, res) = test_complete("\"~/julia") +let (c, r, res) = test_complete("\"~/ka8w5rsz") if !Sys.iswindows() - @test res && c == String[homedir() * "/julia"] + @test res && c == String[homedir() * "/ka8w5rsz"] else @test !res end From 894296bd2398c297a4d18f0f16c7601a3237ffef Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 23 Oct 2024 20:15:39 -0300 Subject: [PATCH 483/548] Fix trampoline warning on x86 as well (#56280) --- cli/trampolines/trampolines_x86_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/trampolines/trampolines_x86_64.S b/cli/trampolines/trampolines_x86_64.S index 3b800da56eee1..fcc8e40e1ddc9 100644 --- a/cli/trampolines/trampolines_x86_64.S +++ b/cli/trampolines/trampolines_x86_64.S @@ -6,9 +6,9 @@ #define XX(name) \ DEBUGINFO(name); \ .global CNAME(name); \ +CNAME(name)##:; \ .cfi_startproc; \ SEH_START1(name); \ -CNAME(name)##:; \ SEH_START2(); \ CET_START(); \ mov CNAMEADDR(name)(%rip),%r11; \ From 53ffe5630cff5d974722ec197c6f53b907ffd457 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 24 Oct 2024 07:32:33 +0800 Subject: [PATCH 484/548] typeintersect: more fastpath to skip intersect under circular env (#56304) fix #56040 --- src/subtype.c | 9 +++++++-- test/subtype.jl | 9 +++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 5edcd100ee8e0..f5c13b77ea0cf 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2464,8 +2464,10 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, return y; if (y == (jl_value_t*)jl_any_type && !jl_is_typevar(x)) return x; - // band-aid for #46736 - if (obviously_egal(x, y)) + // band-aid for #46736 #56040 + if (obviously_in_union(x, y)) + return y; + if (obviously_in_union(y, x)) return x; jl_varbinding_t *vars = NULL; @@ -2495,6 +2497,9 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { + // band-aid for #56040 + if (!jl_is_uniontype(x) && obviously_in_union((jl_value_t *)u, x)) + return x; int no_free = !jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u); if (param == 2 || no_free) { jl_value_t *a=NULL, *b=NULL; diff --git a/test/subtype.jl b/test/subtype.jl index 7be869107b432..dfa1487eaa55d 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2721,3 +2721,12 @@ let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}} @test !(T1 <: T2) @test Tuple{Union{Val{1},Val{2}}} <: Tuple{S} where {T, S<:Val{T}} end + +#issue 56040 +let S = Dict{V,V} where {V}, + T = Dict{Ref{Union{Set{A2}, Set{A3}, A3}}, Ref{Union{Set{A3}, Set{A2}, Set{A1}, Set{A4}, A4}}} where {A1, A2<:Set{A1}, A3<:Union{Set{A1}, Set{A2}}, A4<:Union{Set{A2}, Set{A1}, Set{A3}}}, + A = Dict{Ref{Set{Union{}}}, Ref{Set{Union{}}}} + @testintersect(S, T, !Union{}) + @test A <: typeintersect(S, T) + @test A <: typeintersect(T, S) +end From 2a06376c18afd7ec875335070743dcebcd85dee7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 24 Oct 2024 08:31:24 +0530 Subject: [PATCH 485/548] Preserve type in `first` for `OneTo` (#56263) With this PR, ```julia julia> first(Base.OneTo(10), 4) Base.OneTo(4) ``` Previously, this would have used indexing to return a `UnitRange`. This is probably the only way to slice a `Base.OneTo` and obtain a `Base.OneTo` back. --- base/range.jl | 5 +++++ test/ranges.jl | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/base/range.jl b/base/range.jl index cee15db39b911..c4435f2ff3e97 100644 --- a/base/range.jl +++ b/base/range.jl @@ -850,6 +850,11 @@ first(r::OneTo{T}) where {T} = oneunit(T) first(r::StepRangeLen) = unsafe_getindex(r, 1) first(r::LinRange) = r.start +function first(r::OneTo, n::Integer) + n < 0 && throw(ArgumentError("Number of elements must be non-negative")) + OneTo(oftype(r.stop, min(r.stop, n))) +end + last(r::OrdinalRange{T}) where {T} = convert(T, r.stop) # via steprange_last last(r::StepRangeLen) = unsafe_getindex(r, length(r)) last(r::LinRange) = r.stop diff --git a/test/ranges.jl b/test/ranges.jl index 86cd1c3f2345c..629c2966b2fa6 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1539,6 +1539,9 @@ end @test size(r) == (3,) @test step(r) == 1 @test first(r) == 1 + @test first(r,2) === Base.OneTo(2) + @test first(r,20) === r + @test_throws ArgumentError first(r,-20) @test last(r) == 3 @test minimum(r) == 1 @test maximum(r) == 3 @@ -1570,6 +1573,9 @@ end @test findall(in(2:(length(r) - 1)), r) === 2:(length(r) - 1) @test findall(in(r), 2:(length(r) - 1)) === 1:(length(r) - 2) end + let r = Base.OneTo(Int8(4)) + @test first(r,4) === r + end @test convert(Base.OneTo, 1:2) === Base.OneTo{Int}(2) @test_throws ArgumentError("first element must be 1, got 2") convert(Base.OneTo, 2:3) @test_throws ArgumentError("step must be 1, got 2") convert(Base.OneTo, 1:2:5) From bf6da77c01872dbeffb764784a9a3feb49b7364c Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 24 Oct 2024 19:25:18 +0530 Subject: [PATCH 486/548] Matmul: dispatch on specific blas paths using an enum (#55002) This expands on the approach taken by https://github.com/JuliaLang/julia/pull/54552. We pass on more type information to `generic_matmatmul_wrapper!`, which lets us convert the branches to method dispatches. This helps spread the latency around, so that instead of compiling all the branches in the first call, we now compile the branches only when they are actually taken. While this reduces the latency in individual branches, there is no reduction in latency if all the branches are reachable. ```julia julia> A = rand(2,2); julia> @time A * A; 0.479805 seconds (809.66 k allocations: 40.764 MiB, 99.93% compilation time) # 1.12.0-DEV.806 0.346739 seconds (633.17 k allocations: 31.320 MiB, 99.90% compilation time) # This PR julia> @time A * A'; 0.030413 seconds (101.98 k allocations: 5.359 MiB, 98.54% compilation time) # v1.12.0-DEV.806 0.148118 seconds (219.51 k allocations: 11.652 MiB, 99.72% compilation time) # This PR ``` The latency is spread between the two calls here. In fresh sessions: ```julia julia> A = rand(2,2); julia> @time A * A'; 0.473630 seconds (825.65 k allocations: 41.554 MiB, 99.91% compilation time) # v1.12.0-DEV.806 0.490305 seconds (774.87 k allocations: 38.824 MiB, 99.90% compilation time) # This PR ``` In this case, both the `syrk` and `gemm` branches are reachable, so there is no reduction in latency. Analogously, there is a reduction in latency in the second set of matrix multiplications where we call `symm!/hemm!` or `_generic_matmatmul`: ```julia julia> using LinearAlgebra julia> A = rand(2,2); julia> @time Symmetric(A) * A; 0.711178 seconds (2.06 M allocations: 103.878 MiB, 2.20% gc time, 99.98% compilation time) # v1.12.0-DEV.806 0.540669 seconds (904.12 k allocations: 43.576 MiB, 2.60% gc time, 97.36% compilation time) # This PR ``` --- stdlib/LinearAlgebra/src/matmul.jl | 168 ++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 52 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index a8205a1dde808..74cb50f955bbb 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -294,16 +294,45 @@ true """ @inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) = _mul!(C, A, B, α, β) # Add a level of indirection and specialize _mul! to avoid ambiguities in mul! -@inline _mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) = +module BlasFlag +@enum BlasFunction SYRK HERK GEMM SYMM HEMM NONE +const SyrkHerkGemm = Union{Val{SYRK}, Val{HERK}, Val{GEMM}} +const SymmHemmGeneric = Union{Val{SYMM}, Val{HEMM}, Val{NONE}} +end +@inline function _mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) + tA = wrapper_char(A) + tB = wrapper_char(B) + tA_uc = uppercase(tA) + tB_uc = uppercase(tB) + isntc = wrapper_char_NTC(A) & wrapper_char_NTC(B) + blasfn = if isntc + if (tA_uc == 'T' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'T') + BlasFlag.SYRK + elseif (tA_uc == 'C' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'C') + BlasFlag.HERK + else isntc + BlasFlag.GEMM + end + else + if (tA_uc == 'S' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'S') + BlasFlag.SYMM + elseif (tA_uc == 'H' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'H') + BlasFlag.HEMM + else + BlasFlag.NONE + end + end + generic_matmatmul_wrapper!( C, - wrapper_char(A), - wrapper_char(B), + tA, + tB, _unwrap(A), _unwrap(B), α, β, - Val(wrapper_char_NTC(A) & wrapper_char_NTC(B)) + Val(blasfn), ) +end # this indirection allows is to specialize on the types of the wrappers of A and B to some extent, # even though the wrappers are stripped off in mul! @@ -408,7 +437,7 @@ end # THE one big BLAS dispatch. This is split into two methods to improve latency Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number, ::Val{true}) where {T<:BlasFloat} + α::Number, β::Number, val::BlasFlag.SyrkHerkGemm) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if any(iszero, size(A)) || any(iszero, size(B)) || iszero(α) @@ -418,24 +447,31 @@ Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix return _rmul_or_fill!(C, β) end matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α, β) && return C - # We convert the chars to uppercase to potentially unwrap a WrapperChar, - # and extract the char corresponding to the wrapper type - tA_uc, tB_uc = uppercase(tA), uppercase(tB) - # the map in all ensures constprop by acting on tA and tB individually, instead of looping over them. - if tA_uc == 'T' && tB_uc == 'N' && A === B - return syrk_wrapper!(C, 'T', A, α, β) - elseif tA_uc == 'N' && tB_uc == 'T' && A === B - return syrk_wrapper!(C, 'N', A, α, β) - elseif tA_uc == 'C' && tB_uc == 'N' && A === B - return herk_wrapper!(C, 'C', A, α, β) - elseif tA_uc == 'N' && tB_uc == 'C' && A === B - return herk_wrapper!(C, 'N', A, α, β) + _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, val) + return C +end +Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.SYRK}) + if A === B + tA_uc = uppercase(tA) # potentially strip a WrapperChar + return syrk_wrapper!(C, tA_uc, A, α, β) else return gemm_wrapper!(C, tA, tB, A, B, α, β) end end +Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.HERK}) + if A === B + tA_uc = uppercase(tA) # potentially strip a WrapperChar + return herk_wrapper!(C, tA_uc, A, α, β) + else + return gemm_wrapper!(C, tA, tB, A, B, α, β) + end +end +Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.GEMM}) + return gemm_wrapper!(C, tA, tB, A, B, α, β) +end +_valtypeparam(v::Val{T}) where {T} = T Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number, ::Val{false}) where {T<:BlasFloat} + α::Number, β::Number, val::BlasFlag.SymmHemmGeneric) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if any(iszero, size(A)) || any(iszero, size(B)) || iszero(α) @@ -445,23 +481,48 @@ Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix return _rmul_or_fill!(C, β) end matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α, β) && return C - # We convert the chars to uppercase to potentially unwrap a WrapperChar, - # and extract the char corresponding to the wrapper type - tA_uc, tB_uc = uppercase(tA), uppercase(tB) alpha, beta = promote(α, β, zero(T)) - if alpha isa Union{Bool,T} && beta isa Union{Bool,T} - if tA_uc == 'S' && tB_uc == 'N' - return BLAS.symm!('L', tA == 'S' ? 'U' : 'L', alpha, A, B, beta, C) - elseif tA_uc == 'N' && tB_uc == 'S' - return BLAS.symm!('R', tB == 'S' ? 'U' : 'L', alpha, B, A, beta, C) - elseif tA_uc == 'H' && tB_uc == 'N' - return BLAS.hemm!('L', tA == 'H' ? 'U' : 'L', alpha, A, B, beta, C) - elseif tA_uc == 'N' && tB_uc == 'H' - return BLAS.hemm!('R', tB == 'H' ? 'U' : 'L', alpha, B, A, beta, C) - end + blasfn = _valtypeparam(val) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && blasfn ∈ (BlasFlag.SYMM, BlasFlag.HEMM) + _blasfn = blasfn + αβ = (alpha, beta) + else + _blasfn = BlasFlag.NONE + αβ = (α, β) end - return _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) + _symm_hemm_generic!(C, tA, tB, A, B, αβ..., Val(_blasfn)) + return C end +Base.@constprop :aggressive function _lrchar_ulchar(tA, tB) + if uppercase(tA) == 'N' + lrchar = 'R' + ulchar = isuppercase(tB) ? 'U' : 'L' + else + lrchar = 'L' + ulchar = isuppercase(tA) ? 'U' : 'L' + end + return lrchar, ulchar +end +function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.SYMM}) + lrchar, ulchar = _lrchar_ulchar(tA, tB) + if lrchar == 'L' + BLAS.symm!(lrchar, ulchar, alpha, A, B, beta, C) + else + BLAS.symm!(lrchar, ulchar, alpha, B, A, beta, C) + end +end +function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.HEMM}) + lrchar, ulchar = _lrchar_ulchar(tA, tB) + if lrchar == 'L' + BLAS.hemm!(lrchar, ulchar, alpha, A, B, beta, C) + else + BLAS.hemm!(lrchar, ulchar, alpha, B, A, beta, C) + end +end +Base.@constprop :aggressive function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.NONE}) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) +end + # legacy method Base.@constprop :aggressive generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add::MulAddMul = MulAddMul()) where {T<:BlasFloat} = @@ -472,8 +533,8 @@ function generic_matmatmul_wrapper!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::S gemm_wrapper!(C, tA, tB, A, B, α, β) end Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - α::Number, β::Number, ::Val{false}) where {T<:BlasReal} - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) + alpha::Number, beta::Number, ::Val{false}) where {T<:BlasReal} + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) end # legacy method Base.@constprop :aggressive generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, @@ -675,7 +736,7 @@ Base.@constprop :aggressive function gemm_wrapper(tA::AbstractChar, tB::Abstract if all(map(in(('N', 'T', 'C')), (tA_uc, tB_uc))) gemm_wrapper!(C, tA, tB, A, B, true, false) else - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul()) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), true, false) end end @@ -702,7 +763,7 @@ Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{T}, tA::Ab _fullstride2(A) && _fullstride2(B) && _fullstride2(C)) return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) end - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), α, β) end # legacy method gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, @@ -737,7 +798,7 @@ Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{Complex{T} BLAS.gemm!(tA, tB, alpha, reinterpret(T, A), B, beta, reinterpret(T, C)) return C end - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), α, β) end # legacy method gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, @@ -908,12 +969,16 @@ end # aggressive const prop makes mixed eltype mul!(C, A, B) invoke _generic_matmatmul! directly # legacy method Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul = MulAddMul()) = - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) -Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) = - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(α, β)) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add.alpha, _add.beta) +Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, alpha::Number, beta::Number) = + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) + +# legacy method +_generic_matmatmul!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul) = + _generic_matmatmul!(C, A, B, _add.alpha, _add.beta) -@noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, - _add::MulAddMul{ais1}) where {T,S,R,ais1} +@noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat, B::AbstractVecOrMat, + alpha::Number, beta::Number) where {R} AxM = axes(A, 1) AxK = axes(A, 2) # we use two `axes` calls in case of `AbstractVector` BxK = axes(B, 1) @@ -929,34 +994,33 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A if BxN != CxN throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) end - _rmul_alpha = MulAddMul{ais1,true,typeof(_add.alpha),Bool}(_add.alpha,false) if isbitstype(R) && sizeof(R) ≤ 16 && !(A isa Adjoint || A isa Transpose) - _rmul_or_fill!(C, _add.beta) - (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C + _rmul_or_fill!(C, beta) + (iszero(alpha) || isempty(A) || isempty(B)) && return C @inbounds for n in BxN, k in BxK # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) - Balpha = _rmul_alpha(B[k,n]) + Balpha = @stable_muladdmul MulAddMul(alpha, false)(B[k,n]) @simd for m in AxM C[m,n] = muladd(A[m,k], Balpha, C[m,n]) end end elseif isbitstype(R) && sizeof(R) ≤ 16 && ((A isa Adjoint && B isa Adjoint) || (A isa Transpose && B isa Transpose)) - _rmul_or_fill!(C, _add.beta) - (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C + _rmul_or_fill!(C, beta) + (iszero(alpha) || isempty(A) || isempty(B)) && return C t = wrapperop(A) pB = parent(B) pA = parent(A) tmp = similar(C, CxN) ci = first(CxM) - ta = t(_add.alpha) + ta = t(alpha) for i in AxM mul!(tmp, pB, view(pA, :, i)) @views C[ci,:] .+= t.(ta .* tmp) ci += 1 end else - if iszero(_add.alpha) || isempty(A) || isempty(B) - return _rmul_or_fill!(C, _add.beta) + if iszero(alpha) || isempty(A) || isempty(B) + return _rmul_or_fill!(C, beta) end a1 = first(AxK) b1 = first(BxK) @@ -966,7 +1030,7 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A @simd for k in AxK Ctmp = muladd(A[i, k], B[k, j], Ctmp) end - _modify!(_add, Ctmp, C, (i,j)) + @stable_muladdmul _modify!(MulAddMul(alpha,beta), Ctmp, C, (i,j)) end end return C From c188e0c626149149e90a0d45eab4af570bc6b669 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 24 Oct 2024 19:26:19 +0530 Subject: [PATCH 487/548] Scaling `mul!` for generic `AbstractArray`s (#56313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves performance in the scaling `mul!` for `StridedArray`s by using loops instead of broadcasting. ```julia julia> using LinearAlgebra julia> A = zeros(200,200); C = similar(A); julia> @btime mul!($C, $A, 1, 2, 2); 19.180 μs (0 allocations: 0 bytes) # nightly v"1.12.0-DEV.1479" 11.361 μs (0 allocations: 0 bytes) # This PR ``` The latency is reduced as well for the same reason. ```julia julia> using LinearAlgebra julia> A = zeros(2,2); C = similar(A); julia> @time mul!(C, A, 1, 2, 2); 0.203034 seconds (522.94 k allocations: 27.011 MiB, 14.95% gc time, 99.97% compilation time) # nightly 0.034713 seconds (59.16 k allocations: 2.962 MiB, 99.91% compilation time) # This PR ``` Thirdly, I've replaced the `.*ₛ` calls by explicit branches. This fixes the following: ```julia julia> A = [zeros(2), zeros(2)]; C = similar(A); julia> mul!(C, A, 1) ERROR: MethodError: no method matching +(::Vector{Float64}, ::Bool) ``` After this, ```julia julia> mul!(C, A, 1) 2-element Vector{Vector{Float64}}: [0.0, 0.0] [0.0, 0.0] ``` Also, I've added `@stable_muladdmul` annotations to the `generic_mul!` call, but moved it within the loop to narrow its scope. This doesn't increase the latency, while making the call type-stable. ```julia julia> D = Diagonal(1:2); C = similar(D); julia> @time mul!(C, D, 1, 2, 2); 0.248385 seconds (898.18 k allocations: 47.027 MiB, 12.30% gc time, 99.96% compilation time) # nightly 0.249940 seconds (919.80 k allocations: 49.128 MiB, 11.36% gc time, 99.99% compilation time) # This PR ``` --- stdlib/LinearAlgebra/src/generic.jl | 49 +++++++++++---- stdlib/LinearAlgebra/test/generic.jl | 89 ++++++++++++++-------------- 2 files changed, 84 insertions(+), 54 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 6c65c49add74b..9c050a32bbda7 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -174,23 +174,23 @@ end end -function generic_mul!(C::AbstractArray, X::AbstractArray, s::Number, _add::MulAddMul) +function generic_mul!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) if length(C) != length(X) throw(DimensionMismatch(lazy"first array has length $(length(C)) which does not match the length of the second, $(length(X)).")) end for (IC, IX) in zip(eachindex(C), eachindex(X)) - @inbounds _modify!(_add, X[IX] * s, C, IC) + @inbounds @stable_muladdmul _modify!(MulAddMul(alpha,beta), X[IX] * s, C, IC) end C end -function generic_mul!(C::AbstractArray, s::Number, X::AbstractArray, _add::MulAddMul) +function generic_mul!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) if length(C) != length(X) - throw(DimensionMismatch(lazy"first array has length $(length(C)) which does not -match the length of the second, $(length(X)).")) + throw(DimensionMismatch(LazyString(lazy"first array has length $(length(C)) which does not", + lazy"match the length of the second, $(length(X))."))) end for (IC, IX) in zip(eachindex(C), eachindex(X)) - @inbounds _modify!(_add, s * X[IX], C, IC) + @inbounds @stable_muladdmul _modify!(MulAddMul(alpha,beta), s * X[IX], C, IC) end C end @@ -198,22 +198,51 @@ end @inline mul!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) = _lscale_add!(C, s, X, alpha, beta) +_lscale_add!(C::StridedArray, s::Number, X::StridedArray, alpha::Number, beta::Number) = + generic_mul!(C, s, X, alpha, beta) @inline function _lscale_add!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) if axes(C) == axes(X) - C .= (s .* X) .*ₛ alpha .+ C .*ₛ beta + if isone(alpha) + if iszero(beta) + @. C = s * X + else + @. C = s * X + C * beta + end + else + if iszero(beta) + @. C = s * X * alpha + else + @. C = s * X * alpha + C * beta + end + end else - generic_mul!(C, s, X, MulAddMul(alpha, beta)) + generic_mul!(C, s, X, alpha, beta) end return C end @inline mul!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) = _rscale_add!(C, X, s, alpha, beta) +_rscale_add!(C::StridedArray, X::StridedArray, s::Number, alpha::Number, beta::Number) = + generic_mul!(C, X, s, alpha, beta) @inline function _rscale_add!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) if axes(C) == axes(X) - C .= (X .* s) .*ₛ alpha .+ C .*ₛ beta + if isone(alpha) + if iszero(beta) + @. C = X * s + else + @. C = X * s + C * beta + end + else + s_alpha = s * alpha + if iszero(beta) + @. C = X * s_alpha + else + @. C = X * s_alpha + C * beta + end + end else - generic_mul!(C, X, s, MulAddMul(alpha, beta)) + generic_mul!(C, X, s, alpha, beta) end return C end diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 2bf9c75141700..725f9b3497db8 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -131,53 +131,54 @@ end @testset "array and subarray" begin - aa = reshape([1.:6;], (2,3)) - for a in (aa, view(aa, 1:2, 1:2)) - am, an = size(a) - @testset "Scaling with rmul! and lmul" begin - @test rmul!(copy(a), 5.) == a*5 - @test lmul!(5., copy(a)) == a*5 - b = randn(2048) - subB = view(b, :, :) - @test rmul!(copy(b), 5.) == b*5 - @test rmul!(copy(subB), 5.) == subB*5 - @test lmul!(Diagonal([1.; 2.]), copy(a)) == a.*[1; 2] - @test lmul!(Diagonal([1; 2]), copy(a)) == a.*[1; 2] - @test rmul!(copy(a), Diagonal(1.:an)) == a.*Vector(1:an)' - @test rmul!(copy(a), Diagonal(1:an)) == a.*Vector(1:an)' - @test_throws DimensionMismatch lmul!(Diagonal(Vector{Float64}(undef,am+1)), a) - @test_throws DimensionMismatch rmul!(a, Diagonal(Vector{Float64}(undef,an+1))) - end + for aa in (reshape([1.:6;], (2,3)), fill(float.(rand(Int8,2,2)), 2,3)) + for a in (aa, view(aa, 1:2, 1:2)) + am, an = size(a) + @testset "Scaling with rmul! and lmul" begin + @test rmul!(copy(a), 5.) == a*5 + @test lmul!(5., copy(a)) == a*5 + b = randn(2048) + subB = view(b, :, :) + @test rmul!(copy(b), 5.) == b*5 + @test rmul!(copy(subB), 5.) == subB*5 + @test lmul!(Diagonal([1.; 2.]), copy(a)) == a.*[1; 2] + @test lmul!(Diagonal([1; 2]), copy(a)) == a.*[1; 2] + @test rmul!(copy(a), Diagonal(1.:an)) == a.*Vector(1:an)' + @test rmul!(copy(a), Diagonal(1:an)) == a.*Vector(1:an)' + @test_throws DimensionMismatch lmul!(Diagonal(Vector{Float64}(undef,am+1)), a) + @test_throws DimensionMismatch rmul!(a, Diagonal(Vector{Float64}(undef,an+1))) + end - @testset "Scaling with rdiv! and ldiv!" begin - @test rdiv!(copy(a), 5.) == a/5 - @test ldiv!(5., copy(a)) == a/5 - @test ldiv!(zero(a), 5., copy(a)) == a/5 - end + @testset "Scaling with rdiv! and ldiv!" begin + @test rdiv!(copy(a), 5.) == a/5 + @test ldiv!(5., copy(a)) == a/5 + @test ldiv!(zero(a), 5., copy(a)) == a/5 + end - @testset "Scaling with 3-argument mul!" begin - @test mul!(similar(a), 5., a) == a*5 - @test mul!(similar(a), a, 5.) == a*5 - @test mul!(similar(a), Diagonal([1.; 2.]), a) == a.*[1; 2] - @test mul!(similar(a), Diagonal([1; 2]), a) == a.*[1; 2] - @test_throws DimensionMismatch mul!(similar(a), Diagonal(Vector{Float64}(undef, am+1)), a) - @test_throws DimensionMismatch mul!(Matrix{Float64}(undef, 3, 2), a, Diagonal(Vector{Float64}(undef, an+1))) - @test_throws DimensionMismatch mul!(similar(a), a, Diagonal(Vector{Float64}(undef, an+1))) - @test mul!(similar(a), a, Diagonal(1.:an)) == a.*Vector(1:an)' - @test mul!(similar(a), a, Diagonal(1:an)) == a.*Vector(1:an)' - end + @testset "Scaling with 3-argument mul!" begin + @test mul!(similar(a), 5., a) == a*5 + @test mul!(similar(a), a, 5.) == a*5 + @test mul!(similar(a), Diagonal([1.; 2.]), a) == a.*[1; 2] + @test mul!(similar(a), Diagonal([1; 2]), a) == a.*[1; 2] + @test_throws DimensionMismatch mul!(similar(a), Diagonal(Vector{Float64}(undef, am+1)), a) + @test_throws DimensionMismatch mul!(Matrix{Float64}(undef, 3, 2), a, Diagonal(Vector{Float64}(undef, an+1))) + @test_throws DimensionMismatch mul!(similar(a), a, Diagonal(Vector{Float64}(undef, an+1))) + @test mul!(similar(a), a, Diagonal(1.:an)) == a.*Vector(1:an)' + @test mul!(similar(a), a, Diagonal(1:an)) == a.*Vector(1:an)' + end - @testset "Scaling with 5-argument mul!" begin - @test mul!(copy(a), 5., a, 10, 100) == a*150 - @test mul!(copy(a), a, 5., 10, 100) == a*150 - @test mul!(vec(copy(a)), 5., a, 10, 100) == vec(a*150) - @test mul!(vec(copy(a)), a, 5., 10, 100) == vec(a*150) - @test_throws DimensionMismatch mul!([vec(copy(a)); 0], 5., a, 10, 100) - @test_throws DimensionMismatch mul!([vec(copy(a)); 0], a, 5., 10, 100) - @test mul!(copy(a), Diagonal([1.; 2.]), a, 10, 100) == 10a.*[1; 2] .+ 100a - @test mul!(copy(a), Diagonal([1; 2]), a, 10, 100) == 10a.*[1; 2] .+ 100a - @test mul!(copy(a), a, Diagonal(1.:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a - @test mul!(copy(a), a, Diagonal(1:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a + @testset "Scaling with 5-argument mul!" begin + @test mul!(copy(a), 5., a, 10, 100) == a*150 + @test mul!(copy(a), a, 5., 10, 100) == a*150 + @test mul!(vec(copy(a)), 5., a, 10, 100) == vec(a*150) + @test mul!(vec(copy(a)), a, 5., 10, 100) == vec(a*150) + @test_throws DimensionMismatch mul!([vec(copy(a)); 0], 5., a, 10, 100) + @test_throws DimensionMismatch mul!([vec(copy(a)); 0], a, 5., 10, 100) + @test mul!(copy(a), Diagonal([1.; 2.]), a, 10, 100) == 10a.*[1; 2] .+ 100a + @test mul!(copy(a), Diagonal([1; 2]), a, 10, 100) == 10a.*[1; 2] .+ 100a + @test mul!(copy(a), a, Diagonal(1.:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a + @test mul!(copy(a), a, Diagonal(1:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a + end end end end From 20f933ae785423d3b94474e11453794c1fadc188 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Thu, 24 Oct 2024 11:07:58 -0400 Subject: [PATCH 488/548] InteractiveUtils.jl: fixes issue where subtypes resolves bindings and causes deprecation warnings (#56306) The current version of `subtypes` will throw deprecation errors even if no one is using the deprecated bindings. A similar bug was fixed in Aqua.jl - https://github.com/JuliaTesting/Aqua.jl/pull/89/files See discussion here: - https://github.com/JuliaIO/ImageMagick.jl/issues/235 (for identifying the problem) - https://github.com/simonster/Reexport.jl/issues/42 (for pointing to the issue in Aqua.jl) - https://github.com/JuliaTesting/Aqua.jl/pull/89/files (for the fix in Aqua.jl) This adds the `isbindingresolved` test to the `subtypes` function to avoid throwing deprecation warnings. It also adds a test to check that this doesn't happen. --- On the current master branch (before the fix), the added test shows: ``` WARNING: using deprecated binding InternalModule.MyOldType in OuterModule. , use MyType instead. Subtypes and deprecations: Test Failed at /home/dgleich/devextern/julia/usr/share/julia/stdlib/v1.12/Test/src/Test.jl:932 Expression: isempty(stderr_content) Evaluated: isempty("WARNING: using deprecated binding InternalModule.MyOldType in OuterModule.\n, use MyType instead.\n") Test Summary: | Fail Total Time Subtypes and deprecations | 1 1 2.8s ERROR: LoadError: Some tests did not pass: 0 passed, 1 failed, 0 errored, 0 broken. in expression starting at /home/dgleich/devextern/julia/stdlib/InteractiveUtils/test/runtests.jl:841 ERROR: Package InteractiveUtils errored during testing ``` --- Using the results of this pull request: ``` @test_nowarn subtypes(Integer); ``` passes without error. The other tests pass too. --- .../InteractiveUtils/src/InteractiveUtils.jl | 4 ++-- stdlib/InteractiveUtils/test/runtests.jl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index f3c1ff7fba59f..f14e2f7de2f49 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -16,7 +16,7 @@ export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, import Base.Docs.apropos using Base: unwrap_unionall, rewrap_unionall, isdeprecated, Bottom, summarysize, - signature_type, format_bytes + signature_type, format_bytes, isbindingresolved using Base.Libc using Markdown @@ -262,7 +262,7 @@ function _subtypes_in!(mods::Array, x::Type) m = pop!(mods) xt = xt::DataType for s in names(m, all = true) - if isdefined(m, s) && !isdeprecated(m, s) + if isbindingresolved(m, s) && !isdeprecated(m, s) && isdefined(m, s) t = getfield(m, s) dt = isa(t, UnionAll) ? unwrap_unionall(t) : t if isa(dt, DataType) diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 851391ec6c249..e729ae67bde19 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -823,3 +823,22 @@ end @testset "Docstrings" begin @test isempty(Docs.undocumented_names(InteractiveUtils)) end + +# issue https://github.com/JuliaIO/ImageMagick.jl/issues/235 +module OuterModule + module InternalModule + struct MyType + x::Int + end + + Base.@deprecate_binding MyOldType MyType + + export MyType + end + using .InternalModule + export MyType, MyOldType +end # module +@testset "Subtypes and deprecations" begin + using .OuterModule + @test_nowarn subtypes(Integer); +end From f285de58f470cc3f0ac263842a055ce9bddb2451 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:10:18 -0400 Subject: [PATCH 489/548] [CRC32c] Support AbstractVector{UInt8} as input (#56164) This is a similar PR to https://github.com/JuliaIO/CRC32.jl/pull/12 I added a generic fallback method for `AbstractVector{UInt8}` similar to the existing generic `IO` method. Co-authored-by: Steven G. Johnson --- stdlib/CRC32c/src/CRC32c.jl | 25 +++++++++++++++++++------ stdlib/CRC32c/test/runtests.jl | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/stdlib/CRC32c/src/CRC32c.jl b/stdlib/CRC32c/src/CRC32c.jl index 03bef027bde16..923f7333b4c17 100644 --- a/stdlib/CRC32c/src/CRC32c.jl +++ b/stdlib/CRC32c/src/CRC32c.jl @@ -7,7 +7,6 @@ See [`CRC32c.crc32c`](@ref) for more information. """ module CRC32c -import Base.FastContiguousSubArray import Base: DenseBytes export crc32c @@ -16,9 +15,9 @@ export crc32c crc32c(data, crc::UInt32=0x00000000) Compute the CRC-32c checksum of the given `data`, which can be -an `Array{UInt8}`, a contiguous subarray thereof, or a `String`. Optionally, you can pass -a starting `crc` integer to be mixed in with the checksum. The `crc` parameter -can be used to compute a checksum on data divided into chunks: performing +an `Array{UInt8}`, a contiguous subarray thereof, an `AbstractVector{UInt8}`, or a `String`. +Optionally, you can pass a starting `crc` integer to be mixed in with the checksum. +The `crc` parameter can be used to compute a checksum on data divided into chunks: performing `crc32c(data2, crc32c(data1))` is equivalent to the checksum of `[data1; data2]`. (Technically, a little-endian checksum is computed.) @@ -30,11 +29,26 @@ calling [`take!`](@ref). For a `String`, note that the result is specific to the UTF-8 encoding (a different checksum would be obtained from a different Unicode encoding). -To checksum an `a::Array` of some other bitstype, you can do `crc32c(reinterpret(UInt8,a))`, +To checksum an `a::AbstractArray` of some other bitstype without padding, +you can do `crc32c(vec(reinterpret(UInt8,a)))`, but note that the result may be endian-dependent. """ function crc32c end +function crc32c(a::AbstractVector{UInt8}, crc::UInt32=0x00000000) + # use block size 24576=8192*3, since that is the threshold for + # 3-way parallel SIMD code in the underlying jl_crc32c C function. + last = lastindex(a) + nb = length(a) + buf = Memory{UInt8}(undef, Int(min(nb, 24576))) + while nb > 0 + n = min(nb, 24576) + copyto!(buf, 1, a, last - nb + 1, n) + crc = Base.unsafe_crc32c(buf, n % Csize_t, crc) + nb -= n + end + return crc +end function crc32c(a::DenseBytes, crc::UInt32=0x00000000) Base._crc32c(a, crc) @@ -51,6 +65,5 @@ mixed with a starting `crc` integer. If `nb` is not supplied, then """ crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) = Base._crc32c(io, nb, crc) crc32c(io::IO, crc::UInt32=0x00000000) = Base._crc32c(io, crc) -crc32c(io::IOStream, crc::UInt32=0x00000000) = Base._crc32c(io, crc) end diff --git a/stdlib/CRC32c/test/runtests.jl b/stdlib/CRC32c/test/runtests.jl index e1bd75d0e15f6..37b447e6d999a 100644 --- a/stdlib/CRC32c/test/runtests.jl +++ b/stdlib/CRC32c/test/runtests.jl @@ -3,12 +3,23 @@ using Test, Random using CRC32c +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) +using .Main.OffsetArrays: Origin + +isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) +using .Main.FillArrays: Fill + function test_crc32c(crc32c) # CRC32c checksum (test data generated from @andrewcooke's CRC.jl package) for (n,crc) in [(0,0x00000000),(1,0xa016d052),(2,0x03f89f52),(3,0xf130f21e),(4,0x29308cf4),(5,0x53518fab),(6,0x4f4dfbab),(7,0xbd3a64dc),(8,0x46891f81),(9,0x5a14b9f9),(10,0xb219db69),(11,0xd232a91f),(12,0x51a15563),(13,0x9f92de41),(14,0x4d8ae017),(15,0xc8b74611),(16,0xa0de6714),(17,0x672c992a),(18,0xe8206eb6),(19,0xc52fd285),(20,0x327b0397),(21,0x318263dd),(22,0x08485ccd),(23,0xea44d29e),(24,0xf6c0cb13),(25,0x3969bba2),(26,0x6a8810ec),(27,0x75b3d0df),(28,0x82d535b1),(29,0xbdf7fc12),(30,0x1f836b7d),(31,0xd29f33af),(32,0x8e4acb3e),(33,0x1cbee2d1),(34,0xb25f7132),(35,0xb0fa484c),(36,0xb9d262b4),(37,0x3207fe27),(38,0xa024d7ac),(39,0x49a2e7c5),(40,0x0e2c157f),(41,0x25f7427f),(42,0x368c6adc),(43,0x75efd4a5),(44,0xa84c5c31),(45,0x0fc817b2),(46,0x8d99a881),(47,0x5cc3c078),(48,0x9983d5e2),(49,0x9267c2db),(50,0xc96d4745),(51,0x058d8df3),(52,0x453f9cf3),(53,0xb714ade1),(54,0x55d3c2bc),(55,0x495710d0),(56,0x3bddf494),(57,0x4f2577d0),(58,0xdae0f604),(59,0x3c57c632),(60,0xfe39bbb0),(61,0x6f5d1d41),(62,0x7d996665),(63,0x68c738dc),(64,0x8dfea7ae)] s = String(UInt8[1:n;]) ss = SubString(String(UInt8[0:(n+1);]), 2:(n+1)) @test crc32c(UInt8[1:n;]) == crc == crc32c(s) == crc32c(ss) + @test crc == crc32c(UInt8(1):UInt8(n)) + m = Memory{UInt8}(undef, n) + m .= 1:n + @test crc == crc32c(m) end # test that crc parameter is equivalent to checksum of concatenated data, @@ -50,9 +61,24 @@ function test_crc32c(crc32c) LONG = 8192 # from crc32c.c SHORT = 256 # from crc32c.c n = LONG*3+SHORT*3+SHORT*2+64+7 - big = vcat(reinterpret(UInt8, hton.(0x74d7f887 .^ (1:n÷4))), UInt8[1:n%4;]) + bigg = vcat(reinterpret(UInt8, hton.(0x74d7f887 .^ (1:n÷4))), UInt8[1:n%4;]) for (offset,crc) in [(0, 0x13a5ecd5), (1, 0xecf34b7e), (2, 0xfa71b596), (3, 0xbfd24745), (4, 0xf0cb3370), (5, 0xb0ec88b5), (6, 0x258c20a8), (7, 0xa9bd638d)] - @test crc == crc32c(@view big[1+offset:end]) + @test crc == crc32c(@view bigg[1+offset:end]) + end + + # test crc of AbstractVector{UInt8} + @test crc32c(Origin(0)(b"hello")) == crc32c(b"hello") + weird_vectors = [ + view(rand(UInt8, 300000), 1:2:300000), + vec(reinterpret(UInt8, collect(Int64(1):Int64(4)))), + vec(reinterpret(UInt8, Int64(1):Int64(4))), + view([0x01, 0x02], UInt(1):UInt(2)), + Fill(0x00, UInt(100)), + Fill(0x00, big(100)), + reinterpret(UInt8, BitVector((true, false, true, false))), + ] + for a in weird_vectors + @test crc32c(a) == crc32c(collect(a)) end end unsafe_crc32c_sw(a, n, crc) = @@ -64,6 +90,8 @@ function crc32c_sw(s::Union{String, SubString{String}}, crc::UInt32=0x00000000) unsafe_crc32c_sw(s, sizeof(s), crc) end +crc32c_sw(a::AbstractVector{UInt8}, crc::UInt32=0x00000000) = + crc32c_sw(copyto!(Vector{UInt8}(undef, length(a)), a)) function crc32c_sw(io::IO, nb::Integer, crc::UInt32=0x00000000) nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0")) buf = Vector{UInt8}(undef, min(nb, 24576)) From 5cdf3789d8058d137b62259d2ab12f6eb456911e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 24 Oct 2024 22:43:27 +0200 Subject: [PATCH 490/548] Put `jl_gc_new_weakref` in a header file again (#56319) --- src/gc-common.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gc-common.h b/src/gc-common.h index 32b7470b13a58..bbac5f30c6755 100644 --- a/src/gc-common.h +++ b/src/gc-common.h @@ -185,4 +185,13 @@ extern jl_ptls_t* gc_all_tls_states; extern int gc_logging_enabled; +// =========================================================================== // +// Misc +// =========================================================================== // + +// Allocates a new weak-reference, assigns its value and increments Julia allocation +// counters. If thread-local allocators are used, then this function should allocate in the +// thread-local allocator of the current thread. +JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value); + #endif // JL_GC_COMMON_H From bc660477fd4a82e61e9dc70dafa12613df656bba Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 24 Oct 2024 19:17:06 -0400 Subject: [PATCH 491/548] use textwidth for string display truncation (#55442) It makes a big difference when displaying strings that have width-2 or width-0 characters. --- base/strings/io.jl | 32 +++++++++++++------------------- base/strings/util.jl | 21 ++++++++++++--------- test/show.jl | 14 +++++++------- 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index 82dd128240a92..116bcf71eeb7a 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -214,35 +214,29 @@ function show( # one line in collection, seven otherwise get(io, :typeinfo, nothing) === nothing && (limit *= 7) end + limit = max(0, limit-2) # quote chars # early out for short strings - len = ncodeunits(str) - len ≤ limit - 2 && # quote chars - return show(io, str) + check_textwidth(str, limit) && return show(io, str) # these don't depend on string data units = codeunit(str) == UInt8 ? "bytes" : "code units" skip_text(skip) = " ⋯ $skip $units ⋯ " - short = length(skip_text("")) + 4 # quote chars - chars = max(limit, short + 1) - short # at least 1 digit - # figure out how many characters to print in elided case - chars -= d = ndigits(len - chars) # first adjustment - chars += d - ndigits(len - chars) # second if needed - chars = max(0, chars) + # longest possible replacement string for omitted chars + max_replacement = skip_text(ncodeunits(str) * 100) # *100 for 2 inner quote chars - # find head & tail, avoiding O(length(str)) computation - head = nextind(str, 0, 1 + (chars + 1) ÷ 2) - tail = prevind(str, len + 1, chars ÷ 2) + head, tail = string_truncate_boundaries(str, limit, max_replacement, Val(:center)) # threshold: min chars skipped to make elision worthwhile - t = short + ndigits(len - chars) - 1 - n = tail - head # skipped code units - if 4t ≤ n || t ≤ n && t ≤ length(str, head, tail-1) - skip = skip_text(n) - show(io, SubString(str, 1:prevind(str, head))) - printstyled(io, skip; color=:light_yellow, bold=true) - show(io, SubString(str, tail)) + afterhead = nextind(str, head) + n = tail - afterhead # skipped code units + replacement = skip_text(n) + t = ncodeunits(replacement) # length of replacement (textwidth == ncodeunits here) + @views if 4t ≤ n || t ≤ n && t ≤ textwidth(str[afterhead:prevind(str,tail)]) + show(io, str[begin:head]) + printstyled(io, replacement; color=:light_yellow, bold=true) + show(io, str[tail:end]) else show(io, str) end diff --git a/base/strings/util.jl b/base/strings/util.jl index 0ba76e1c76fa0..04d451a4fd288 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -613,22 +613,25 @@ function ctruncate(str::AbstractString, maxwidth::Integer, replacement::Union{Ab end end +# return whether textwidth(str) <= maxwidth +function check_textwidth(str::AbstractString, maxwidth::Integer) + # check efficiently for early return if str is wider than maxwidth + total_width = 0 + for c in str + total_width += textwidth(c) + total_width > maxwidth && return false + end + return true +end + function string_truncate_boundaries( str::AbstractString, maxwidth::Integer, replacement::Union{AbstractString,AbstractChar}, ::Val{mode}, prefer_left::Bool = true) where {mode} - maxwidth >= 0 || throw(ArgumentError("maxwidth $maxwidth should be non-negative")) - - # check efficiently for early return if str is less wide than maxwidth - total_width = 0 - for c in str - total_width += textwidth(c) - total_width > maxwidth && break - end - total_width <= maxwidth && return nothing + check_textwidth(str, maxwidth) && return nothing l0, _ = left, right = firstindex(str), lastindex(str) width = textwidth(replacement) diff --git a/test/show.jl b/test/show.jl index 976141f1ebb17..de5cf32b726ee 100644 --- a/test/show.jl +++ b/test/show.jl @@ -928,19 +928,19 @@ end # string show with elision @testset "string show with elision" begin @testset "elision logic" begin - strs = ["A", "∀", "∀A", "A∀", "😃"] + strs = ["A", "∀", "∀A", "A∀", "😃", "x̂"] for limit = 0:100, len = 0:100, str in strs str = str^len str = str[1:nextind(str, 0, len)] out = sprint() do io show(io, MIME"text/plain"(), str; limit) end - lower = length("\"\" ⋯ $(ncodeunits(str)) bytes ⋯ \"\"") + lower = textwidth("\"\" ⋯ $(ncodeunits(str)) bytes ⋯ \"\"") limit = max(limit, lower) - if length(str) + 2 ≤ limit + if textwidth(str) + 2 ≤ limit+1 && !contains(out, '⋯') @test eval(Meta.parse(out)) == str else - @test limit-!isascii(str) <= length(out) <= limit + @test limit-2 <= textwidth(out) <= limit re = r"(\"[^\"]*\") ⋯ (\d+) bytes ⋯ (\"[^\"]*\")" m = match(re, out) head = eval(Meta.parse(m.captures[1])) @@ -956,11 +956,11 @@ end @testset "default elision limit" begin r = replstr("x"^1000) - @test length(r) == 7*80 - @test r == repr("x"^271) * " ⋯ 459 bytes ⋯ " * repr("x"^270) + @test length(r) == 7*80-1 + @test r == repr("x"^270) * " ⋯ 460 bytes ⋯ " * repr("x"^270) r = replstr(["x"^1000]) @test length(r) < 120 - @test r == "1-element Vector{String}:\n " * repr("x"^31) * " ⋯ 939 bytes ⋯ " * repr("x"^30) + @test r == "1-element Vector{String}:\n " * repr("x"^30) * " ⋯ 940 bytes ⋯ " * repr("x"^30) end end From dc6072726ea721c21b7bef82e0a1c2392462ffdf Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 25 Oct 2024 01:41:35 +0200 Subject: [PATCH 492/548] Use `pwd()` as the default directory to walk in `walkdir` (#55550) --- base/file.jl | 7 +++++-- test/file.jl | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/base/file.jl b/base/file.jl index 567783c4b1e5b..c69a598f42623 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1100,7 +1100,7 @@ function _readdir(dir::AbstractString; return_objects::Bool=false, join::Bool=fa end """ - walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) + walkdir(dir = pwd(); topdown=true, follow_symlinks=false, onerror=throw) Return an iterator that walks the directory tree of a directory. @@ -1117,6 +1117,9 @@ resume where the last left off, like [`Iterators.Stateful`](@ref). See also: [`readdir`](@ref). +!!! compat "Julia 1.12" + `pwd()` as the default directory was added in Julia 1.12. + # Examples ```julia for (path, dirs, files) in walkdir(".") @@ -1146,7 +1149,7 @@ julia> (path, dirs, files) = first(itr) ("my/test/dir", String[], String[]) ``` """ -function walkdir(path; topdown=true, follow_symlinks=false, onerror=throw) +function walkdir(path = pwd(); topdown=true, follow_symlinks=false, onerror=throw) function _walkdir(chnl, path) tryf(f, p) = try f(p) diff --git a/test/file.jl b/test/file.jl index a4262c4eaaa21..498761d6a624b 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1728,6 +1728,13 @@ cd(dirwalk) do @test dirs == [] @test files == ["foo"] end + + # pwd() as default directory + for ((r1, d1, f1), (r2, d2, f2)) in zip(walkdir(), walkdir(pwd())) + @test r1 == r2 + @test d1 == d2 + @test f1 == f2 + end end rm(dirwalk, recursive=true) From beda6322f18dd5cb7c7f687afcd9e8b8122989aa Mon Sep 17 00:00:00 2001 From: Zentrik Date: Fri, 25 Oct 2024 00:43:24 +0100 Subject: [PATCH 493/548] Reset mtime of BOLTed files to prevent make rebuilding targets (#55587) This simplifies the `finish_stage` rule. Co-authored-by: Zentrik --- contrib/bolt/Makefile | 12 +++++++----- contrib/pgo-lto-bolt/Makefile | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/contrib/bolt/Makefile b/contrib/bolt/Makefile index ea92ba9ff936a..76833b9865020 100644 --- a/contrib/bolt/Makefile +++ b/contrib/bolt/Makefile @@ -79,22 +79,21 @@ copy_originals: stage1 # I don't think there's any particular reason to have -no-huge-pages here, perhaps slightly more accurate profile data # as the final build uses -no-huge-pages +# We reset the mtime of the files to prevent make from rebuilding targets depending on them. bolt_instrument: copy_originals for file in $(FILES_TO_OPTIMIZE); do \ abs_file=$(STAGE1_BUILD)/usr/lib/$$file; \ + old_time=$$(stat -c %Y $$abs_file); \ $(LLVM_BOLT) "$$abs_file.original" -o $$abs_file --instrument --instrumentation-file-append-pid --instrumentation-file="$(PROFILE_DIR)/$$file-prof" -no-huge-pages; \ mkdir -p $$(dirname "$(PROFILE_DIR)/$$file-prof"); \ + touch -d "@$$old_time" $$abs_file; \ printf "\n"; \ done && \ touch $@ @echo $(AFTER_INSTRUMENT_MESSAGE) -# We don't want to rebuild julia-src as then we lose the bolt instrumentation -# So we have to manually build the sysimage and package image finish_stage1: stage1 - $(MAKE) -C $(STAGE1_BUILD) julia-base-cache && \ - $(MAKE) -C $(STAGE1_BUILD) -f sysimage.mk sysimg-release && \ - $(MAKE) -C $(STAGE1_BUILD) -f pkgimage.mk release + $(MAKE) -C $(STAGE1_BUILD) merge_data: bolt_instrument for file in $(FILES_TO_OPTIMIZE); do \ @@ -108,10 +107,13 @@ merge_data: bolt_instrument # It tries to reuse old text segments to reduce binary size # BOLT doesn't fully support computed gotos https://github.com/llvm/llvm-project/issues/89117, so we cannot use --use-old-text on libjulia-internal # That flag saves less than 1 MiB for libjulia-internal so oh well. +# We reset the mtime of the files to prevent make from rebuilding targets depending on them. bolt: merge_data for file in $(FILES_TO_OPTIMIZE); do \ abs_file=$(STAGE1_BUILD)/usr/lib/$$file; \ + old_time=$$(stat -c %Y $$abs_file); \ $(LLVM_BOLT) "$$abs_file.original" -data "$(PROFILE_DIR)/$$file-prof.merged.fdata" -o $$abs_file $(BOLT_ARGS) $$(if [ "$$file" != $(shell readlink $(STAGE1_BUILD)/usr/lib/libjulia-internal.so) ]; then echo "--use-old-text -split-strategy=cdsplit"; fi); \ + touch -d "@$$old_time" $$abs_file; \ done && \ touch $@ diff --git a/contrib/pgo-lto-bolt/Makefile b/contrib/pgo-lto-bolt/Makefile index 2114b14991184..ce1b8b04f68c9 100644 --- a/contrib/pgo-lto-bolt/Makefile +++ b/contrib/pgo-lto-bolt/Makefile @@ -123,25 +123,24 @@ copy_originals: stage2 # I don't think there's any particular reason to have -no-huge-pages here, perhaps slightly more accurate profile data # as the final build uses -no-huge-pages +# We reset the mtime of the files to prevent make from rebuilding targets depending on them. bolt_instrument: copy_originals for file in $(FILES_TO_OPTIMIZE); do \ abs_file=$(STAGE2_BUILD)/usr/lib/$$file; \ + old_time=$$(stat -c %Y $$abs_file); \ $(LLVM_BOLT) "$$abs_file.original" -o $$abs_file --instrument --instrumentation-file-append-pid --instrumentation-file="$(BOLT_PROFILE_DIR)/$$file-prof" -no-huge-pages; \ mkdir -p $$(dirname "$(BOLT_PROFILE_DIR)/$$file-prof"); \ + touch -d "@$$old_time" $$abs_file; \ printf "\n"; \ done && \ touch $@ @echo $(AFTER_INSTRUMENT_MESSAGE) -# We don't want to rebuild julia-src as then we lose the bolt instrumentation -# So we have to manually build the sysimage and package image finish_stage2: PGO_CFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) finish_stage2: PGO_CXXFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) finish_stage2: PGO_LDFLAGS:=-flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe finish_stage2: stage2 - $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) julia-base-cache && \ - $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f sysimage.mk sysimg-release && \ - $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f pkgimage.mk release + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) merge_data: bolt_instrument for file in $(FILES_TO_OPTIMIZE); do \ @@ -155,10 +154,13 @@ merge_data: bolt_instrument # It tries to reuse old text segments to reduce binary size # BOLT doesn't fully support computed gotos https://github.com/llvm/llvm-project/issues/89117, so we cannot use --use-old-text on libjulia-internal # That flag saves less than 1 MiB for libjulia-internal so oh well. +# We reset the mtime of the files to prevent make from rebuilding targets depending on them. bolt: merge_data for file in $(FILES_TO_OPTIMIZE); do \ abs_file=$(STAGE2_BUILD)/usr/lib/$$file; \ + old_time=$$(stat -c %Y $$abs_file); \ $(LLVM_BOLT) "$$abs_file.original" -data "$(BOLT_PROFILE_DIR)/$$file-prof.merged.fdata" -o $$abs_file $(BOLT_ARGS) $$(if [ "$$file" != $(shell readlink $(STAGE2_BUILD)/usr/lib/libjulia-internal.so) ]; then echo "--use-old-text -split-strategy=cdsplit"; fi); \ + touch -d "@$$old_time" $$abs_file; \ done && \ touch $@ From c94102bea4443c8cab8c78589a96cbcddbea283e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 24 Oct 2024 22:43:21 -0400 Subject: [PATCH 494/548] add docstring note about `displaysize` and `IOContext` with `context` (#55510) --- base/show.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/base/show.jl b/base/show.jl index ee467ae90ff50..dbdd85f0608da 100644 --- a/base/show.jl +++ b/base/show.jl @@ -339,6 +339,11 @@ end IOContext(io::IO, context::IOContext) Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. + +!!! note + Unless explicitly set in the wrapped `io` the `displaysize` of `io` will not be inherited. + This is because by default `displaysize` is not a property of IO objects themselves, but lazily inferred, + as the size of the terminal window can change during the lifetime of the IO object. """ IOContext(io::IO, context::IO) = IOContext(io, ioproperties(context)) From ac5bb668dafbfb0ce96449cfa32e64821a4cce15 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 25 Oct 2024 09:45:44 +0530 Subject: [PATCH 495/548] LinearAlgebra: replace some hardcoded loop ranges with axes (#56243) These are safer in general, as well as easier to read. Also, narrow the scopes of some `@inbounds` annotations. --- stdlib/LinearAlgebra/src/generic.jl | 95 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 9c050a32bbda7..20c58e593d3f6 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -734,18 +734,15 @@ norm(::Missing, p::Real=2) = missing # special cases of opnorm function opnorm1(A::AbstractMatrix{T}) where T require_one_based_indexing(A) - m, n = size(A) Tnorm = typeof(float(real(zero(T)))) Tsum = promote_type(Float64, Tnorm) nrm::Tsum = 0 - @inbounds begin - for j = 1:n - nrmj::Tsum = 0 - for i = 1:m - nrmj += norm(A[i,j]) - end - nrm = max(nrm,nrmj) + for j in axes(A,2) + nrmj::Tsum = 0 + for i in axes(A,1) + nrmj += norm(@inbounds A[i,j]) end + nrm = max(nrm,nrmj) end return convert(Tnorm, nrm) end @@ -761,18 +758,15 @@ end function opnormInf(A::AbstractMatrix{T}) where T require_one_based_indexing(A) - m,n = size(A) Tnorm = typeof(float(real(zero(T)))) Tsum = promote_type(Float64, Tnorm) nrm::Tsum = 0 - @inbounds begin - for i = 1:m - nrmi::Tsum = 0 - for j = 1:n - nrmi += norm(A[i,j]) - end - nrm = max(nrm,nrmi) + for i in axes(A,1) + nrmi::Tsum = 0 + for j in axes(A,2) + nrmi += norm(@inbounds A[i,j]) end + nrm = max(nrm,nrmi) end return convert(Tnorm, nrm) end @@ -967,7 +961,7 @@ function dot(x::AbstractArray, y::AbstractArray) end s = zero(dot(first(x), first(y))) for (Ix, Iy) in zip(eachindex(x), eachindex(y)) - @inbounds s += dot(x[Ix], y[Iy]) + s += dot(@inbounds(x[Ix]), @inbounds(y[Iy])) end s end @@ -1008,11 +1002,11 @@ function dot(x::AbstractVector, A::AbstractMatrix, y::AbstractVector) s = zero(T) i₁ = first(eachindex(x)) x₁ = first(x) - @inbounds for j in eachindex(y) - yj = y[j] + for j in eachindex(y) + yj = @inbounds y[j] if !iszero(yj) - temp = zero(adjoint(A[i₁,j]) * x₁) - @simd for i in eachindex(x) + temp = zero(adjoint(@inbounds A[i₁,j]) * x₁) + @inbounds @simd for i in eachindex(x) temp += adjoint(A[i,j]) * x[i] end s += dot(temp, yj) @@ -1625,10 +1619,12 @@ function rotate!(x::AbstractVector, y::AbstractVector, c, s) if n != length(y) throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end - @inbounds for i = 1:n - xi, yi = x[i], y[i] - x[i] = c *xi + s*yi - y[i] = -conj(s)*xi + c*yi + for i in eachindex(x,y) + @inbounds begin + xi, yi = x[i], y[i] + x[i] = c *xi + s*yi + y[i] = -conj(s)*xi + c*yi + end end return x, y end @@ -1648,10 +1644,12 @@ function reflect!(x::AbstractVector, y::AbstractVector, c, s) if n != length(y) throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end - @inbounds for i = 1:n - xi, yi = x[i], y[i] - x[i] = c *xi + s*yi - y[i] = conj(s)*xi - c*yi + for i in eachindex(x,y) + @inbounds begin + xi, yi = x[i], y[i] + x[i] = c *xi + s*yi + y[i] = conj(s)*xi - c*yi + end end return x, y end @@ -1662,18 +1660,16 @@ end require_one_based_indexing(x) n = length(x) n == 0 && return zero(eltype(x)) - @inbounds begin - ξ1 = x[1] - normu = norm(x) - if iszero(normu) - return zero(ξ1/normu) - end - ν = T(copysign(normu, real(ξ1))) - ξ1 += ν - x[1] = -ν - for i = 2:n - x[i] /= ξ1 - end + ξ1 = @inbounds x[1] + normu = norm(x) + if iszero(normu) + return zero(ξ1/normu) + end + ν = T(copysign(normu, real(ξ1))) + ξ1 += ν + @inbounds x[1] = -ν + for i in 2:n + @inbounds x[i] /= ξ1 end ξ1/ν end @@ -1684,16 +1680,16 @@ end Multiplies `A` in-place by a Householder reflection on the left. It is equivalent to `A .= (I - conj(τ)*[1; x[2:end]]*[1; x[2:end]]')*A`. """ @inline function reflectorApply!(x::AbstractVector, τ::Number, A::AbstractVecOrMat) - require_one_based_indexing(x) + require_one_based_indexing(x, A) m, n = size(A, 1), size(A, 2) if length(x) != m throw(DimensionMismatch(lazy"reflector has length $(length(x)), which must match the first dimension of matrix A, $m")) end m == 0 && return A - @inbounds for j = 1:n - Aj, xj = view(A, 2:m, j), view(x, 2:m) - vAj = conj(τ)*(A[1, j] + dot(xj, Aj)) - A[1, j] -= vAj + for j in axes(A,2) + Aj, xj = @inbounds view(A, 2:m, j), view(x, 2:m) + vAj = conj(τ)*(@inbounds(A[1, j]) + dot(xj, Aj)) + @inbounds A[1, j] -= vAj axpy!(-vAj, xj, Aj) end return A @@ -1828,9 +1824,10 @@ julia> LinearAlgebra.det_bareiss!(M) ``` """ function det_bareiss!(M) + Base.require_one_based_indexing(M) n = checksquare(M) sign, prev = Int8(1), one(eltype(M)) - for i in 1:n-1 + for i in axes(M,2)[begin:end-1] if iszero(M[i,i]) # swap with another col to make nonzero swapto = findfirst(!iszero, @view M[i,i+1:end]) isnothing(swapto) && return zero(prev) @@ -2020,12 +2017,12 @@ function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) A = Base.unalias(B, A) if uplo == 'U' LAPACK.lacpy_size_check((m1, n1), (n < m ? n : m, n)) - for j in 1:n, i in 1:min(j,m) + for j in axes(A,2), i in axes(A,1)[begin : min(j,end)] @inbounds B[i,j] = A[i,j] end else # uplo == 'L' LAPACK.lacpy_size_check((m1, n1), (m, m < n ? m : n)) - for j in 1:n, i in j:m + for j in axes(A,2), i in axes(A,1)[j:end] @inbounds B[i,j] = A[i,j] end end From 29b509da2efb9fa499b567fd1d976532da5864fc Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:18:31 +0900 Subject: [PATCH 496/548] inference: fix `[modifyfield!|replacefield!]_tfunc`s (#56310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the following code snippet results in an internal error: ```julia julia> func(x) = @atomic :monotonic x[].count += 1; julia> let;Base.Experimental.@force_compile x = Ref(nothing) func(x) end Internal error: during type inference of ... ``` This issue is caused by the incorrect use of `_fieldtype_tfunc(𝕃, o, f)` within `modifyfield!_tfunc`, specifically because `o` should be `widenconst`ed, but it isn’t. By using `_fieldtype_tfunc` correctly, we can avoid the error through error-catching in `abstract_modifyop!`. This commit also includes a similar fix for `replacefield!_tfunc` as well. --- base/compiler/tfuncs.jl | 6 ++++-- test/compiler/inference.jl | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 450cfdcfadf82..a74146dcff552 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1347,13 +1347,15 @@ end return getfield_tfunc(𝕃, o, f) end @nospecs function modifyfield!_tfunc(𝕃::AbstractLattice, o, f, op, v, order=Symbol) - T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) + o′ = widenconst(o) + T = _fieldtype_tfunc(𝕃, o′, f, isconcretetype(o′)) T === Bottom && return Bottom PT = Const(Pair) return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T), true)[1] end @nospecs function replacefield!_tfunc(𝕃::AbstractLattice, o, f, x, v, success_order=Symbol, failure_order=Symbol) - T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) + o′ = widenconst(o) + T = _fieldtype_tfunc(𝕃, o′, f, isconcretetype(o′)) T === Bottom && return Bottom PT = Const(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T) return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T), true)[1] diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index dd62e329962c6..dab8e57aa2309 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6063,3 +6063,18 @@ end === Union{} @test Base.infer_return_type() do TypeVar(:Issue56248, Any, 1) end === Union{} + +@test Base.infer_return_type((Nothing,)) do x + @atomic x.count += 1 +end == Union{} +@test Base.infer_return_type((Nothing,)) do x + @atomicreplace x.count 0 => 1 +end == Union{} +mutable struct AtomicModifySafety + @atomic count::Int +end +let src = code_typed((Union{Nothing,AtomicModifySafety},)) do x + @atomic x.count += 1 + end |> only |> first + @test any(@nospecialize(x)->Meta.isexpr(x, :invoke_modify), src.code) +end From e8aacbf45ea24dc4e4d016fbc16ea2a8cd8c1f1d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:20:05 +0900 Subject: [PATCH 497/548] inference: don't allow `SSAValue`s in assignment lhs (#56314) In `InferenceState` the lhs of a `:=` expression should only contain `GlobalRef` or `SlotNumber` and no other IR elements. Currently when `SSAValue` appears in `lhs`, the invalid assignment effect is somehow ignored, but this is incorrect anyway, so this commit removes that check. Since `SSAValue` should not appear in `lhs` in the first place, this is not a significant change though. --- base/compiler/abstractinterpretation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index dbe79e19bf9b4..777240adf581b 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -3648,7 +3648,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr changes = StateUpdate(lhs, VarState(rt, false)) elseif isa(lhs, GlobalRef) handle_global_assignment!(interp, frame, lhs, rt) - elseif !isa(lhs, SSAValue) + else merge_effects!(interp, frame, EFFECTS_UNKNOWN) end end From ec2e1217fd816f53b8921d515634534930fa6e37 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Fri, 25 Oct 2024 03:25:34 -0400 Subject: [PATCH 498/548] Fix `unsafe_read` for `IOBuffer` with non dense data (#55776) Fixes one part of #54636 It was only safe to use the following if `from.data` was a dense vector of bytes. ```julia GC.@preserve from unsafe_copyto!(p, pointer(from.data, from.ptr), adv) ``` This PR adds a fallback suggested by @matthias314 in https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/7 --- base/iobuffer.jl | 21 ++++++++++++++++++++- base/strings/search.jl | 8 -------- test/iobuffer.jl | 10 ++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index bd924fd040496..7e309b9ad586c 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -194,7 +194,7 @@ function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) from.readable || _throw_not_readable() avail = bytesavailable(from) adv = min(avail, nb) - GC.@preserve from unsafe_copyto!(p, pointer(from.data, from.ptr), adv) + unsafe_read!(p, from.data, from.ptr, adv) from.ptr += adv if nb > avail throw(EOFError()) @@ -202,6 +202,25 @@ function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) nothing end +function unsafe_read!(dest::Ptr{UInt8}, src::AbstractVector{UInt8}, so::Integer, nbytes::UInt) + for i in 1:nbytes + unsafe_store!(dest, @inbounds(src[so+i-1]), i) + end +end + +# Note: Currently, CodeUnits <: DenseVector, which makes this union redundant w.r.t +# DenseArrayType{UInt8}, but this is a bug, and may be removed in future versions +# of Julia. See #54002 +const DenseBytes = Union{ + <:DenseArrayType{UInt8}, + CodeUnits{UInt8, <:Union{String, SubString{String}}}, +} + +function unsafe_read!(dest::Ptr{UInt8}, src::DenseBytes, so::Integer, nbytes::UInt) + GC.@preserve src unsafe_copyto!(dest, pointer(src, so), nbytes) + nothing +end + function peek(from::GenericIOBuffer, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) from.readable || _throw_not_readable() avail = bytesavailable(from) diff --git a/base/strings/search.jl b/base/strings/search.jl index a481b3af775e0..5f658e24526ba 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -61,14 +61,6 @@ function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar} end end -# Note: Currently, CodeUnits <: DenseVector, which makes this union redundant w.r.t -# DenseArrayType{UInt8}, but this is a bug, and may be removed in future versions -# of Julia. See #54002 -const DenseBytes = Union{ - <:DenseArrayType{UInt8}, - CodeUnits{UInt8, <:Union{String, SubString{String}}}, -} - function findfirst(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{UInt8, Int8}}, a::Union{DenseInt8, DenseUInt8}) findnext(pred, a, firstindex(a)) end diff --git a/test/iobuffer.jl b/test/iobuffer.jl index b5b34a2dbed8c..933662f7e41d1 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -389,3 +389,13 @@ end b = pushfirst!([0x02], 0x01) @test take!(IOBuffer(b)) == [0x01, 0x02] end + +@testset "#54636 reading from non-dense vectors" begin + data = 0x00:0xFF + io = IOBuffer(data) + @test read(io) == data + + data = @view(collect(0x00:0x0f)[begin:2:end]) + io = IOBuffer(data) + @test read(io) == data +end From b38fde1ad42c977878d4f481c962b108a3ae20ab Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Fri, 25 Oct 2024 09:26:36 +0200 Subject: [PATCH 499/548] support `isless` for zero-dimensional `AbstractArray`s (#55772) Fixes #55771 --- NEWS.md | 1 + base/abstractarray.jl | 9 +++++++++ test/arrayops.jl | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/NEWS.md b/NEWS.md index cf04fbf577248..658dcc7aa320e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -117,6 +117,7 @@ New library features * `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988]) * `Lockable` is now exported ([#54595]) * New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) +* `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772]) Standard library changes ------------------------ diff --git a/base/abstractarray.jl b/base/abstractarray.jl index e877a87c2cdd1..cbbae8e852b2e 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3044,6 +3044,15 @@ function cmp(A::AbstractVector, B::AbstractVector) return cmp(length(A), length(B)) end +""" + isless(A::AbstractArray{<:Any,0}, B::AbstractArray{<:Any,0}) + +Return `true` when the only element of `A` is less than the only element of `B`. +""" +function isless(A::AbstractArray{<:Any,0}, B::AbstractArray{<:Any,0}) + isless(only(A), only(B)) +end + """ isless(A::AbstractVector, B::AbstractVector) diff --git a/test/arrayops.jl b/test/arrayops.jl index ec8f54828b965..49d51176dcf71 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1409,6 +1409,14 @@ end end @testset "lexicographic comparison" begin + @testset "zero-dimensional" begin + vals = (0, 0.0, 1, 1.0) + for l ∈ vals + for r ∈ vals + @test cmp(fill(l), fill(r)) == cmp(l, r) + end + end + end @test cmp([1.0], [1]) == 0 @test cmp([1], [1.0]) == 0 @test cmp([1, 1], [1, 1]) == 0 From bf8f8142ea9b237533f53109b564b72cd9056682 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 25 Oct 2024 21:10:27 +0900 Subject: [PATCH 500/548] inference: don't add backdge when `applicable` inferred to return `Bool` (#56316) Also just as a minor backedge reduction optimization, this commit avoids adding backedges when `applicable` is inferred to return `::Bool`. --- base/compiler/tfuncs.jl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a74146dcff552..2f78348b79844 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -3008,14 +3008,16 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, else rt = Const(true) # has applicable matches end - for i in 1:napplicable - match = applicable[i]::MethodMatch - edge = specialize_method(match)::MethodInstance - add_backedge!(sv, edge) - end - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - add_uncovered_edges!(sv, matches, atype) + if rt !== Bool + for i in 1:napplicable + match = applicable[i]::MethodMatch + edge = specialize_method(match) + add_backedge!(sv, edge) + end + # also need an edge to the method table in case something gets + # added that did not intersect with any existing method + add_uncovered_edges!(sv, matches, atype) + end end return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo())) end From b81e33fe522f8a8f060f885f83944f11192cd0db Mon Sep 17 00:00:00 2001 From: Adrian Hill Date: Fri, 25 Oct 2024 15:22:00 +0200 Subject: [PATCH 501/548] Mark `require_one_based_indexing` and `has_offset_axes` as public (#56196) The discussion here mentions `require_one_based_indexing` being part of the public API: https://github.com/JuliaLang/julia/pull/43263 Both functions are also documented (albeit in the dev docs): * `require_one_based_indexing`: https://docs.julialang.org/en/v1/devdocs/offset-arrays/#man-custom-indices * `has_offset_axes`: https://docs.julialang.org/en/v1/devdocs/offset-arrays/#For-objects-that-mimic-AbstractArray-but-are-not-subtypes Towards https://github.com/JuliaLang/julia/issues/51335. --------- Co-authored-by: Matt Bauman --- NEWS.md | 1 + base/public.jl | 4 ++++ doc/src/base/arrays.md | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/NEWS.md b/NEWS.md index 658dcc7aa320e..228d133bd8557 100644 --- a/NEWS.md +++ b/NEWS.md @@ -116,6 +116,7 @@ New library features the uniquing checking ([#53474]) * `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988]) * `Lockable` is now exported ([#54595]) +* `Base.require_one_based_indexing` and `Base.has_offset_axes` are now public ([#56196]) * New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) * `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772]) diff --git a/base/public.jl b/base/public.jl index 1a23550485d84..8777a454c920a 100644 --- a/base/public.jl +++ b/base/public.jl @@ -28,6 +28,10 @@ public acquire, release, +# arrays + has_offset_axes, + require_one_based_indexing, + # collections IteratorEltype, IteratorSize, diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 66fe5c78f1ee6..defe497daf00c 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -115,6 +115,12 @@ Base.checkindex Base.elsize ``` +While most code can be written in an index-agnostic manner (see, e.g., [`eachindex`](@ref)), it can sometimes be useful to explicitly check for offset axes: +```@docs +Base.require_one_based_indexing +Base.has_offset_axes +``` + ## Views (SubArrays and other view types) A “view” is a data structure that acts like an array (it is a subtype of `AbstractArray`), but the underlying data is actually From fb297af78ed661faf491fd496dda4d261db07384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1ll=20Haraldsson?= Date: Fri, 25 Oct 2024 14:06:00 +0000 Subject: [PATCH 502/548] Avoid some allocations in various `println` methods (#56308) --- base/coreio.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/base/coreio.jl b/base/coreio.jl index 7fc608111d5f2..b5c543a25d5ad 100644 --- a/base/coreio.jl +++ b/base/coreio.jl @@ -1,8 +1,13 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +print(x) = print(stdout, x) +print(x1, x2) = print(stdout, x1, x2) +println(x) = print(stdout, x, "\n") +println(x1, x2) = print(stdout, x1, x2, "\n") + print(xs...) = print(stdout, xs...) -println(xs...) = println(stdout, xs...) -println(io::IO) = print(io, '\n') +println(xs...) = print(stdout, xs..., "\n") # fewer allocations than `println(stdout, xs...)` +println(io::IO) = print(io, "\n") function show end function repr end From 49e3b873fe443d94d87830181655b04ed857a9f7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 25 Oct 2024 19:39:22 +0530 Subject: [PATCH 503/548] Add a developer documentation section to the `LinearAlgebra` docs (#56324) Functions that are meant for package developers may go here, instead of the main section that is primarily for users. --- stdlib/LinearAlgebra/docs/src/index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index e3e79b7034969..1e44bf5cb04d7 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -891,6 +891,12 @@ LinearAlgebra.LAPACK.trsyl! LinearAlgebra.LAPACK.hseqr! ``` +## Developer Documentation + +```@docs +LinearAlgebra.matprod_dest +``` + ```@meta DocTestSetup = nothing ``` From db3d816985cf307520e7e86ebedba2d674af1353 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 25 Oct 2024 10:30:28 -0400 Subject: [PATCH 504/548] drop require lock when not needed during loading to allow parallel precompile loading (#56291) Fixes `_require_search_from_serialized` to first acquire all start_loading locks (using a deadlock-free batch-locking algorithm) before doing stalechecks and the rest, so that all the global computations happen behind the require_lock, then the rest can happen behind module-specific locks, then (as before) extensions can be loaded in parallel eventually after `require` returns. --- base/loading.jl | 270 ++++++++++++++++++++++++++++-------------------- 1 file changed, 157 insertions(+), 113 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 69bb332193519..6391e2511f8d5 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1261,47 +1261,52 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No assert_havelock(require_lock) timing_imports = TIMING_IMPORTS[] > 0 try - if timing_imports - t_before = time_ns() - cumulative_compile_timing(true) - t_comp_before = cumulative_compile_time_ns() - end + if timing_imports + t_before = time_ns() + cumulative_compile_timing(true) + t_comp_before = cumulative_compile_time_ns() + end - for i in eachindex(depmods) - dep = depmods[i] - dep isa Module && continue - _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} - dep = something(maybe_loaded_precompile(depkey, depbuild_id)) - @assert PkgId(dep) == depkey && module_build_id(dep) === depbuild_id - depmods[i] = dep - end + for i in eachindex(depmods) + dep = depmods[i] + dep isa Module && continue + _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} + dep = something(maybe_loaded_precompile(depkey, depbuild_id)) + @assert PkgId(dep) == depkey && module_build_id(dep) === depbuild_id + depmods[i] = dep + end - if ocachepath !== nothing - @debug "Loading object cache file $ocachepath for $(repr("text/plain", pkg))" - sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring, Cint), ocachepath, depmods, false, pkg.name, ignore_native) - else - @debug "Loading cache file $path for $(repr("text/plain", pkg))" - sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), path, depmods, false, pkg.name) - end - if isa(sv, Exception) - return sv - end + unlock(require_lock) # temporarily _unlock_ during these operations + sv = try + if ocachepath !== nothing + @debug "Loading object cache file $ocachepath for $(repr("text/plain", pkg))" + ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring, Cint), ocachepath, depmods, false, pkg.name, ignore_native) + else + @debug "Loading cache file $path for $(repr("text/plain", pkg))" + ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), path, depmods, false, pkg.name) + end + finally + lock(require_lock) + end + if isa(sv, Exception) + return sv + end - restored = register_restored_modules(sv, pkg, path) + restored = register_restored_modules(sv, pkg, path) - for M in restored - M = M::Module - if parentmodule(M) === M && PkgId(M) == pkg - register && register_root_module(M) - if timing_imports - elapsed_time = time_ns() - t_before - comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before - print_time_imports_report(M, elapsed_time, comp_time, recomp_time) + for M in restored + M = M::Module + if parentmodule(M) === M && PkgId(M) == pkg + register && register_root_module(M) + if timing_imports + elapsed_time = time_ns() - t_before + comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before + print_time_imports_report(M, elapsed_time, comp_time, recomp_time) + end + return M end - return M end - end - return ErrorException("Required dependency $(repr("text/plain", pkg)) failed to load from a cache file.") + return ErrorException("Required dependency $(repr("text/plain", pkg)) failed to load from a cache file.") finally timing_imports && cumulative_compile_timing(false) @@ -2020,13 +2025,46 @@ end if staledeps === true continue end - try - staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} - # finish checking staledeps module graph - for i in eachindex(staledeps) + staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} + startedloading = length(staledeps) + 1 + try # any exit from here (goto, break, continue, return) will end_loading + # finish checking staledeps module graph, while acquiring all start_loading locks + # so that concurrent require calls won't make any different decisions that might conflict with the decisions here + # note that start_loading will drop the loading lock if necessary + let i = 0 + # start_loading here has a deadlock problem if we try to load `A,B,C` and `B,A,D` at the same time: + # it will claim A,B have a cycle, but really they just have an ambiguous order and need to be batch-acquired rather than singly + # solve that by making sure we can start_loading everything before allocating each of those and doing all the stale checks + while i < length(staledeps) + i += 1 + dep = staledeps[i] + dep isa Module && continue + _, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} + dep = canstart_loading(modkey, modbuild_id, stalecheck) + if dep isa Module + if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id + staledeps[i] = dep + continue + else + @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." + @goto check_next_path + end + continue + elseif dep === nothing + continue + end + wait(dep) # releases require_lock, so requires restarting this loop + i = 0 + end + end + for i in reverse(eachindex(staledeps)) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} + # inline a call to start_loading here + @assert canstart_loading(modkey, modbuild_id, stalecheck) === nothing + package_locks[modkey] = current_task() => Threads.Condition(require_lock) + startedloading = i modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) for modpath_to_try in modpaths modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try; stalecheck) @@ -2054,37 +2092,22 @@ end end end # finish loading module graph into staledeps - # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock + # n.b. this runs __init__ methods too early, so it is very unwise to have those, as they may see inconsistent loading state, causing them to fail unpredictably here for i in eachindex(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} - dep = start_loading(modkey, modbuild_id, stalecheck) - while true - if dep isa Module - if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id - break - else - @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." - @goto check_next_path - end - end - if dep === nothing - try - set_pkgorigin_version_path(modkey, modpath) - dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) - finally - end_loading(modkey, dep) - end - if !isa(dep, Module) - @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep - @goto check_next_path - else - push!(newdeps, modkey) - end - end + set_pkgorigin_version_path(modkey, modpath) + dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) + if !isa(dep, Module) + @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep + @goto check_next_path + else + startedloading = i + 1 + end_loading(modkey, dep) + staledeps[i] = dep + push!(newdeps, modkey) end - staledeps[i] = dep end restored = maybe_loaded_precompile(pkg, newbuild_id) if !isa(restored, Module) @@ -2094,11 +2117,21 @@ end @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored @label check_next_path finally + # cancel all start_loading locks that were taken but not fulfilled before failing + for i in startedloading:length(staledeps) + dep = staledeps[i] + dep isa Module && continue + if dep isa Tuple{String, PkgId, UInt128} + _, modkey, _ = dep + else + _, modkey, _ = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} + end + end_loading(modkey, nothing) + end for modkey in newdeps insert_extension_triggers(modkey) stalecheck && run_package_callbacks(modkey) end - empty!(newdeps) end end end @@ -2111,66 +2144,76 @@ const package_locks = Dict{PkgId,Pair{Task,Threads.Condition}}() debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but more complete algorithm that can handle simultaneous tasks. # This only triggers if you have multiple tasks trying to load the same package at the same time, # so it is unlikely to make a performance difference normally. -function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) - # handle recursive and concurrent calls to require + +function canstart_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) assert_havelock(require_lock) require_lock.reentrancy_cnt == 1 || throw(ConcurrencyViolationError("recursive call to start_loading")) - while true - loaded = stalecheck ? maybe_root_module(modkey) : nothing + loaded = stalecheck ? maybe_root_module(modkey) : nothing + loaded isa Module && return loaded + if build_id != UInt128(0) + loaded = maybe_loaded_precompile(modkey, build_id) loaded isa Module && return loaded - if build_id != UInt128(0) - loaded = maybe_loaded_precompile(modkey, build_id) - loaded isa Module && return loaded + end + loading = get(package_locks, modkey, nothing) + loading === nothing && return nothing + # load already in progress for this module on the task + task, cond = loading + deps = String[modkey.name] + pkgid = modkey + assert_havelock(cond.lock) + if debug_loading_deadlocks && current_task() !== task + waiters = Dict{Task,Pair{Task,PkgId}}() # invert to track waiting tasks => loading tasks + for each in package_locks + cond2 = each[2][2] + assert_havelock(cond2.lock) + for waiting in cond2.waitq + push!(waiters, waiting => (each[2][1] => each[1])) + end end - loading = get(package_locks, modkey, nothing) - if loading === nothing - package_locks[modkey] = current_task() => Threads.Condition(require_lock) - return nothing + while true + running = get(waiters, task, nothing) + running === nothing && break + task, pkgid = running + push!(deps, pkgid.name) + task === current_task() && break end - # load already in progress for this module on the task - task, cond = loading - deps = String[modkey.name] - pkgid = modkey - assert_havelock(cond.lock) - if debug_loading_deadlocks && current_task() !== task - waiters = Dict{Task,Pair{Task,PkgId}}() # invert to track waiting tasks => loading tasks - for each in package_locks - cond2 = each[2][2] - assert_havelock(cond2.lock) - for waiting in cond2.waitq - push!(waiters, waiting => (each[2][1] => each[1])) - end - end - while true - running = get(waiters, task, nothing) - running === nothing && break - task, pkgid = running - push!(deps, pkgid.name) - task === current_task() && break + end + if current_task() === task + others = String[modkey.name] # repeat this to emphasize the cycle here + for each in package_locks # list the rest of the packages being loaded too + if each[2][1] === task + other = each[1].name + other == modkey.name || other == pkgid.name || push!(others, other) end end - if current_task() === task - others = String[modkey.name] # repeat this to emphasize the cycle here - for each in package_locks # list the rest of the packages being loaded too - if each[2][1] === task - other = each[1].name - other == modkey.name || other == pkgid.name || push!(others, other) - end - end - msg = sprint(deps, others) do io, deps, others - print(io, "deadlock detected in loading ") - join(io, deps, " -> ") - print(io, " -> ") - join(io, others, " && ") - end - throw(ConcurrencyViolationError(msg)) + msg = sprint(deps, others) do io, deps, others + print(io, "deadlock detected in loading ") + join(io, deps, " -> ") + print(io, " -> ") + join(io, others, " && ") + end + throw(ConcurrencyViolationError(msg)) + end + return cond +end + +function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) + # handle recursive and concurrent calls to require + while true + loaded = canstart_loading(modkey, build_id, stalecheck) + if loaded === nothing + package_locks[modkey] = current_task() => Threads.Condition(require_lock) + return nothing + elseif loaded isa Module + return loaded end - loaded = wait(cond) + loaded = wait(loaded) loaded isa Module && return loaded end end function end_loading(modkey::PkgId, @nospecialize loaded) + assert_havelock(require_lock) loading = pop!(package_locks, modkey) notify(loading[2], loaded, all=true) nothing @@ -2650,6 +2693,7 @@ function _require(pkg::PkgId, env=nothing) end # load a serialized file directly, including dependencies (without checking staleness except for immediate conflicts) +# this does not call start_loading / end_loading, so can lead to some odd behaviors function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Union{String, Nothing}, sourcepath::String) @lock require_lock begin set_pkgorigin_version_path(uuidkey, sourcepath) From 2b3a0f0ab41350e420934d288b68df7b3872b782 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:49:12 -0400 Subject: [PATCH 505/548] Make `String(::Memory)` copy (#54457) A more targeted fix of #54369 than #54372 Preserves the performance improvements added in #53962 by creating a new internal `_unsafe_takestring!(v::Memory{UInt8})` function that does what `String(::Memory{UInt8})` used to do. --- base/gmp.jl | 4 ++-- base/intfuncs.jl | 12 ++++++------ base/strings/string.jl | 13 +++++++------ base/strings/util.jl | 2 +- base/uuid.jl | 2 +- stdlib/FileWatching/src/pidfile.jl | 2 +- test/strings/basic.jl | 11 +++++++++++ 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index 1eaa20d6baecf..df0d9fee49348 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -11,7 +11,7 @@ import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, bin, oct, dec, hex, isequal, invmod, _prevpow2, _nextpow2, ndigits0zpb, widen, signed, unsafe_trunc, trunc, iszero, isone, big, flipsign, signbit, sign, hastypemax, isodd, iseven, digits!, hash, hash_integer, top_set_bit, - clamp + clamp, unsafe_takestring if Clong == Int32 const ClongMax = Union{Int8, Int16, Int32} @@ -761,7 +761,7 @@ function string(n::BigInt; base::Integer = 10, pad::Integer = 1) sv[i] = '0' % UInt8 end isneg(n) && (sv[1] = '-' % UInt8) - String(sv) + unsafe_takestring(sv) end function digits!(a::AbstractVector{T}, n::BigInt; base::Integer = 10) where {T<:Integer} diff --git a/base/intfuncs.jl b/base/intfuncs.jl index ec450aff2dff2..e8d4b65305be7 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -792,7 +792,7 @@ function bin(x::Unsigned, pad::Int, neg::Bool) i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') - String(a) + unsafe_takestring(a) end function oct(x::Unsigned, pad::Int, neg::Bool) @@ -806,7 +806,7 @@ function oct(x::Unsigned, pad::Int, neg::Bool) i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') - String(a) + unsafe_takestring(a) end # 2-digit decimal characters ("00":"99") @@ -876,7 +876,7 @@ function dec(x::Unsigned, pad::Int, neg::Bool) a = StringMemory(n) append_c_digits_fast(n, x, a, 1) neg && (@inbounds a[1] = 0x2d) # UInt8('-') - String(a) + unsafe_takestring(a) end function hex(x::Unsigned, pad::Int, neg::Bool) @@ -897,7 +897,7 @@ function hex(x::Unsigned, pad::Int, neg::Bool) @inbounds a[i] = d + ifelse(d > 0x9, 0x57, 0x30) end neg && (@inbounds a[1] = 0x2d) # UInt8('-') - String(a) + unsafe_takestring(a) end const base36digits = UInt8['0':'9';'a':'z'] @@ -922,7 +922,7 @@ function _base(base::Integer, x::Integer, pad::Int, neg::Bool) i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') - String(a) + unsafe_takestring(a) end split_sign(n::Integer) = unsigned(abs(n)), n < 0 @@ -998,7 +998,7 @@ function bitstring(x::T) where {T} x = lshr_int(x, 4) i -= 4 end - return String(str) + return unsafe_takestring(str) end """ diff --git a/base/strings/string.jl b/base/strings/string.jl index a46ee60e4f023..9f3c3d00e4b81 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -61,12 +61,7 @@ by [`take!`](@ref) on a writable [`IOBuffer`](@ref) and by calls to In other cases, `Vector{UInt8}` data may be copied, but `v` is truncated anyway to guarantee consistent behavior. """ -String(v::AbstractVector{UInt8}) = String(copyto!(StringMemory(length(v)), v)) -function String(v::Memory{UInt8}) - len = length(v) - len == 0 && return "" - return ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), v, len) -end +String(v::AbstractVector{UInt8}) = unsafe_takestring(copyto!(StringMemory(length(v)), v)) function String(v::Vector{UInt8}) #return ccall(:jl_array_to_string, Ref{String}, (Any,), v) len = length(v) @@ -83,6 +78,12 @@ function String(v::Vector{UInt8}) return str end +"Create a string re-using the memory, if possible. +Mutating or reading the memory after calling this function is undefined behaviour." +function unsafe_takestring(m::Memory{UInt8}) + isempty(m) ? "" : ccall(:jl_genericmemory_to_string, Ref{String}, (Any, Int), m, length(m)) +end + """ unsafe_string(p::Ptr{UInt8}, [length::Integer]) diff --git a/base/strings/util.jl b/base/strings/util.jl index 04d451a4fd288..fcccb9babadfd 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -1217,7 +1217,7 @@ function bytes2hex(itr) b[2i - 1] = hex_chars[1 + x >> 4] b[2i ] = hex_chars[1 + x & 0xf] end - return String(b) + return unsafe_takestring(b) end function bytes2hex(io::IO, itr) diff --git a/base/uuid.jl b/base/uuid.jl index 56f3a6aa417e7..4b9bae863d926 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -98,7 +98,7 @@ let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] u >>= 4 end @inbounds a[24] = a[19] = a[14] = a[9] = '-' - return String(a) + return unsafe_takestring(a) end end diff --git a/stdlib/FileWatching/src/pidfile.jl b/stdlib/FileWatching/src/pidfile.jl index 95b8f20face29..6862aaa9f8453 100644 --- a/stdlib/FileWatching/src/pidfile.jl +++ b/stdlib/FileWatching/src/pidfile.jl @@ -304,7 +304,7 @@ function open_exclusive(path::String; end function _rand_filename(len::Int=4) # modified from Base.Libc - slug = Base.StringMemory(len) + slug = Base.StringVector(len) chars = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" for i = 1:len slug[i] = chars[(Libc.rand() % length(chars)) + 1] diff --git a/test/strings/basic.jl b/test/strings/basic.jl index de04055d047af..ee92995bd2e11 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1093,6 +1093,17 @@ let v = [0x40,0x41,0x42] @test String(view(v, 2:3)) == "AB" end +# issue #54369 +let v = Base.StringMemory(3) + v .= [0x41,0x42,0x43] + s = String(v) + @test s == "ABC" + @test v == [0x41,0x42,0x43] + v[1] = 0x43 + @test s == "ABC" + @test v == [0x43,0x42,0x43] +end + # make sure length for identical String and AbstractString return the same value, PR #25533 let rng = MersenneTwister(1), strs = ["∀εa∀aε"*String(rand(rng, UInt8, 100))*"∀εa∀aε", String(rand(rng, UInt8, 200))] From f1a90e076186e77792c77e2e2f0905e14b19f321 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:07:33 -0400 Subject: [PATCH 506/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20799dc2d54=20to=20116ba910c=20(#56336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: 799dc2d54 New commit: 116ba910c Julia version: 1.12.0-DEV Pkg version: 1.12.0 Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/799dc2d54c4e809b9779de8c604564a5b3befaa0...116ba910c74ab565d348aa8a50d6dd10148f11ab ``` $ git log --oneline 799dc2d54..116ba910c 116ba910c fix Base.unreference_module call (#4057) 6ed1d2f40 do not show right hand progress without colors (#4047) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 | 1 + .../Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 | 1 + .../Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 | 1 - .../Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 create mode 100644 deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 diff --git a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 new file mode 100644 index 0000000000000..61dca3054d58f --- /dev/null +++ b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 @@ -0,0 +1 @@ +9905cd10c29974f3b0bb47f2e40951b0 diff --git a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 new file mode 100644 index 0000000000000..3757366fd23cf --- /dev/null +++ b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 @@ -0,0 +1 @@ +b99db15e6646b1eaa35df705ca39c7f3ddb05073293c779963231c22d17f4ae449739f4e8535a41ae9ae5fb1661f76c915fb2c7853a86fc695335b3e1ce3c06d diff --git a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 deleted file mode 100644 index 7c0bfbf62bd6e..0000000000000 --- a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6fce8506a1701acdcbc4888250eeb86a diff --git a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 b/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 deleted file mode 100644 index 06e3ea9c8dfa7..0000000000000 --- a/deps/checksums/Pkg-799dc2d54c4e809b9779de8c604564a5b3befaa0.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e251745da221a82f3ec5e21a76c29df0b695dc4028ee2c719373c08637050318db7b543c9d40074314fc3495738d39fd8af5a7954e8b72695df44e25e395f883 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index c29c83fce4046..24c73834eca22 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 799dc2d54c4e809b9779de8c604564a5b3befaa0 +PKG_SHA1 = 116ba910c74ab565d348aa8a50d6dd10148f11ab PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From f6a38e00c009292e2b2730758bb99a154809c413 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:51:27 -0300 Subject: [PATCH 507/548] Wall-time/all tasks profiler (#55889) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One limitation of sampling CPU/thread profiles, as is currently done in Julia, is that they primarily capture samples from CPU-intensive tasks. If many tasks are performing IO or contending for concurrency primitives like semaphores, these tasks won’t appear in the profile, as they aren't scheduled on OS threads sampled by the profiler. A wall-time profiler, like the one implemented in this PR, samples tasks regardless of OS thread scheduling. This enables profiling of IO-heavy tasks and detecting areas of heavy contention in the system. Co-developed with @nickrobinson251. --- NEWS.md | 2 + doc/src/manual/img/cpu-profile.png | Bin 0 -> 147343 bytes doc/src/manual/img/task-sampling-failure.png | Bin 0 -> 359826 bytes .../wall-time-profiler-channel-example.png | Bin 0 -> 191682 bytes ...ll-time-profiler-compute-bound-example.png | Bin 0 -> 435521 bytes doc/src/manual/profile.md | 214 ++++++++++++++++++ src/gc-stacks.c | 33 +++ src/gc-stock.c | 24 +- src/init.c | 4 + src/julia_internal.h | 29 +++ src/julia_threads.h | 1 + src/mtarraylist.c | 2 +- src/signal-handling.c | 135 +++++++++-- src/signals-mach.c | 178 ++++++++------- src/signals-unix.c | 170 ++++++++------ src/signals-win.c | 39 ++-- src/stackwalk.c | 58 +++-- stdlib/Profile/src/Profile.jl | 77 +++++-- stdlib/Profile/test/runtests.jl | 61 ++++- 19 files changed, 796 insertions(+), 231 deletions(-) create mode 100644 doc/src/manual/img/cpu-profile.png create mode 100644 doc/src/manual/img/task-sampling-failure.png create mode 100644 doc/src/manual/img/wall-time-profiler-channel-example.png create mode 100644 doc/src/manual/img/wall-time-profiler-compute-bound-example.png diff --git a/NEWS.md b/NEWS.md index 228d133bd8557..079625b1610aa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -213,4 +213,6 @@ External dependencies Tooling Improvements -------------------- +- A wall-time profiler is now available for users who need a sampling profiler that captures tasks regardless of their scheduling or running state. This type of profiler enables profiling of I/O-heavy tasks and helps detect areas of heavy contention in the system ([#55889]). + diff --git a/doc/src/manual/img/cpu-profile.png b/doc/src/manual/img/cpu-profile.png new file mode 100644 index 0000000000000000000000000000000000000000..ec48b41f6e78b9975c1bf1fc670d8221514f249f GIT binary patch literal 147343 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%0DlU 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д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�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 z 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

gq!7n(IsL)&H8$Ml%d96QRO>Rv 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

;`arX$fO(N*1UWRDSh*sDK+UZL97*D9v@)%9{0 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ƋF3Qs{u)$ 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 literal 0 HcmV?d00001 diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index a3baec593a53d..718b79815473e 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -297,6 +297,220 @@ Of course, you can decrease the delay as well as increase it; however, the overh grows once the delay becomes similar to the amount of time needed to take a backtrace (~30 microseconds on the author's laptop). +## Wall-time Profiler + +### Introduction & Problem Motivation + +The profiler described in the previous section is a sampling CPU profiler. At a high level, the profiler periodically stops all Julia compute threads to collect their backtraces and estimates the time spent in each function based on the number of backtrace samples that include a frame from that function. However, note that only tasks currently running on system threads just before the profiler stops them will have their backtraces collected. + +While this profiler is typically well-suited for workloads where the majority of tasks are compute-bound, it is less helpful for systems where most tasks are IO-heavy or for diagnosing contention on synchronization primitives in your code. + +Let's consider this simple workload: + +```Julia +using Base.Threads +using Profile +using PProf + +ch = Channel(1) + +const N_SPAWNED_TASKS = (1 << 10) +const WAIT_TIME_NS = 10_000_000 + +function spawn_a_bunch_of_tasks_waiting_on_channel() + for i in 1:N_SPAWNED_TASKS + Threads.@spawn begin + take!(ch) + end + end +end + +function busywait() + t0 = time_ns() + while true + if time_ns() - t0 > WAIT_TIME_NS + break + end + end +end + +function main() + spawn_a_bunch_of_tasks_waiting_on_channel() + for i in 1:N_SPAWNED_TASKS + put!(ch, i) + busywait() + end +end + +Profile.@profile main() +``` + +Our goal is to detect whether there is contention on the `ch` channel—i.e., whether the number of waiters is excessive given the rate at which work items are being produced in the channel. + +If we run this, we obtain the following [PProf](https://github.com/JuliaPerf/PProf.jl) flame graph: + +![CPU Profile](./img/cpu-profile.png)() + +This profile provides no information to help determine where contention occurs in the system’s synchronization primitives. Waiters on a channel will be blocked and descheduled, meaning no system thread will be running the tasks assigned to those waiters, and as a result, they won't be sampled by the profiler. + +### Wall-time Profiler + +Instead of sampling threads—and thus only sampling tasks that are running—a wall-time task profiler samples tasks independently of their scheduling state. For example, tasks that are sleeping on a synchronization primitive at the time the profiler is running will be sampled with the same probability as tasks that were actively running when the profiler attempted to capture backtraces. + +This approach allows us to construct a profile where backtraces from tasks blocked on the `ch` channel, as in the example above, are actually represented. + +Let's run the same example, but now with a wall-time profiler: + + +```Julia +using Base.Threads +using Profile +using PProf + +ch = Channel(1) + +const N_SPAWNED_TASKS = (1 << 10) +const WAIT_TIME_NS = 10_000_000 + +function spawn_a_bunch_of_tasks_waiting_on_channel() + for i in 1:N_SPAWNED_TASKS + Threads.@spawn begin + take!(ch) + end + end +end + +function busywait() + t0 = time_ns() + while true + if time_ns() - t0 > WAIT_TIME_NS + break + end + end +end + +function main() + spawn_a_bunch_of_tasks_waiting_on_channel() + for i in 1:N_SPAWNED_TASKS + put!(ch, i) + busywait() + end +end + +Profile.@profile_walltime main() +``` + +We obtain the following flame graph: + +![Wall-time Profile Channel](./img/wall-time-profiler-channel-example.png)() + +We see that a large number of samples come from channel-related `take!` functions, which allows us to determine that there is indeed an excessive number of waiters in `ch`. + +### A Compute-Bound Workload + +Despite the wall-time profiler sampling all live tasks in the system and not just the currently running ones, it can still be helpful for identifying performance hotspots, even if your code is compute-bound. Let’s consider a simple example: + +```Julia +using Base.Threads +using Profile +using PProf + +ch = Channel(1) + +const MAX_ITERS = (1 << 22) +const N_TASKS = (1 << 12) + +function spawn_a_task_waiting_on_channel() + Threads.@spawn begin + take!(ch) + end +end + +function sum_of_sqrt() + sum_of_sqrt = 0.0 + for i in 1:MAX_ITERS + sum_of_sqrt += sqrt(i) + end + return sum_of_sqrt +end + +function spawn_a_bunch_of_compute_heavy_tasks() + Threads.@sync begin + for i in 1:N_TASKS + Threads.@spawn begin + sum_of_sqrt() + end + end + end +end + +function main() + spawn_a_task_waiting_on_channel() + spawn_a_bunch_of_compute_heavy_tasks() +end + +Profile.@profile_walltime main() +``` + +After collecting a wall-time profile, we get the following flame graph: + +![Wall-time Profile Compute-Bound](./img/wall-time-profiler-compute-bound-example.png)() + +Notice how many of the samples contain `sum_of_sqrt`, which is the expensive compute function in our example. + +### Identifying Task Sampling Failures in your Profile + +In the current implementation, the wall-time profiler attempts to sample from tasks that have been alive since the last garbage collection, along with those created afterward. However, if most tasks are extremely short-lived, you may end up sampling tasks that have already completed, resulting in missed backtrace captures. + +If you encounter samples containing `failed_to_sample_task_fun` or `failed_to_stop_thread_fun`, this likely indicates a high volume of short-lived tasks, which prevented their backtraces from being collected. + +Let's consider this simple example: + +```Julia +using Base.Threads +using Profile +using PProf + +const N_SPAWNED_TASKS = (1 << 16) +const WAIT_TIME_NS = 100_000 + +function spawn_a_bunch_of_short_lived_tasks() + for i in 1:N_SPAWNED_TASKS + Threads.@spawn begin + # Do nothing + end + end +end + +function busywait() + t0 = time_ns() + while true + if time_ns() - t0 > WAIT_TIME_NS + break + end + end +end + +function main() + GC.enable(false) + spawn_a_bunch_of_short_lived_tasks() + for i in 1:N_SPAWNED_TASKS + busywait() + end + GC.enable(true) +end + +Profile.@profile_walltime main() +``` + +Notice that the tasks spawned in `spawn_a_bunch_of_short_lived_tasks` are extremely short-lived. Since these tasks constitute the majority in the system, we will likely miss capturing a backtrace for most sampled tasks. + +After collecting a wall-time profile, we obtain the following flame graph: + +![Task Sampling Failure](./img/task-sampling-failure.png)() + +The large number of samples from `failed_to_stop_thread_fun` confirms that we have a significant number of short-lived tasks in the system. + ## Memory allocation analysis One of the most common techniques to improve performance is to reduce memory allocation. Julia diff --git a/src/gc-stacks.c b/src/gc-stacks.c index a2d3862dc9501..a0ca2561c5cf9 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -296,6 +296,39 @@ void sweep_stack_pool_loop(void) JL_NOTSAFEPOINT jl_atomic_fetch_add(&gc_n_threads_sweeping_stacks, -1); } +// Builds a list of the live tasks. Racy: `live_tasks` can expand at any time. +arraylist_t *jl_get_all_tasks_arraylist(void) JL_NOTSAFEPOINT +{ + arraylist_t *tasks = (arraylist_t*)malloc_s(sizeof(arraylist_t)); + arraylist_new(tasks, 0); + size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); + jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); + for (size_t i = 0; i < nthreads; i++) { + // skip GC threads... + if (gc_is_collector_thread(i)) { + continue; + } + jl_ptls_t ptls2 = allstates[i]; + if (ptls2 == NULL) { + continue; + } + jl_task_t *t = ptls2->root_task; + if (t->ctx.stkbuf != NULL) { + arraylist_push(tasks, t); + } + small_arraylist_t *live_tasks = &ptls2->gc_tls_common.heap.live_tasks; + size_t n = mtarraylist_length(live_tasks); + for (size_t i = 0; i < n; i++) { + jl_task_t *t = (jl_task_t*)mtarraylist_get(live_tasks, i); + assert(t != NULL); + if (t->ctx.stkbuf != NULL) { + arraylist_push(tasks, t); + } + } + } + return tasks; +} + JL_DLLEXPORT jl_array_t *jl_live_tasks(void) { size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); diff --git a/src/gc-stock.c b/src/gc-stock.c index 541c5b4ecc5c2..3a2027f9190a7 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -1025,7 +1025,22 @@ void gc_sweep_wait_for_all_stacks(void) JL_NOTSAFEPOINT } } -void sweep_stack_pools(jl_ptls_t ptls) JL_NOTSAFEPOINT +void sweep_mtarraylist_buffers(void) JL_NOTSAFEPOINT +{ + for (int i = 0; i < gc_n_threads; i++) { + jl_ptls_t ptls = gc_all_tls_states[i]; + if (ptls == NULL) { + continue; + } + small_arraylist_t *buffers = &ptls->lazily_freed_mtarraylist_buffers; + void *buf; + while ((buf = small_arraylist_pop(buffers)) != NULL) { + free(buf); + } + } +} + +void sweep_stack_pools_and_mtarraylist_buffers(jl_ptls_t ptls) JL_NOTSAFEPOINT { // initialize ptls index for parallel sweeping of stack pools assert(gc_n_threads); @@ -1035,9 +1050,12 @@ void sweep_stack_pools(jl_ptls_t ptls) JL_NOTSAFEPOINT else jl_atomic_store_relaxed(&gc_stack_free_idx, stack_free_idx + 1); jl_atomic_store_release(&gc_ptls_sweep_idx, gc_n_threads - 1); // idx == gc_n_threads = release stacks to the OS so it's serial + uv_mutex_lock(&live_tasks_lock); gc_sweep_wake_all_stacks(ptls); sweep_stack_pool_loop(); gc_sweep_wait_for_all_stacks(); + sweep_mtarraylist_buffers(); + uv_mutex_unlock(&live_tasks_lock); } static void gc_pool_sync_nfree(jl_gc_pagemeta_t *pg, jl_taggedvalue_t *last) JL_NOTSAFEPOINT @@ -3084,7 +3102,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) current_sweep_full = sweep_full; sweep_weak_refs(); uint64_t stack_pool_time = jl_hrtime(); - sweep_stack_pools(ptls); + sweep_stack_pools_and_mtarraylist_buffers(ptls); stack_pool_time = jl_hrtime() - stack_pool_time; gc_num.total_stack_pool_sweep_time += stack_pool_time; gc_num.stack_pool_sweep_time = stack_pool_time; @@ -3453,6 +3471,8 @@ void jl_init_thread_heap(jl_ptls_t ptls) jl_atomic_store_relaxed(&q->bottom, 0); jl_atomic_store_relaxed(&q->array, wsa2); arraylist_new(&mq->reclaim_set, 32); + // Initialize `lazily_freed_mtarraylist_buffers` + small_arraylist_new(&ptls->lazily_freed_mtarraylist_buffers, 0); memset(&ptls->gc_tls_common.gc_num, 0, sizeof(ptls->gc_tls_common.gc_num)); jl_atomic_store_relaxed(&ptls->gc_tls_common.gc_num.allocd, -(int64_t)gc_num.interval); diff --git a/src/init.c b/src/init.c index 413d4e8055e54..b3ca33344d258 100644 --- a/src/init.c +++ b/src/init.c @@ -744,6 +744,10 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) // initialize symbol-table lock uv_mutex_init(&symtab_lock); + // initialize the live tasks lock + uv_mutex_init(&live_tasks_lock); + // initialize the profiler buffer lock + uv_mutex_init(&bt_data_prof_lock); // initialize backtraces jl_init_profile_lock(); diff --git a/src/julia_internal.h b/src/julia_internal.h index 8c4ee9fca36e0..ade5940f30687 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -211,6 +211,35 @@ JL_DLLEXPORT void jl_unlock_profile_wr(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEA int jl_lock_stackwalk(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER; void jl_unlock_stackwalk(int lockret) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE; +arraylist_t *jl_get_all_tasks_arraylist(void) JL_NOTSAFEPOINT; +typedef struct { + size_t bt_size; + int tid; +} jl_record_backtrace_result_t; +JL_DLLEXPORT jl_record_backtrace_result_t jl_record_backtrace(jl_task_t *t, struct _jl_bt_element_t *bt_data, + size_t max_bt_size, int all_tasks_profiler) JL_NOTSAFEPOINT; +extern volatile struct _jl_bt_element_t *profile_bt_data_prof; +extern volatile size_t profile_bt_size_max; +extern volatile size_t profile_bt_size_cur; +extern volatile int profile_running; +extern volatile int profile_all_tasks; +// Ensures that we can safely read the `live_tasks`field of every TLS when profiling. +// We want to avoid the case that a GC gets interleaved with `jl_profile_task` and shrinks +// the `live_tasks` array while we are reading it or frees tasks that are being profiled. +// Because of that, this lock must be held in `jl_profile_task` and `sweep_stack_pools_and_mtarraylist_buffers`. +extern uv_mutex_t live_tasks_lock; +// Ensures that we can safely write to `profile_bt_data_prof` and `profile_bt_size_cur`. +// We want to avoid the case that: +// - We start to profile a task very close to the profiling time window end. +// - The profiling time window ends and we start to read the profile data in a compute thread. +// - We write to the profile in a profiler thread while the compute thread is reading it. +// Locking discipline: `bt_data_prof_lock` must be held inside the scope of `live_tasks_lock`. +extern uv_mutex_t bt_data_prof_lock; +#define PROFILE_STATE_THREAD_NOT_SLEEPING (1) +#define PROFILE_STATE_THREAD_SLEEPING (2) +#define PROFILE_STATE_WALL_TIME_PROFILING (3) +void jl_profile_task(void); + // number of cycles since power-on static inline uint64_t cycleclock(void) JL_NOTSAFEPOINT { diff --git a/src/julia_threads.h b/src/julia_threads.h index 67da2978b4267..faa8ab9e0aaf4 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -157,6 +157,7 @@ typedef struct _jl_tls_states_t { int finalizers_inhibited; jl_gc_tls_states_t gc_tls; // this is very large, and the offset of the first member is baked into codegen jl_gc_tls_states_common_t gc_tls_common; // common tls for both GCs + small_arraylist_t lazily_freed_mtarraylist_buffers; volatile sig_atomic_t defer_signal; _Atomic(struct _jl_task_t*) current_task; struct _jl_task_t *next_task; diff --git a/src/mtarraylist.c b/src/mtarraylist.c index 8bad44797dab4..7af265a86ab63 100644 --- a/src/mtarraylist.c +++ b/src/mtarraylist.c @@ -37,7 +37,7 @@ static void mtarraylist_resizeto(small_mtarraylist_t *a, size_t len, size_t newl a->max = nm; if (olditems != (void*)&a->_space[0]) { jl_task_t *ct = jl_current_task; - jl_gc_add_quiescent(ct->ptls, (void**)olditems, free); + small_arraylist_push(&ct->ptls->lazily_freed_mtarraylist_buffers, olditems); } } } diff --git a/src/signal-handling.c b/src/signal-handling.c index ce7e8ba57af19..ff073cc82a0a5 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -18,46 +18,48 @@ extern "C" { #include // Profiler control variables -// Note: these "static" variables are also used in "signals-*.c" -static volatile jl_bt_element_t *bt_data_prof = NULL; -static volatile size_t bt_size_max = 0; -static volatile size_t bt_size_cur = 0; +uv_mutex_t live_tasks_lock; +uv_mutex_t bt_data_prof_lock; +volatile jl_bt_element_t *profile_bt_data_prof = NULL; +volatile size_t profile_bt_size_max = 0; +volatile size_t profile_bt_size_cur = 0; static volatile uint64_t nsecprof = 0; -static volatile int running = 0; -static const uint64_t GIGA = 1000000000ULL; +volatile int profile_running = 0; +volatile int profile_all_tasks = 0; +static const uint64_t GIGA = 1000000000ULL; // Timers to take samples at intervals JL_DLLEXPORT void jl_profile_stop_timer(void); -JL_DLLEXPORT int jl_profile_start_timer(void); +JL_DLLEXPORT int jl_profile_start_timer(uint8_t); /////////////////////// // Utility functions // /////////////////////// JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec) { - bt_size_max = maxsize; + profile_bt_size_max = maxsize; nsecprof = delay_nsec; - if (bt_data_prof != NULL) - free((void*)bt_data_prof); - bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t)); - if (bt_data_prof == NULL && maxsize > 0) + if (profile_bt_data_prof != NULL) + free((void*)profile_bt_data_prof); + profile_bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t)); + if (profile_bt_data_prof == NULL && maxsize > 0) return -1; - bt_size_cur = 0; + profile_bt_size_cur = 0; return 0; } JL_DLLEXPORT uint8_t *jl_profile_get_data(void) { - return (uint8_t*) bt_data_prof; + return (uint8_t*) profile_bt_data_prof; } JL_DLLEXPORT size_t jl_profile_len_data(void) { - return bt_size_cur; + return profile_bt_size_cur; } JL_DLLEXPORT size_t jl_profile_maxlen_data(void) { - return bt_size_max; + return profile_bt_size_max; } JL_DLLEXPORT uint64_t jl_profile_delay_nsec(void) @@ -67,12 +69,12 @@ JL_DLLEXPORT uint64_t jl_profile_delay_nsec(void) JL_DLLEXPORT void jl_profile_clear_data(void) { - bt_size_cur = 0; + profile_bt_size_cur = 0; } JL_DLLEXPORT int jl_profile_is_running(void) { - return running; + return profile_running; } // Any function that acquires this lock must be either a unmanaged thread @@ -184,7 +186,102 @@ JL_DLLEXPORT int jl_profile_is_buffer_full(void) // Declare buffer full if there isn't enough room to sample even just the // thread metadata and one max-sized frame. The `+ 6` is for the two block // terminator `0`'s plus the 4 metadata entries. - return bt_size_cur + ((JL_BT_MAX_ENTRY_SIZE + 1) + 6) > bt_size_max; + return profile_bt_size_cur + ((JL_BT_MAX_ENTRY_SIZE + 1) + 6) > profile_bt_size_max; +} + +NOINLINE int failed_to_sample_task_fun(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; +NOINLINE int failed_to_stop_thread_fun(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; + +#define PROFILE_TASK_DEBUG_FORCE_SAMPLING_FAILURE (0) +#define PROFILE_TASK_DEBUG_FORCE_STOP_THREAD_FAILURE (0) + +void jl_profile_task(void) +{ + if (jl_profile_is_buffer_full()) { + // Buffer full: Delete the timer + jl_profile_stop_timer(); + return; + } + + jl_task_t *t = NULL; + int got_mutex = 0; + if (uv_mutex_trylock(&live_tasks_lock) != 0) { + goto collect_backtrace; + } + got_mutex = 1; + + arraylist_t *tasks = jl_get_all_tasks_arraylist(); + uint64_t seed = jl_rand(); + const int n_max_random_attempts = 4; + // randomly select a task that is not done + for (int i = 0; i < n_max_random_attempts; i++) { + t = (jl_task_t*)tasks->items[cong(tasks->len, &seed)]; + assert(t == NULL || jl_is_task(t)); + if (t == NULL) { + continue; + } + int t_state = jl_atomic_load_relaxed(&t->_state); + if (t_state == JL_TASK_STATE_DONE) { + continue; + } + break; + } + arraylist_free(tasks); + free(tasks); + +collect_backtrace: + + uv_mutex_lock(&bt_data_prof_lock); + if (profile_running == 0) { + uv_mutex_unlock(&bt_data_prof_lock); + if (got_mutex) { + uv_mutex_unlock(&live_tasks_lock); + } + return; + } + + jl_record_backtrace_result_t r = {0, INT16_MAX}; + jl_bt_element_t *bt_data_prof = (jl_bt_element_t*)(profile_bt_data_prof + profile_bt_size_cur); + size_t bt_size_max = profile_bt_size_max - profile_bt_size_cur - 1; + if (t == NULL || PROFILE_TASK_DEBUG_FORCE_SAMPLING_FAILURE) { + // failed to find a task + r.bt_size = failed_to_sample_task_fun(bt_data_prof, bt_size_max, 0); + } + else { + if (!PROFILE_TASK_DEBUG_FORCE_STOP_THREAD_FAILURE) { + r = jl_record_backtrace(t, bt_data_prof, bt_size_max, 1); + } + // we failed to get a backtrace + if (r.bt_size == 0) { + r.bt_size = failed_to_stop_thread_fun(bt_data_prof, bt_size_max, 0); + } + } + + // update the profile buffer size + profile_bt_size_cur += r.bt_size; + + // store threadid but add 1 as 0 is preserved to indicate end of block + profile_bt_data_prof[profile_bt_size_cur++].uintptr = (uintptr_t)r.tid + 1; + + // store task id (never null) + profile_bt_data_prof[profile_bt_size_cur++].jlvalue = (jl_value_t*)t; + + // store cpu cycle clock + profile_bt_data_prof[profile_bt_size_cur++].uintptr = cycleclock(); + + // the thread profiler uses this block to record whether the thread is not sleeping (1) or sleeping (2) + // let's use a dummy value which is not 1 or 2 to + // indicate that we are profiling a task, and therefore, this block is not about the thread state + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 3; + + // Mark the end of this block with two 0's + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + + uv_mutex_unlock(&bt_data_prof_lock); + if (got_mutex) { + uv_mutex_unlock(&live_tasks_lock); + } } static uint64_t jl_last_sigint_trigger = 0; diff --git a/src/signals-mach.c b/src/signals-mach.c index a939e4df71ae0..24508a8902d5e 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -724,6 +724,84 @@ void jl_unlock_stackwalk(int lockret) jl_unlock_profile_mach(1, lockret); } +// assumes holding `jl_lock_profile_mach` +void jl_profile_thread_mach(int tid) +{ + // if there is no space left, return early + if (jl_profile_is_buffer_full()) { + jl_profile_stop_timer(); + return; + } + if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_prepare(); + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_prepare(); // briefly acquire the dlsym lock + host_thread_state_t state; + int valid_thread = jl_thread_suspend_and_get_state2(tid, &state); + unw_context_t *uc = (unw_context_t*)&state; + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_parent(); // quickly release the dlsym lock + if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_parent(); + if (!valid_thread) + return; + if (profile_running) { +#ifdef LLVMLIBUNWIND + /* + * Unfortunately compact unwind info is incorrectly generated for quite a number of + * libraries by quite a large number of compilers. We can fall back to DWARF unwind info + * in some cases, but in quite a number of cases (especially libraries not compiled in debug + * mode, only the compact unwind info may be available). Even more unfortunately, there is no + * way to detect such bogus compact unwind info (other than noticing the resulting segfault). + * What we do here is ugly, but necessary until the compact unwind info situation improves. + * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. + * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost + * entry will always be correct, and the number of cases in which this is an issue is rather small. + * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling + * and during stack unwinding we only ever read memory, but never write it. + */ + + forceDwarf = 0; + unw_getcontext(&profiler_uc); // will resume from this point if the next lines segfault at any point + + if (forceDwarf == 0) { + // Save the backtrace + profile_bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)profile_bt_data_prof + profile_bt_size_cur, profile_bt_size_max - profile_bt_size_cur - 1, uc, NULL); + } + else if (forceDwarf == 1) { + profile_bt_size_cur += rec_backtrace_ctx_dwarf((jl_bt_element_t*)profile_bt_data_prof + profile_bt_size_cur, profile_bt_size_max - profile_bt_size_cur - 1, uc, NULL); + } + else if (forceDwarf == -1) { + jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); + } + + forceDwarf = -2; +#else + profile_bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)profile_bt_data_prof + profile_bt_size_cur, profile_bt_size_max - profile_bt_size_cur - 1, uc, NULL); +#endif + jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + + // store threadid but add 1 as 0 is preserved to indicate end of block + profile_bt_data_prof[profile_bt_size_cur++].uintptr = ptls->tid + 1; + + // store task id (never null) + profile_bt_data_prof[profile_bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); + + // store cpu cycle clock + profile_bt_data_prof[profile_bt_size_cur++].uintptr = cycleclock(); + + // store whether thread is sleeping (don't ever encode a state as `0` since is preserved to indicate end of block) + int state = jl_atomic_load_relaxed(&ptls->sleep_check_state) == 0 ? PROFILE_STATE_THREAD_NOT_SLEEPING : PROFILE_STATE_THREAD_SLEEPING; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = state; + + // Mark the end of this block with two 0's + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + } + // We're done! Resume the thread. + jl_thread_resume(tid); +} + void *mach_profile_listener(void *arg) { (void)arg; @@ -741,88 +819,21 @@ void *mach_profile_listener(void *arg) // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) int keymgr_locked = jl_lock_profile_mach(0); - int nthreads = jl_atomic_load_acquire(&jl_n_threads); - int *randperm = profile_get_randperm(nthreads); - for (int idx = nthreads; idx-- > 0; ) { - // Stop the threads in the random or reverse round-robin order. - int i = randperm[idx]; - // if there is no space left, break early - if (jl_profile_is_buffer_full()) { - jl_profile_stop_timer(); - break; - } - - if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) - _dyld_dlopen_atfork_prepare(); - if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) - _dyld_atfork_prepare(); // briefly acquire the dlsym lock - host_thread_state_t state; - int valid_thread = jl_thread_suspend_and_get_state2(i, &state); - unw_context_t *uc = (unw_context_t*)&state; - if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) - _dyld_atfork_parent(); // quickly release the dlsym lock - if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) - _dyld_dlopen_atfork_parent(); - if (!valid_thread) - continue; - if (running) { -#ifdef LLVMLIBUNWIND - /* - * Unfortunately compact unwind info is incorrectly generated for quite a number of - * libraries by quite a large number of compilers. We can fall back to DWARF unwind info - * in some cases, but in quite a number of cases (especially libraries not compiled in debug - * mode, only the compact unwind info may be available). Even more unfortunately, there is no - * way to detect such bogus compact unwind info (other than noticing the resulting segfault). - * What we do here is ugly, but necessary until the compact unwind info situation improves. - * We try to use the compact unwind info and if that results in a segfault, we retry with DWARF info. - * Note that in a small number of cases this may result in bogus stack traces, but at least the topmost - * entry will always be correct, and the number of cases in which this is an issue is rather small. - * Other than that, this implementation is not incorrect as the other thread is paused while we are profiling - * and during stack unwinding we only ever read memory, but never write it. - */ - - forceDwarf = 0; - unw_getcontext(&profiler_uc); // will resume from this point if the next lines segfault at any point - - if (forceDwarf == 0) { - // Save the backtrace - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, NULL); - } - else if (forceDwarf == 1) { - bt_size_cur += rec_backtrace_ctx_dwarf((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, NULL); - } - else if (forceDwarf == -1) { - jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); - } - - forceDwarf = -2; -#else - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, NULL); -#endif - jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[i]; - - // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; - - // META_OFFSET_TASKID store task id (never null) - bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); - - // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock - bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - - // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; - - // Mark the end of this block with two 0's - bt_data_prof[bt_size_cur++].uintptr = 0; - bt_data_prof[bt_size_cur++].uintptr = 0; + if (profile_all_tasks) { + // Don't take the stackwalk lock here since it's already taken in `jl_rec_backtrace` + jl_profile_task(); + } + else { + int *randperm = profile_get_randperm(nthreads); + for (int idx = nthreads; idx-- > 0; ) { + // Stop the threads in random order. + int i = randperm[idx]; + jl_profile_thread_mach(i); } - // We're done! Resume the thread. - jl_thread_resume(i); } jl_unlock_profile_mach(0, keymgr_locked); - if (running) { + if (profile_running) { jl_check_profile_autostop(); // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); @@ -831,7 +842,8 @@ void *mach_profile_listener(void *arg) } } -JL_DLLEXPORT int jl_profile_start_timer(void) + +JL_DLLEXPORT int jl_profile_start_timer(uint8_t all_tasks) { kern_return_t ret; if (!profile_started) { @@ -860,7 +872,8 @@ JL_DLLEXPORT int jl_profile_start_timer(void) timerprof.tv_sec = nsecprof/GIGA; timerprof.tv_nsec = nsecprof%GIGA; - running = 1; + profile_running = 1; + profile_all_tasks = all_tasks; // ensure the alarm is running ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); HANDLE_MACH_ERROR("clock_alarm", ret); @@ -870,5 +883,8 @@ JL_DLLEXPORT int jl_profile_start_timer(void) JL_DLLEXPORT void jl_profile_stop_timer(void) { - running = 0; + uv_mutex_lock(&bt_data_prof_lock); + profile_running = 0; + profile_all_tasks = 0; + uv_mutex_unlock(&bt_data_prof_lock); } diff --git a/src/signals-unix.c b/src/signals-unix.c index caf0e977929c5..301b875018c1c 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -9,6 +9,10 @@ #include #include #include + +#include "julia.h" +#include "julia_internal.h" + #if defined(_OS_DARWIN_) && !defined(MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON #endif @@ -646,7 +650,7 @@ int timer_graceperiod_elapsed(void) static timer_t timerprof; static struct itimerspec itsprof; -JL_DLLEXPORT int jl_profile_start_timer(void) +JL_DLLEXPORT int jl_profile_start_timer(uint8_t all_tasks) { struct sigevent sigprof; @@ -655,10 +659,12 @@ JL_DLLEXPORT int jl_profile_start_timer(void) sigprof.sigev_notify = SIGEV_SIGNAL; sigprof.sigev_signo = SIGUSR1; sigprof.sigev_value.sival_ptr = &timerprof; - // Because SIGUSR1 is multipurpose, set `running` before so that we know that the first SIGUSR1 came from the timer - running = 1; + // Because SIGUSR1 is multipurpose, set `profile_running` before so that we know that the first SIGUSR1 came from the timer + profile_running = 1; + profile_all_tasks = all_tasks; if (timer_create(CLOCK_REALTIME, &sigprof, &timerprof) == -1) { - running = 0; + profile_running = 0; + profile_all_tasks = 0; return -2; } @@ -668,7 +674,8 @@ JL_DLLEXPORT int jl_profile_start_timer(void) itsprof.it_value.tv_sec = nsecprof / GIGA; itsprof.it_value.tv_nsec = nsecprof % GIGA; if (timer_settime(timerprof, 0, &itsprof, NULL) == -1) { - running = 0; + profile_running = 0; + profile_all_tasks = 0; return -3; } return 0; @@ -676,11 +683,13 @@ JL_DLLEXPORT int jl_profile_start_timer(void) JL_DLLEXPORT void jl_profile_stop_timer(void) { - if (running) { + uv_mutex_lock(&bt_data_prof_lock); + if (profile_running) { timer_delete(timerprof); last_timer_delete_time = jl_hrtime(); - running = 0; + profile_running = 0; } + uv_mutex_unlock(&bt_data_prof_lock); } #elif defined(__OpenBSD__) @@ -797,7 +806,7 @@ void trigger_profile_peek(void) jl_safe_printf("\n======================================================================================\n"); jl_safe_printf("Information request received. A stacktrace will print followed by a %.1f second profile\n", profile_peek_duration); jl_safe_printf("======================================================================================\n"); - if (bt_size_max == 0){ + if (profile_bt_size_max == 0){ // If the buffer hasn't been initialized, initialize with default size // Keep these values synchronized with Profile.default_init() if (jl_profile_init(10000000, 1000000) == -1) { @@ -805,13 +814,62 @@ void trigger_profile_peek(void) return; } } - bt_size_cur = 0; // clear profile buffer - if (jl_profile_start_timer() < 0) + profile_bt_size_cur = 0; // clear profile buffer + if (jl_profile_start_timer(0) < 0) jl_safe_printf("ERROR: Could not start profile timer\n"); else profile_autostop_time = jl_hrtime() + (profile_peek_duration * 1e9); } +// assumes holding `jl_lock_stackwalk` +void jl_profile_thread_unix(int tid, bt_context_t *signal_context) +{ + if (jl_profile_is_buffer_full()) { + // Buffer full: Delete the timer + jl_profile_stop_timer(); + return; + } + // notify thread to stop + if (!jl_thread_suspend_and_get_state(tid, 1, signal_context)) + return; + // unwinding can fail, so keep track of the current state + // and restore from the SEGV handler if anything happens. + jl_jmp_buf *old_buf = jl_get_safe_restore(); + jl_jmp_buf buf; + + jl_set_safe_restore(&buf); + if (jl_setjmp(buf, 0)) { + jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); + } else { + // Get backtrace data + profile_bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)profile_bt_data_prof + profile_bt_size_cur, + profile_bt_size_max - profile_bt_size_cur - 1, signal_context, NULL); + } + jl_set_safe_restore(old_buf); + + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + + // store threadid but add 1 as 0 is preserved to indicate end of block + profile_bt_data_prof[profile_bt_size_cur++].uintptr = ptls2->tid + 1; + + // store task id (never null) + profile_bt_data_prof[profile_bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls2->current_task); + + // store cpu cycle clock + profile_bt_data_prof[profile_bt_size_cur++].uintptr = cycleclock(); + + // store whether thread is sleeping (don't ever encode a state as `0` since is preserved to indicate end of block) + int state = jl_atomic_load_relaxed(&ptls2->sleep_check_state) == 0 ? PROFILE_STATE_THREAD_NOT_SLEEPING : PROFILE_STATE_THREAD_SLEEPING; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = state; + + // Mark the end of this block with two 0's + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + + // notify thread to resume + jl_thread_resume(tid); +} + static void *signal_listener(void *arg) { static jl_bt_element_t bt_data[JL_MAX_BT_SIZE + 1]; @@ -911,13 +969,13 @@ static void *signal_listener(void *arg) int doexit = critical; #ifdef SIGINFO if (sig == SIGINFO) { - if (running != 1) + if (profile_running != 1) trigger_profile_peek(); doexit = 0; } #else if (sig == SIGUSR1) { - if (running != 1 && timer_graceperiod_elapsed()) + if (profile_running != 1 && timer_graceperiod_elapsed()) trigger_profile_peek(); doexit = 0; } @@ -951,78 +1009,46 @@ static void *signal_listener(void *arg) bt_size = 0; #if !defined(JL_DISABLE_LIBUNWIND) bt_context_t signal_context; - // sample each thread, round-robin style in reverse order - // (so that thread zero gets notified last) - if (critical || profile) { + if (critical) { int lockret = jl_lock_stackwalk(); - int *randperm; - if (profile) - randperm = profile_get_randperm(nthreads); - for (int idx = nthreads; idx-- > 0; ) { - // Stop the threads in the random or reverse round-robin order. - int i = profile ? randperm[idx] : idx; + // sample each thread, round-robin style in reverse order + // (so that thread zero gets notified last) + for (int i = nthreads; i-- > 0; ) { // notify thread to stop if (!jl_thread_suspend_and_get_state(i, 1, &signal_context)) continue; // do backtrace on thread contexts for critical signals // this part must be signal-handler safe - if (critical) { - bt_size += rec_backtrace_ctx(bt_data + bt_size, - JL_MAX_BT_SIZE / nthreads - 1, - &signal_context, NULL); - bt_data[bt_size++].uintptr = 0; - } - - // do backtrace for profiler - if (profile && running) { - if (jl_profile_is_buffer_full()) { - // Buffer full: Delete the timer - jl_profile_stop_timer(); - } - else { - // unwinding can fail, so keep track of the current state - // and restore from the SEGV handler if anything happens. - jl_jmp_buf *old_buf = jl_get_safe_restore(); - jl_jmp_buf buf; - - jl_set_safe_restore(&buf); - if (jl_setjmp(buf, 0)) { - jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); - } else { - // Get backtrace data - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, - bt_size_max - bt_size_cur - 1, &signal_context, NULL); - } - jl_set_safe_restore(old_buf); - - jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[i]; - - // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = ptls2->tid + 1; - - // META_OFFSET_TASKID store task id (never null) - bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls2->current_task); - - // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock - bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - - // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls2->sleep_check_state) + 1; - - // Mark the end of this block with two 0's - bt_data_prof[bt_size_cur++].uintptr = 0; - bt_data_prof[bt_size_cur++].uintptr = 0; - } - } - - // notify thread to resume + bt_size += rec_backtrace_ctx(bt_data + bt_size, + JL_MAX_BT_SIZE / nthreads - 1, + &signal_context, NULL); + bt_data[bt_size++].uintptr = 0; jl_thread_resume(i); } jl_unlock_stackwalk(lockret); } + else if (profile) { + if (profile_all_tasks) { + // Don't take the stackwalk lock here since it's already taken in `jl_rec_backtrace` + jl_profile_task(); + } + else { + int lockret = jl_lock_stackwalk(); + int *randperm = profile_get_randperm(nthreads); + for (int idx = nthreads; idx-- > 0; ) { + // Stop the threads in the random order. + int i = randperm[idx]; + // do backtrace for profiler + if (profile_running) { + jl_profile_thread_unix(i, &signal_context); + } + } + jl_unlock_stackwalk(lockret); + } + } #ifndef HAVE_MACH - if (profile && running) { + if (profile_running) { jl_check_profile_autostop(); #if defined(HAVE_TIMER) timer_settime(timerprof, 0, &itsprof, NULL); diff --git a/src/signals-win.c b/src/signals-win.c index b5f8dd8bd79d9..2a594bc92b9b7 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -404,12 +404,16 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) while (1) { DWORD timeout_ms = nsecprof / (GIGA / 1000); Sleep(timeout_ms > 0 ? timeout_ms : 1); - if (running) { + if (profile_running) { if (jl_profile_is_buffer_full()) { jl_profile_stop_timer(); // does not change the thread state SuspendThread(GetCurrentThread()); continue; } + else if (profile_all_tasks) { + // Don't take the stackwalk lock here since it's already taken in `jl_rec_backtrace` + jl_profile_task(); + } else { // TODO: bring this up to parity with other OS by adding loop over tid here int lockret = jl_lock_stackwalk(); @@ -421,26 +425,27 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) break; } // Get backtrace data - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, - bt_size_max - bt_size_cur - 1, &ctxThread, NULL); + profile_bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)profile_bt_data_prof + profile_bt_size_cur, + profile_bt_size_max - profile_bt_size_cur - 1, &ctxThread, NULL); jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; // given only profiling hMainThread // META_OFFSET_THREADID store threadid but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = ptls->tid + 1; // META_OFFSET_TASKID store task id (never null) - bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); + profile_bt_data_prof[profile_bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); // META_OFFSET_CPUCYCLECLOCK store cpu cycle clock - bt_data_prof[bt_size_cur++].uintptr = cycleclock(); + profile_bt_data_prof[profile_bt_size_cur++].uintptr = cycleclock(); - // META_OFFSET_SLEEPSTATE store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; + // store whether thread is sleeping (don't ever encode a state as `0` since is preserved to indicate end of block) + int state = jl_atomic_load_relaxed(&ptls->sleep_check_state) == 0 ? PROFILE_STATE_THREAD_NOT_SLEEPING : PROFILE_STATE_THREAD_SLEEPING; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = state; // Mark the end of this block with two 0's - bt_data_prof[bt_size_cur++].uintptr = 0; - bt_data_prof[bt_size_cur++].uintptr = 0; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; + profile_bt_data_prof[profile_bt_size_cur++].uintptr = 0; jl_unlock_stackwalk(lockret); jl_thread_resume(0); jl_check_profile_autostop(); @@ -455,7 +460,7 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) static volatile TIMECAPS timecaps; -JL_DLLEXPORT int jl_profile_start_timer(void) +JL_DLLEXPORT int jl_profile_start_timer(uint8_t all_tasks) { if (hBtThread == NULL) { @@ -483,20 +488,24 @@ JL_DLLEXPORT int jl_profile_start_timer(void) return -2; } } - if (running == 0) { + if (profile_running == 0) { // Failure to change the timer resolution is not fatal. However, it is important to // ensure that the timeBeginPeriod/timeEndPeriod is paired. if (TIMERR_NOERROR != timeBeginPeriod(timecaps.wPeriodMin)) timecaps.wPeriodMin = 0; } - running = 1; // set `running` finally + profile_all_tasks = all_tasks; + profile_running = 1; // set `profile_running` finally return 0; } JL_DLLEXPORT void jl_profile_stop_timer(void) { - if (running && timecaps.wPeriodMin) + uv_mutex_lock(&bt_data_prof_lock); + if (profile_running && timecaps.wPeriodMin) timeEndPeriod(timecaps.wPeriodMin); - running = 0; + profile_running = 0; + profile_all_tasks = 0; + uv_mutex_unlock(&bt_data_prof_lock); } void jl_install_default_signal_handlers(void) diff --git a/src/stackwalk.c b/src/stackwalk.c index 770daa8bf17a6..251e408c7fd2d 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -208,7 +208,7 @@ NOINLINE size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, // // The first `skip` frames are omitted, in addition to omitting the frame from // `rec_backtrace` itself. -NOINLINE size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) +NOINLINE size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT { bt_context_t context; memset(&context, 0, sizeof(context)); @@ -224,6 +224,24 @@ NOINLINE size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip return bt_size; } +NOINLINE int failed_to_sample_task_fun(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT +{ + if (maxsize < 1) { + return 0; + } + bt_data[0].uintptr = (uintptr_t) &failed_to_sample_task_fun; + return 1; +} + +NOINLINE int failed_to_stop_thread_fun(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT +{ + if (maxsize < 1) { + return 0; + } + bt_data[0].uintptr = (uintptr_t) &failed_to_stop_thread_fun; + return 1; +} + static jl_value_t *array_ptr_void_type JL_ALWAYS_LEAFTYPE = NULL; // Return backtrace information as an svec of (bt1, bt2, [sp]) // @@ -1225,24 +1243,34 @@ return 0; #endif } -JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, size_t max_bt_size) JL_NOTSAFEPOINT +JL_DLLEXPORT jl_record_backtrace_result_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, size_t max_bt_size, int all_tasks_profiler) JL_NOTSAFEPOINT { - jl_task_t *ct = jl_current_task; - jl_ptls_t ptls = ct->ptls; + int16_t tid = INT16_MAX; + jl_record_backtrace_result_t result = {0, tid}; + jl_task_t *ct = NULL; + jl_ptls_t ptls = NULL; + if (!all_tasks_profiler) { + ct = jl_current_task; + ptls = ct->ptls; + ptls->bt_size = 0; + tid = ptls->tid; + } if (t == ct) { - return rec_backtrace(bt_data, max_bt_size, 0); + result.bt_size = rec_backtrace(bt_data, max_bt_size, 0); + result.tid = tid; + return result; } bt_context_t *context = NULL; bt_context_t c; int16_t old; - for (old = -1; !jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid; old = -1) { + for (old = -1; !jl_atomic_cmpswap(&t->tid, &old, tid) && old != tid; old = -1) { int lockret = jl_lock_stackwalk(); // if this task is already running somewhere, we need to stop the thread it is running on and query its state if (!jl_thread_suspend_and_get_state(old, 1, &c)) { jl_unlock_stackwalk(lockret); if (jl_atomic_load_relaxed(&t->tid) != old) continue; - return 0; + return result; } jl_unlock_stackwalk(lockret); if (jl_atomic_load_relaxed(&t->tid) == old) { @@ -1277,13 +1305,16 @@ JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, #endif } size_t bt_size = 0; - if (context) - bt_size = rec_backtrace_ctx(bt_data, max_bt_size, context, t->gcstack); + if (context) { + bt_size = rec_backtrace_ctx(bt_data, max_bt_size, context, all_tasks_profiler ? NULL : t->gcstack); + } if (old == -1) jl_atomic_store_relaxed(&t->tid, old); - else if (old != ptls->tid) + else if (old != tid) jl_thread_resume(old); - return bt_size; + result.bt_size = bt_size; + result.tid = old; + return result; } //-------------------------------------------------- @@ -1317,7 +1348,8 @@ JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT jl_ptls_t ptls = ct->ptls; ptls->bt_size = 0; jl_bt_element_t *bt_data = ptls->bt_data; - size_t bt_size = jl_record_backtrace(t, bt_data, JL_MAX_BT_SIZE); + jl_record_backtrace_result_t r = jl_record_backtrace(t, bt_data, JL_MAX_BT_SIZE, 0); + size_t bt_size = r.bt_size; size_t i; for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { jl_print_bt_entry_codeloc(bt_data + i); @@ -1331,8 +1363,6 @@ JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT jlbacktrace(); } -extern int gc_first_tid; - // Print backtraces for all live tasks, for all threads, to jl_safe_printf stderr JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT { diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index b753c9ca88f24..bea8f288937d0 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -23,7 +23,7 @@ Profiling support. module Profile global print -export @profile +export @profile, @profile_walltime public clear, print, fetch, @@ -63,6 +63,28 @@ macro profile(ex) end end +""" + @profile_walltime + +`@profile_walltime ` runs your expression while taking periodic backtraces of a sample of all live tasks (both running and not running). +These are appended to an internal buffer of backtraces. + +It can be configured via `Profile.init`, same as the `Profile.@profile`, and that you can't use `@profile` simultaneously with `@profile_walltime`. + +As mentioned above, since this tool sample not only running tasks, but also sleeping tasks and tasks performing IO, +it can be used to diagnose performance issues such as lock contention, IO bottlenecks, and other issues that are not visible in the CPU profile. +""" +macro profile_walltime(ex) + return quote + try + start_timer(true) + $(esc(ex)) + finally + stop_timer() + end + end +end + # An internal function called to show the report after an information request (SIGINFO or SIGUSR1). function _peek_report() iob = Base.AnnotatedIOBuffer() @@ -403,9 +425,10 @@ end function has_meta(data) for i in 6:length(data) - data[i] == 0 || continue # first block end null - data[i - 1] == 0 || continue # second block end null - data[i - META_OFFSET_SLEEPSTATE] in 1:2 || continue + data[i] == 0 || continue # first block end null + data[i - 1] == 0 || continue # second block end null + data[i - META_OFFSET_SLEEPSTATE] in 1:3 || continue # 1 for not sleeping, 2 for sleeping, 3 for task profiler fake state + # See definition in `src/julia_internal.h` data[i - META_OFFSET_CPUCYCLECLOCK] != 0 || continue data[i - META_OFFSET_TASKID] != 0 || continue data[i - META_OFFSET_THREADID] != 0 || continue @@ -608,9 +631,9 @@ Julia, and examine the resulting `*.mem` files. clear_malloc_data() = ccall(:jl_clear_malloc_data, Cvoid, ()) # C wrappers -function start_timer() +function start_timer(all_tasks::Bool=false) check_init() # if the profile buffer hasn't been initialized, initialize with default size - status = ccall(:jl_profile_start_timer, Cint, ()) + status = ccall(:jl_profile_start_timer, Cint, (Bool,), all_tasks) if status < 0 error(error_codes[status]) end @@ -722,12 +745,16 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict, startframe = length(data) skip = false nsleeping = 0 + is_task_profile = false for i in startframe:-1:1 (startframe - 1) >= i >= (startframe - (nmeta + 1)) && continue # skip metadata (its read ahead below) and extra block end NULL IP ip = data[i] if is_block_end(data, i) # read metadata - thread_sleeping = data[i - META_OFFSET_SLEEPSTATE] - 1 # subtract 1 as state is incremented to avoid being equal to 0 + thread_sleeping_state = data[i - META_OFFSET_SLEEPSTATE] - 1 # subtract 1 as state is incremented to avoid being equal to 0 + if thread_sleeping_state == 2 + is_task_profile = true + end # cpu_cycle_clock = data[i - META_OFFSET_CPUCYCLECLOCK] taskid = data[i - META_OFFSET_TASKID] threadid = data[i - META_OFFSET_THREADID] @@ -735,7 +762,7 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict, skip = true continue end - if thread_sleeping == 1 + if thread_sleeping_state == 1 nsleeping += 1 end skip = false @@ -769,14 +796,14 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict, end end @assert length(lilist) == length(n) == length(m) == length(lilist_idx) - return (lilist, n, m, totalshots, nsleeping) + return (lilist, n, m, totalshots, nsleeping, is_task_profile) end const FileNameMap = Dict{Symbol,Tuple{String,String,String}} function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfoFlatDict}, cols::Int, fmt::ProfileFormat, threads::Union{Int,AbstractVector{Int}}, tasks::Union{UInt,AbstractVector{UInt}}, is_subsection::Bool) - lilist, n, m, totalshots, nsleeping = parse_flat(fmt.combine ? StackFrame : UInt64, data, lidict, fmt.C, threads, tasks) + lilist, n, m, totalshots, nsleeping, is_task_profile = parse_flat(fmt.combine ? StackFrame : UInt64, data, lidict, fmt.C, threads, tasks) if false # optional: drop the "non-interpretable" ones keep = map(frame -> frame != UNKNOWN && frame.line != 0, lilist) lilist = lilist[keep] @@ -796,11 +823,15 @@ function flat(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoDict, LineInfo return true end is_subsection || print_flat(io, lilist, n, m, cols, filenamemap, fmt) - Base.print(io, "Total snapshots: ", totalshots, ". Utilization: ", round(Int, util_perc), "%") + if is_task_profile + Base.print(io, "Total snapshots: ", totalshots, "\n") + else + Base.print(io, "Total snapshots: ", totalshots, ". Utilization: ", round(Int, util_perc), "%") + end if is_subsection println(io) print_flat(io, lilist, n, m, cols, filenamemap, fmt) - else + elseif !is_task_profile Base.print(io, " across all threads and tasks. Use the `groupby` kwarg to break down by thread and/or task.\n") end return false @@ -1034,12 +1065,16 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI startframe = length(all) skip = false nsleeping = 0 + is_task_profile = false for i in startframe:-1:1 (startframe - 1) >= i >= (startframe - (nmeta + 1)) && continue # skip metadata (it's read ahead below) and extra block end NULL IP ip = all[i] if is_block_end(all, i) # read metadata - thread_sleeping = all[i - META_OFFSET_SLEEPSTATE] - 1 # subtract 1 as state is incremented to avoid being equal to 0 + thread_sleeping_state = all[i - META_OFFSET_SLEEPSTATE] - 1 # subtract 1 as state is incremented to avoid being equal to 0 + if thread_sleeping_state == 2 + is_task_profile = true + end # cpu_cycle_clock = all[i - META_OFFSET_CPUCYCLECLOCK] taskid = all[i - META_OFFSET_TASKID] threadid = all[i - META_OFFSET_THREADID] @@ -1048,7 +1083,7 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI skip = true continue end - if thread_sleeping == 1 + if thread_sleeping_state == 1 nsleeping += 1 end skip = false @@ -1154,7 +1189,7 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI nothing end cleanup!(root) - return root, nsleeping + return root, nsleeping, is_task_profile end function maxstats(root::StackFrameTree) @@ -1223,9 +1258,9 @@ end function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, LineInfoDict}, cols::Int, fmt::ProfileFormat, threads::Union{Int,AbstractVector{Int}}, tasks::Union{UInt,AbstractVector{UInt}}, is_subsection::Bool) if fmt.combine - root, nsleeping = tree!(StackFrameTree{StackFrame}(), data, lidict, fmt.C, fmt.recur, threads, tasks) + root, nsleeping, is_task_profile = tree!(StackFrameTree{StackFrame}(), data, lidict, fmt.C, fmt.recur, threads, tasks) else - root, nsleeping = tree!(StackFrameTree{UInt64}(), data, lidict, fmt.C, fmt.recur, threads, tasks) + root, nsleeping, is_task_profile = tree!(StackFrameTree{UInt64}(), data, lidict, fmt.C, fmt.recur, threads, tasks) end util_perc = (1 - (nsleeping / root.count)) * 100 is_subsection || print_tree(io, root, cols, fmt, is_subsection) @@ -1239,11 +1274,15 @@ function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, Line end return true end - Base.print(io, "Total snapshots: ", root.count, ". Utilization: ", round(Int, util_perc), "%") + if is_task_profile + Base.print(io, "Total snapshots: ", root.count, "\n") + else + Base.print(io, "Total snapshots: ", root.count, ". Utilization: ", round(Int, util_perc), "%") + end if is_subsection Base.println(io) print_tree(io, root, cols, fmt, is_subsection) - else + elseif !is_task_profile Base.print(io, " across all threads and tasks. Use the `groupby` kwarg to break down by thread and/or task.\n") end return false diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 1769cbd12da3e..352d07086f25b 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -25,19 +25,64 @@ end end end -busywait(0, 0) # compile -@profile busywait(1, 20) +@noinline function sleeping_tasks(ch::Channel) + for _ in 1:100 + Threads.@spawn take!(ch) + end + sleep(10) +end -let r = Profile.retrieve() - mktemp() do path, io - serialize(io, r) - close(io) - open(path) do io - @test isa(deserialize(io), Tuple{Vector{UInt},Dict{UInt64,Vector{Base.StackTraces.StackFrame}}}) +function test_profile() + let r = Profile.retrieve() + mktemp() do path, io + serialize(io, r) + close(io) + open(path) do io + @test isa(deserialize(io), Tuple{Vector{UInt},Dict{UInt64,Vector{Base.StackTraces.StackFrame}}}) + end + end + end +end + +function test_has_task_profiler_sample_in_buffer() + let r = Profile.retrieve() + mktemp() do path, io + serialize(io, r) + close(io) + open(path) do io + all = deserialize(io) + data = all[1] + startframe = length(data) + for i in startframe:-1:1 + (startframe - 1) >= i >= (startframe - (Profile.nmeta + 1)) && continue # skip metadata (its read ahead below) and extra block end NULL IP + if Profile.is_block_end(data, i) + thread_sleeping_state = data[i - Profile.META_OFFSET_SLEEPSTATE] + @test thread_sleeping_state == 0x3 + end + end + end end end end +busywait(0, 0) # compile + +@profile_walltime busywait(1, 20) +test_profile() + +Profile.clear() + +ch = Channel(1) +@profile_walltime sleeping_tasks(ch) +test_profile() +close(ch) +test_has_task_profiler_sample_in_buffer() + +Profile.clear() + +@profile busywait(1, 20) +test_profile() + # test printing options for options in ((format=:tree, C=true), (format=:tree, maxdepth=2), From ee09ae70d9f4a04ed8b745f36d3c5d9d578d2887 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 26 Oct 2024 03:01:53 +0200 Subject: [PATCH 508/548] recommend explicit `using Foo: Foo, ...` in package code (was: "using considered harmful") (#42080) I feel we are heading up against a "`using` crisis" where any new feature that is implemented by exporting a new name (either in Base or a package) becomes a breaking change. This is already happening (https://github.com/JuliaGPU/CUDA.jl/pull/1097, https://github.com/JuliaWeb/HTTP.jl/pull/745) and as projects get bigger and more names are exported, the likelihood of this rapidly increases. The flaw in `using Foo` is fundamental in that you cannot lexically see where a name comes from so when two packages export the same name, you are screwed. Any code that relies on `using Foo` and then using an exported name from `Foo` is vulnerable to another dependency exporting the same name. Therefore, I think we should start to strongly discourage the use of `using Foo` and only recommend `using Foo` for ephemeral work (e.g. REPL work). --------- Co-authored-by: Dilum Aluthge Co-authored-by: Mason Protter Co-authored-by: Max Horn Co-authored-by: Matt Bauman Co-authored-by: Alex Arslan Co-authored-by: Ian Butterworth Co-authored-by: Neven Sajko --- base/docs/basedocs.jl | 8 ++++++++ doc/src/manual/modules.md | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index b080bf51e5e98..7441f5b993bf4 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -37,6 +37,14 @@ kw"help", kw"Julia", kw"julia", kw"" available for direct use. Names can also be used via dot syntax (e.g. `Foo.foo` to access the name `foo`), whether they are `export`ed or not. See the [manual section about modules](@ref modules) for details. + +!!! note + When two or more packages/modules export a name and that name does not refer to the + same thing in each of the packages, and the packages are loaded via `using` without + an explicit list of names, it is an error to reference that name without qualification. + It is thus recommended that code intended to be forward-compatible with future versions + of its dependencies and of Julia, e.g., code in released packages, list the names it + uses from each loaded package, e.g., `using Foo: Foo, f` rather than `using Foo`. """ kw"using" diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index b4f0fd78c816a..cf24474916bef 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -116,7 +116,7 @@ VERSION >= v"1.11.0-DEV.469" && eval(Meta.parse("public a, b, c")) ### Standalone `using` and `import` -Possibly the most common way of loading a module is `using ModuleName`. This [loads](@ref +For interactive use, the most common way of loading a module is `using ModuleName`. This [loads](@ref code-loading) the code associated with `ModuleName`, and brings 1. the module name @@ -172,6 +172,13 @@ Importantly, the module name `NiceStuff` will *not* be in the namespace. If you julia> using .NiceStuff: nice, DOG, NiceStuff ``` +When two or more packages/modules export a name and that name does not refer to the +same thing in each of the packages, and the packages are loaded via `using` without +an explicit list of names, it is an error to reference that name without qualification. +It is thus recommended that code intended to be forward-compatible with future versions +of its dependencies and of Julia, e.g., code in released packages, list the names it +uses from each loaded package, e.g., `using Foo: Foo, f` rather than `using Foo`. + Julia has two forms for seemingly the same thing because only `import ModuleName: f` allows adding methods to `f` *without a module path*. That is to say, the following example will give an error: From 446d20fb001fdf304cbe506d5fe0cfc9a7b88178 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 27 Oct 2024 07:46:08 +0530 Subject: [PATCH 509/548] Change some hardcoded loop ranges to axes in dense linalg functions (#56348) These should be safer in general, and are also easier to reason about. --- stdlib/LinearAlgebra/src/dense.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index d8f2513f5bfc8..19fc7e9d422a8 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -18,14 +18,14 @@ function isone(A::AbstractMatrix) m, n = size(A) m != n && return false # only square matrices can satisfy x == one(x) if sizeof(A) < ISONE_CUTOFF - _isone_triacheck(A, m) + _isone_triacheck(A) else - _isone_cachefriendly(A, m) + _isone_cachefriendly(A) end end -@inline function _isone_triacheck(A::AbstractMatrix, m::Int) - @inbounds for i in 1:m, j in i:m +@inline function _isone_triacheck(A::AbstractMatrix) + @inbounds for i in axes(A,2), j in axes(A,1) if i == j isone(A[i,i]) || return false else @@ -36,8 +36,8 @@ end end # Inner loop over rows to be friendly to the CPU cache -@inline function _isone_cachefriendly(A::AbstractMatrix, m::Int) - @inbounds for i in 1:m, j in 1:m +@inline function _isone_cachefriendly(A::AbstractMatrix) + @inbounds for i in axes(A,2), j in axes(A,1) if i == j isone(A[i,i]) || return false else @@ -198,7 +198,7 @@ function fillband!(A::AbstractMatrix{T}, x, l, u) where T require_one_based_indexing(A) m, n = size(A) xT = convert(T, x) - for j in 1:n + for j in axes(A,2) for i in max(1,j-u):min(m,j-l) @inbounds A[i, j] = xT end @@ -553,7 +553,7 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T if isdiag(A) TT = promote_op(^, T, typeof(p)) retmat = copymutable_oftype(A, TT) - for i in 1:n + for i in axes(retmat,1) retmat[i, i] = retmat[i, i] ^ p end return retmat @@ -792,10 +792,10 @@ end ## Swap rows i and j and columns i and j in X function rcswap!(i::Integer, j::Integer, X::AbstractMatrix{<:Number}) - for k = 1:size(X,1) + for k = axes(X,1) X[k,i], X[k,j] = X[k,j], X[k,i] end - for k = 1:size(X,2) + for k = axes(X,2) X[i,k], X[j,k] = X[j,k], X[i,k] end end From fcf7ec081509c62967122e0949640e63a3d07571 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 27 Oct 2024 07:59:28 +0530 Subject: [PATCH 510/548] Make `LinearAlgebra.haszero` public (#56223) The trait `haszero` is used to check if a type `T` has a unique zero defined using `zero(T)`. This lets us dispatch to optimized paths without losing generality. This PR makes the function public so that this may be extended by packages (such as `StaticArrays`). --- NEWS.md | 2 ++ stdlib/LinearAlgebra/docs/src/index.md | 1 + stdlib/LinearAlgebra/src/LinearAlgebra.jl | 1 + stdlib/LinearAlgebra/src/dense.jl | 12 ++++++++++++ 4 files changed, 16 insertions(+) diff --git a/NEWS.md b/NEWS.md index 079625b1610aa..5e066ffd9cdcf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -150,6 +150,8 @@ Standard library changes Custom array types may specialize this function to return an appropriate result ([#55252]). * The matrix multiplication `A * B` calls `matprod_dest(A, B, T::Type)` to generate the destination. This function is now public ([#55537]). +* The function `haszero(T::Type)` is used to check if a type `T` has a unique zero element defined as `zero(T)`. + This is now public. #### Logging diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 1e44bf5cb04d7..3e18a45752aeb 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -895,6 +895,7 @@ LinearAlgebra.LAPACK.hseqr! ```@docs LinearAlgebra.matprod_dest +LinearAlgebra.haszero ``` ```@meta diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 15354603943c2..330df503485cb 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -169,6 +169,7 @@ export public AbstractTriangular, Givens, checksquare, + haszero, hermitian, hermitian_type, isbanded, diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 19fc7e9d422a8..b8d5c84c3db53 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -108,6 +108,18 @@ norm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} = length(x) < NRM2_CUTOFF ? generic_norm2(x) : BLAS.nrm2(x) # Conservative assessment of types that have zero(T) defined for themselves +""" + haszero(T::Type) + +Return whether a type `T` has a unique zero element defined using `zero(T)`. +If a type `M` specializes `zero(M)`, it may also choose to set `haszero(M)` to `true`. +By default, `haszero` is assumed to be `false`, in which case the zero elements +are deduced from values rather than the type. + +!!! note + `haszero` is a conservative check that is used to dispatch to + optimized paths. Extending it is optional, but encouraged. +""" haszero(::Type) = false haszero(::Type{T}) where {T<:Number} = isconcretetype(T) haszero(::Type{Union{Missing,T}}) where {T<:Number} = haszero(T) From 87ecf8f820f30aec981ffcf4c24668bd5858e901 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:02:42 -0300 Subject: [PATCH 511/548] remove spurious parens in profiler docs (#56357) --- doc/src/manual/profile.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index 718b79815473e..49b58ba9671c2 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -349,7 +349,7 @@ Our goal is to detect whether there is contention on the `ch` channel—i.e., wh If we run this, we obtain the following [PProf](https://github.com/JuliaPerf/PProf.jl) flame graph: -![CPU Profile](./img/cpu-profile.png)() +![CPU Profile](./img/cpu-profile.png) This profile provides no information to help determine where contention occurs in the system’s synchronization primitives. Waiters on a channel will be blocked and descheduled, meaning no system thread will be running the tasks assigned to those waiters, and as a result, they won't be sampled by the profiler. @@ -402,7 +402,7 @@ Profile.@profile_walltime main() We obtain the following flame graph: -![Wall-time Profile Channel](./img/wall-time-profiler-channel-example.png)() +![Wall-time Profile Channel](./img/wall-time-profiler-channel-example.png) We see that a large number of samples come from channel-related `take!` functions, which allows us to determine that there is indeed an excessive number of waiters in `ch`. @@ -454,7 +454,7 @@ Profile.@profile_walltime main() After collecting a wall-time profile, we get the following flame graph: -![Wall-time Profile Compute-Bound](./img/wall-time-profiler-compute-bound-example.png)() +![Wall-time Profile Compute-Bound](./img/wall-time-profiler-compute-bound-example.png) Notice how many of the samples contain `sum_of_sqrt`, which is the expensive compute function in our example. @@ -507,7 +507,7 @@ Notice that the tasks spawned in `spawn_a_bunch_of_short_lived_tasks` are extrem After collecting a wall-time profile, we obtain the following flame graph: -![Task Sampling Failure](./img/task-sampling-failure.png)() +![Task Sampling Failure](./img/task-sampling-failure.png) The large number of samples from `failed_to_stop_thread_fun` confirms that we have a significant number of short-lived tasks in the system. From 2cdfe062952c3a1168da7545a10bfa0ec205b4db Mon Sep 17 00:00:00 2001 From: Aravindh Krishnamoorthy Date: Mon, 28 Oct 2024 12:32:21 +0100 Subject: [PATCH 512/548] Fix `log_quasitriu` for internal scaling `s=0` (#56311) This PR is a potential fix for #54833. ## Description The function https://github.com/JuliaLang/julia/blob/2a06376c18afd7ec875335070743dcebcd85dee7/stdlib/LinearAlgebra/src/triangular.jl#L2220 computes $\boldsymbol{A}^{\dfrac{1}{2^s}} - \boldsymbol{I}$ for a real-valued $2\times 2$ matrix $\boldsymbol{A}$ using Algorithm 5.1 in [R1]. However, the algorithm in [R1] as well as the above function do not handle the case $s=0.$ This fix extends the function to compute $\boldsymbol{A}^{\dfrac{1}{2^s}} - \boldsymbol{I} \Bigg|_{s=0} = \boldsymbol{A} - \boldsymbol{I}.$ ## Checklist - [X] Fix code: `stdlib\LinearAlgebra\src\triangular.jl` in function `_sqrt_pow_diag_block_2x2!(A, A0, s)`. - [X] Add test case: `stdlib\LinearAlgebra\test\triangular.jl`. - [X] Update `NEWS.md`. - [X] Testing and self review. | Tag | Reference | | --- | --- | | [R1] | Al-Mohy, Awad H. and Higham, Nicholas J. "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm", 2011, url: https://eprints.maths.manchester.ac.uk/1687/1/paper11.pdf | --------- Co-authored-by: Daniel Karrasch Co-authored-by: Oscar Smith --- stdlib/LinearAlgebra/src/triangular.jl | 5 +++++ stdlib/LinearAlgebra/test/triangular.jl | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 1a7d04115c97d..a032041a4116c 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -2218,6 +2218,11 @@ end # SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553 # Algorithm 5.1 Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s) + if iszero(s) + A[1,1] -= 1 + A[2,2] -= 1 + return A + end _sqrt_real_2x2!(A, A0) if isone(s) A[1,1] -= 1 diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 678827ceac720..2c8dd4db7fc2b 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1386,4 +1386,14 @@ end end end +@testset "log_quasitriu with internal scaling s=0 (issue #54833)" begin + M = [0.9949357359852791 -0.015567763143324862 -0.09091193493947397 -0.03994428739762443 0.07338356301650806; + 0.011813655598647289 0.9968988574699793 -0.06204555000202496 0.04694097614450692 0.09028834462782365; + 0.092737943594701 0.059546719185135925 0.9935850721633324 0.025348893985651405 -0.018530261590167685; + 0.0369187299165628 -0.04903571106913449 -0.025962938675946543 0.9977767446862031 0.12901494726320517; + 0.0 0.0 0.0 0.0 1.0] + + @test exp(log(M)) ≈ M +end + end # module TestTriangular From 9dbdeb4fb323dfc78fde96a82bbb8fdefeb61a70 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 28 Oct 2024 12:51:55 -0400 Subject: [PATCH 513/548] loading: clean up more concurrency issues (#56329) Guarantee that `__init__` runs before `using` returns. Could be slightly breaking for people that do crazy things inside `__init__`, but just don't do that. Since extensions then probably load after `__init__` (or at least, run their `__init__` after), this is a partial step towards changing things so that extensions are guaranteed to load if using all of their triggers before the corresponding `using` returns Fixes #55556 --- base/Base.jl | 4 +- base/loading.jl | 135 +++++++++++++----------------- base/methodshow.jl | 12 +-- contrib/generate_precompile.jl | 4 +- stdlib/REPL/src/Pkg_beforeload.jl | 12 +-- 5 files changed, 70 insertions(+), 97 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index bfac74e5d7bab..c5e318ffe5e38 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -657,8 +657,8 @@ function __init__() init_active_project() append!(empty!(_sysimage_modules), keys(loaded_modules)) empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty - for (mod, key) in module_keys - push!(get!(Vector{Module}, loaded_precompiles, key), mod) + for mod in loaded_modules_order + push!(get!(Vector{Module}, loaded_precompiles, PkgId(mod)), mod) end if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES") MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"]) diff --git a/base/loading.jl b/base/loading.jl index 6391e2511f8d5..8edc1fe79c617 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -524,8 +524,7 @@ See also [`pkgdir`](@ref). """ function pathof(m::Module) @lock require_lock begin - pkgid = get(module_keys, m, nothing) - pkgid === nothing && return nothing + pkgid = PkgId(m) origin = get(pkgorigins, pkgid, nothing) origin === nothing && return nothing path = origin.path @@ -1652,7 +1651,7 @@ get_extension(parent::Module, ext::Symbol) = get_extension(PkgId(parent), ext) function get_extension(parentid::PkgId, ext::Symbol) parentid.uuid === nothing && return nothing extid = PkgId(uuid5(parentid.uuid, string(ext)), string(ext)) - return get(loaded_modules, extid, nothing) + return maybe_root_module(extid) end # End extensions @@ -1811,7 +1810,7 @@ function show(io::IO, it::ImageTarget) end # should sync with the types of arguments of `stale_cachefile` -const StaleCacheKey = Tuple{Base.PkgId, UInt128, String, String} +const StaleCacheKey = Tuple{PkgId, UInt128, String, String} function compilecache_path(pkg::PkgId; ignore_loaded::Bool=false, @@ -2063,7 +2062,7 @@ end modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} # inline a call to start_loading here @assert canstart_loading(modkey, modbuild_id, stalecheck) === nothing - package_locks[modkey] = current_task() => Threads.Condition(require_lock) + package_locks[modkey] = (current_task(), Threads.Condition(require_lock), modbuild_id) startedloading = i modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) for modpath_to_try in modpaths @@ -2139,7 +2138,7 @@ end end # to synchronize multiple tasks trying to import/using something -const package_locks = Dict{PkgId,Pair{Task,Threads.Condition}}() +const package_locks = Dict{PkgId,Tuple{Task,Threads.Condition,UInt128}}() debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but more complete algorithm that can handle simultaneous tasks. # This only triggers if you have multiple tasks trying to load the same package at the same time, @@ -2148,14 +2147,21 @@ debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but mor function canstart_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) assert_havelock(require_lock) require_lock.reentrancy_cnt == 1 || throw(ConcurrencyViolationError("recursive call to start_loading")) - loaded = stalecheck ? maybe_root_module(modkey) : nothing - loaded isa Module && return loaded - if build_id != UInt128(0) + loading = get(package_locks, modkey, nothing) + if loading === nothing + loaded = stalecheck ? maybe_root_module(modkey) : nothing + loaded isa Module && return loaded + if build_id != UInt128(0) + loaded = maybe_loaded_precompile(modkey, build_id) + loaded isa Module && return loaded + end + return nothing + end + if !stalecheck && build_id != UInt128(0) && loading[3] != build_id + # don't block using an existing specific loaded module on needing a different concurrently loaded one loaded = maybe_loaded_precompile(modkey, build_id) loaded isa Module && return loaded end - loading = get(package_locks, modkey, nothing) - loading === nothing && return nothing # load already in progress for this module on the task task, cond = loading deps = String[modkey.name] @@ -2202,7 +2208,7 @@ function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) while true loaded = canstart_loading(modkey, build_id, stalecheck) if loaded === nothing - package_locks[modkey] = current_task() => Threads.Condition(require_lock) + package_locks[modkey] = (current_task(), Threads.Condition(require_lock), build_id) return nothing elseif loaded isa Module return loaded @@ -2333,15 +2339,15 @@ For more details regarding code loading, see the manual sections on [modules](@r [parallel computing](@ref code-availability). """ function require(into::Module, mod::Symbol) - if _require_world_age[] != typemax(UInt) - Base.invoke_in_world(_require_world_age[], __require, into, mod) - else - @invokelatest __require(into, mod) + world = _require_world_age[] + if world == typemax(UInt) + world = get_world_counter() end + return invoke_in_world(world, __require, into, mod) end function __require(into::Module, mod::Symbol) - if into === Base.__toplevel__ && generating_output(#=incremental=#true) + if into === __toplevel__ && generating_output(#=incremental=#true) error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ is not allowed during package precompilation.") end @@ -2445,24 +2451,22 @@ function collect_manifest_warnings() return msg end -require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey) - -function _require_prelocked(uuidkey::PkgId, env=nothing) - if _require_world_age[] != typemax(UInt) - Base.invoke_in_world(_require_world_age[], __require_prelocked, uuidkey, env) - else - @invokelatest __require_prelocked(uuidkey, env) +function require(uuidkey::PkgId) + world = _require_world_age[] + if world == typemax(UInt) + world = get_world_counter() end + return invoke_in_world(world, __require, uuidkey) end - -function __require_prelocked(uuidkey::PkgId, env=nothing) +__require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey) +function _require_prelocked(uuidkey::PkgId, env=nothing) assert_havelock(require_lock) m = start_loading(uuidkey, UInt128(0), true) if m === nothing last = toplevel_load[] try toplevel_load[] = false - m = _require(uuidkey, env) + m = __require_prelocked(uuidkey, env) if m === nothing error("package `$(uuidkey.name)` did not define the expected \ module `$(uuidkey.name)`, check for typos in package module name") @@ -2474,8 +2478,6 @@ function __require_prelocked(uuidkey::PkgId, env=nothing) insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) - else - newm = root_module(uuidkey) end return m end @@ -2491,9 +2493,8 @@ const pkgorigins = Dict{PkgId,PkgOrigin}() const loaded_modules = Dict{PkgId,Module}() # available to be explicitly loaded const loaded_precompiles = Dict{PkgId,Vector{Module}}() # extended (complete) list of modules, available to be loaded const loaded_modules_order = Vector{Module}() -const module_keys = IdDict{Module,PkgId}() # the reverse of loaded_modules -root_module_key(m::Module) = @lock require_lock module_keys[m] +root_module_key(m::Module) = PkgId(m) function maybe_loaded_precompile(key::PkgId, buildid::UInt128) @lock require_lock begin @@ -2527,7 +2528,6 @@ end end maybe_loaded_precompile(key, module_build_id(m)) === nothing && push!(loaded_modules_order, m) loaded_modules[key] = m - module_keys[m] = key end nothing end @@ -2544,24 +2544,27 @@ using Base end # get a top-level Module from the given key +# this is similar to `require`, but worse in almost every possible way root_module(key::PkgId) = @lock require_lock loaded_modules[key] function root_module(where::Module, name::Symbol) key = identify_package(where, String(name)) key isa PkgId || throw(KeyError(name)) return root_module(key) end +root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key) maybe_root_module(key::PkgId) = @lock require_lock get(loaded_modules, key, nothing) -root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key) loaded_modules_array() = @lock require_lock copy(loaded_modules_order) # after unreference_module, a subsequent require call will try to load a new copy of it, if stale # reload(m) = (unreference_module(m); require(m)) function unreference_module(key::PkgId) + @lock require_lock begin if haskey(loaded_modules, key) m = pop!(loaded_modules, key) # need to ensure all modules are GC rooted; will still be referenced - # in module_keys + # in loaded_modules_order + end end end @@ -2582,7 +2585,7 @@ const PKG_PRECOMPILE_HOOK = Ref{Function}() disable_parallel_precompile::Bool = false # Returns `nothing` or the new(ish) module -function _require(pkg::PkgId, env=nothing) +function __require_prelocked(pkg::PkgId, env) assert_havelock(require_lock) # perform the search operation to select the module file require intends to load @@ -2682,7 +2685,7 @@ function _require(pkg::PkgId, env=nothing) unlock(require_lock) try include(__toplevel__, path) - loaded = get(loaded_modules, pkg, nothing) + loaded = maybe_root_module(pkg) finally lock(require_lock) if uuid !== old_uuid @@ -2755,38 +2758,18 @@ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=noth @lock require_lock begin # 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 - newm = maybe_root_module(this_uuidkey) - if newm isa Module - return newm - end - # first since this is a stdlib, try to look there directly first env = Sys.STDLIB - #sourcepath = "" - if ext === nothing - sourcepath = normpath(env, this_uuidkey.name, "src", this_uuidkey.name * ".jl") - else - sourcepath = find_ext_path(normpath(joinpath(env, package_uuidkey.name)), ext) - end - #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(this_uuidkey) - # if sourcepath !== nothing - # sourcepath, env = sourcepath - # end - #end - #if sourcepath === nothing - # throw(ArgumentError(""" - # Package $(repr("text/plain", this_uuidkey)) is required but does not seem to be installed. - # """)) - #end - set_pkgorigin_version_path(this_uuidkey, sourcepath) - depot_path = append_bundled_depot_path!(empty(DEPOT_PATH)) newm = start_loading(this_uuidkey, UInt128(0), true) newm === nothing || return newm try + # first since this is a stdlib, try to look there directly first + if ext === nothing + sourcepath = normpath(env, this_uuidkey.name, "src", this_uuidkey.name * ".jl") + else + sourcepath = find_ext_path(normpath(joinpath(env, package_uuidkey.name)), ext) + end + depot_path = append_bundled_depot_path!(empty(DEPOT_PATH)) + set_pkgorigin_version_path(this_uuidkey, sourcepath) newm = _require_search_from_serialized(this_uuidkey, sourcepath, UInt128(0), false; DEPOT_PATH=depot_path) finally end_loading(this_uuidkey, newm) @@ -3968,32 +3951,32 @@ end if M !== nothing @assert PkgId(M) == req_key && module_build_id(M) === req_build_id depmods[i] = M - elseif root_module_exists(req_key) - M = root_module(req_key) + continue + end + M = maybe_root_module(req_key) + if M isa Module if PkgId(M) == req_key && module_build_id(M) === req_build_id depmods[i] = M + continue elseif M == Core @debug "Rejecting cache file $cachefile because it was made with a different julia version" record_reason(reasons, "wrong julia version") return true # Won't be able to fulfill dependency elseif ignore_loaded || !stalecheck # Used by Pkg.precompile given that there it's ok to precompile different versions of loaded packages - @goto locate_branch else @debug "Rejecting cache file $cachefile because module $req_key is already loaded and incompatible." record_reason(reasons, "wrong dep version loaded") return true # Won't be able to fulfill dependency end - else - @label locate_branch - path = locate_package(req_key) # TODO: add env and/or skip this when stalecheck is false - if path === nothing - @debug "Rejecting cache file $cachefile because dependency $req_key not found." - record_reason(reasons, "dep missing source") - return true # Won't be able to fulfill dependency - end - depmods[i] = (path, req_key, req_build_id) end + path = locate_package(req_key) # TODO: add env and/or skip this when stalecheck is false + if path === nothing + @debug "Rejecting cache file $cachefile because dependency $req_key not found." + record_reason(reasons, "dep missing source") + return true # Won't be able to fulfill dependency + end + depmods[i] = (path, req_key, req_build_id) end # check if this file is going to provide one of our concrete dependencies diff --git a/base/methodshow.jl b/base/methodshow.jl index 477acd2960c48..a2158cb9180e4 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -378,7 +378,6 @@ function url(m::Method) line = m.line line <= 0 || occursin(r"In\[[0-9]+\]"a, file) && return "" Sys.iswindows() && (file = replace(file, '\\' => '/')) - libgit2_id = PkgId(UUID((0x76f85450_5226_5b5a,0x8eaa_529ad045b433)), "LibGit2") if inbase(M) if isempty(Base.GIT_VERSION_INFO.commit) # this url will only work if we're on a tagged release @@ -386,8 +385,10 @@ function url(m::Method) else return "https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/$file#L$line" end - elseif root_module_exists(libgit2_id) - LibGit2 = root_module(libgit2_id) + end + libgit2_id = PkgId(UUID((0x76f85450_5226_5b5a,0x8eaa_529ad045b433)), "LibGit2") + LibGit2 = maybe_root_module(libgit2_id) + if LibGit2 isa Module try d = dirname(file) return LibGit2.with(LibGit2.GitRepoExt(d)) do repo @@ -404,11 +405,10 @@ function url(m::Method) end end catch - return fileurl(file) + # oops, this was a bad idea end - else - return fileurl(file) end + return fileurl(file) end function show(io::IO, ::MIME"text/html", m::Method) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 037e8926d5003..55de3492e9447 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -35,8 +35,8 @@ precompile(Base.unsafe_string, (Ptr{UInt8},)) precompile(Base.unsafe_string, (Ptr{Int8},)) # loading.jl -precompile(Base.__require_prelocked, (Base.PkgId, Nothing)) -precompile(Base._require, (Base.PkgId, Nothing)) +precompile(Base.__require, (Module, Symbol)) +precompile(Base.__require, (Base.PkgId,)) precompile(Base.indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int)) precompile(Base.indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int, Int)) precompile(Tuple{typeof(Base.Threads.atomic_add!), Base.Threads.Atomic{Int}, Int}) diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index e110910bafc2f..86b5cd35abd2f 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -1,17 +1,7 @@ ## Pkg stuff needed before Pkg has loaded const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") - -function load_pkg() - REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") - @lock Base.require_lock begin - # 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, Base.PkgId(REPLExt), nothing) - lock !== nothing && wait(lock[2]) - end - return REPLExt -end +load_pkg() = Base.require_stdlib(Pkg_pkgid, "REPLExt") ## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt # can populate the env correctly before Pkg loads From e802eff6dd910a8321b45c6c629dc4300f5aef1e Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 28 Oct 2024 17:57:04 -0400 Subject: [PATCH 514/548] make `_unsetindex` fast for isbits eltype (#56364) fixes https://github.com/JuliaLang/julia/issues/56359#issuecomment-2441537634 ``` using Plots function f(n) a = Vector{Int}(undef, n) s = time_ns() resize!(a, 8) time_ns() - s end x = 8:10:1000000 y = f.(x) plot(x, y) ``` ![image](https://github.com/user-attachments/assets/5a1fb963-7d44-4cac-bedd-6f0733d4cf56) --- base/genericmemory.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index de1fc668333f5..f814aa4d84bdd 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -84,17 +84,16 @@ function _unsetindex!(A::MemoryRef{T}) where T MemT = typeof(mem) arrayelem = datatype_arrayelem(MemT) elsz = datatype_layoutsize(MemT) - isboxed = 1; isunion = 2 + isbits = 0; isboxed = 1; isunion = 2 + arrayelem == isbits && datatype_pointerfree(T::DataType) && return A t = @_gc_preserve_begin mem p = Ptr{Ptr{Cvoid}}(@inbounds pointer(A)) if arrayelem == isboxed Intrinsics.atomic_pointerset(p, C_NULL, :monotonic) elseif arrayelem != isunion - if !datatype_pointerfree(T::DataType) - for j = 1:Core.sizeof(Ptr{Cvoid}):elsz - # XXX: this violates memory ordering, since it writes more than one C_NULL to each - Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) - end + for j = 1:Core.sizeof(Ptr{Cvoid}):elsz + # XXX: this violates memory ordering, since it writes more than one C_NULL to each + Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) end end @_gc_preserve_end t From 4c076c80af9d9c8439cfa20e2efd5c884d88b64d Mon Sep 17 00:00:00 2001 From: matthias314 <56549971+matthias314@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:02:41 -0400 Subject: [PATCH 515/548] improved `eltype` for `flatten` with tuple argument (#55946) We have always had ``` julia> t = (Int16[1,2], Int32[3,4]); eltype(Iterators.flatten(t)) Any ``` With this PR, the result is `Signed` (`promote_typejoin` applied to the element types of the tuple elements). The same applies to `NamedTuple`: ``` julia> nt = (a = [1,2], b = (3,4)); eltype(Iterators.flatten(nt)) Any # old Int64 # new ``` --- base/iterators.jl | 3 ++- test/iterators.jl | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/iterators.jl b/base/iterators.jl index 8bd30991319b6..1a0d42ed7447f 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -30,7 +30,7 @@ end import .Base: first, last, isempty, length, size, axes, ndims, - eltype, IteratorSize, IteratorEltype, + eltype, IteratorSize, IteratorEltype, promote_typejoin, haskey, keys, values, pairs, getindex, setindex!, get, iterate, popfirst!, isdone, peek, intersect @@ -1213,6 +1213,7 @@ julia> [(x,y) for x in 0:1 for y in 'a':'c'] # collects generators involving It flatten(itr) = Flatten(itr) eltype(::Type{Flatten{I}}) where {I} = eltype(eltype(I)) +eltype(::Type{Flatten{I}}) where {I<:Union{Tuple,NamedTuple}} = promote_typejoin(map(eltype, fieldtypes(I))...) eltype(::Type{Flatten{Tuple{}}}) = eltype(Tuple{}) IteratorEltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, IteratorEltype(I)) IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) diff --git a/test/iterators.jl b/test/iterators.jl index 0df4d9afd371a..d1e7525c43465 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -513,6 +513,8 @@ end @test collect(flatten(Any[flatten(Any[1:2, 6:5]), flatten(Any[6:7, 8:9])])) == Any[1,2,6,7,8,9] @test collect(flatten(Any[2:1])) == Any[] @test eltype(flatten(UnitRange{Int8}[1:2, 3:4])) == Int8 +@test eltype(flatten(([1, 2], [3.0, 4.0]))) == Real +@test eltype(flatten((a = [1, 2], b = Int8[3, 4]))) == Signed @test length(flatten(zip(1:3, 4:6))) == 6 @test length(flatten(1:6)) == 6 @test collect(flatten(Any[])) == Any[] From 710e1f99a75357f9b85d1c517926d083b5cba704 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 29 Oct 2024 13:29:21 +0530 Subject: [PATCH 516/548] Reland "Reroute (Upper/Lower)Triangular * Diagonal through __muldiag #55984" (#56270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This relands #55984 which was reverted in #56267. Previously, in #55984, the destination in multiplying triangular matrices with diagonals was also assumed to be triangular, which is not necessarily the case in `mul!`. Tests for this case, however, were being run non-deterministically, so this wasn't caught by the CI runs. This improves performance: ```julia julia> U = UpperTriangular(rand(100,100)); D = Diagonal(rand(size(U,2))); C = similar(U); julia> @btime mul!($C, $D, $U); 1.517 μs (0 allocations: 0 bytes) # nightly 1.116 μs (0 allocations: 0 bytes) # This PR ``` --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 + stdlib/LinearAlgebra/src/diagonal.jl | 191 ++++++++++++---------- stdlib/LinearAlgebra/test/addmul.jl | 22 +++ stdlib/LinearAlgebra/test/diagonal.jl | 53 +++++- 4 files changed, 184 insertions(+), 84 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 330df503485cb..6e560428a7011 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -656,6 +656,8 @@ matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = _matprod_dest_diag(A, TS) matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = _matprod_dest_diag(B, TS) matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) _matprod_dest_diag(A, TS) = similar(A, TS) +_matprod_dest_diag(A::UnitUpperTriangular, TS) = UpperTriangular(similar(parent(A), TS)) +_matprod_dest_diag(A::UnitLowerTriangular, TS) = LowerTriangular(similar(parent(A), TS)) function _matprod_dest_diag(A::SymTridiagonal, TS) n = size(A, 1) ev = similar(A, TS, max(0, n-1)) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 417bcfa5715b1..1ed599fbb120e 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -397,89 +397,124 @@ function lmul!(D::Diagonal, T::Tridiagonal) return T end -function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - require_one_based_indexing(out, B) - alpha, beta = _add.alpha, _add.beta - if iszero(alpha) - _rmul_or_fill!(out, beta) - else - if bis0 - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - out[i,j] = D.diag[i] * B[i,j] * alpha - end - end - else - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta - end - end +@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B, _add::MulAddMul) + @inbounds for j in axes(B, 2) + @simd for i in axes(B, 1) + _modify!(_add, D.diag[i] * B[i,j], out, (i,j)) end end - return out -end -function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - require_one_based_indexing(out, A) - alpha, beta = _add.alpha, _add.beta - if iszero(alpha) - _rmul_or_fill!(out, beta) - else - if bis0 - @inbounds for j in axes(A, 2) - dja = D.diag[j] * alpha - @simd for i in axes(A, 1) - out[i,j] = A[i,j] * dja - end - end - else - @inbounds for j in axes(A, 2) - dja = D.diag[j] * alpha - @simd for i in axes(A, 1) - out[i,j] = A[i,j] * dja + out[i,j] * beta - end + out +end +_has_matching_zeros(out::UpperOrUnitUpperTriangular, A::UpperOrUnitUpperTriangular) = true +_has_matching_zeros(out::LowerOrUnitLowerTriangular, A::LowerOrUnitLowerTriangular) = true +_has_matching_zeros(out, A) = false +function _rowrange_tri_stored(B::UpperOrUnitUpperTriangular, col) + isunit = B isa UnitUpperTriangular + 1:min(col-isunit, size(B,1)) +end +function _rowrange_tri_stored(B::LowerOrUnitLowerTriangular, col) + isunit = B isa UnitLowerTriangular + col+isunit:size(B,1) +end +_rowrange_tri_zeros(B::UpperOrUnitUpperTriangular, col) = col+1:size(B,1) +_rowrange_tri_zeros(B::LowerOrUnitLowerTriangular, col) = 1:col-1 +function __muldiag_nonzeroalpha!(out, D::Diagonal, B::UpperOrLowerTriangular, _add::MulAddMul) + isunit = B isa UnitUpperOrUnitLowerTriangular + out_maybeparent, B_maybeparent = _has_matching_zeros(out, B) ? (parent(out), parent(B)) : (out, B) + for j in axes(B, 2) + # store the diagonal separately for unit triangular matrices + if isunit + @inbounds _modify!(_add, D.diag[j] * B[j,j], out, (j,j)) + end + # The indices of out corresponding to the stored indices of B + rowrange = _rowrange_tri_stored(B, j) + @inbounds @simd for i in rowrange + _modify!(_add, D.diag[i] * B_maybeparent[i,j], out_maybeparent, (i,j)) + end + # Fill the indices of out corresponding to the zeros of B + # we only fill these if out and B don't have matching zeros + if !_has_matching_zeros(out, B) + rowrange = _rowrange_tri_zeros(B, j) + @inbounds @simd for i in rowrange + _modify!(_add, D.diag[i] * B[i,j], out, (i,j)) end end end return out end -function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - d1 = D1.diag - d2 = D2.diag - alpha, beta = _add.alpha, _add.beta - if iszero(alpha) - _rmul_or_fill!(out.diag, beta) - else - if bis0 - @inbounds @simd for i in eachindex(out.diag) - out.diag[i] = d1[i] * d2[i] * alpha - end - else - @inbounds @simd for i in eachindex(out.diag) - out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta + +@inline function __muldiag_nonzeroalpha!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + beta = _add.beta + _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) + @inbounds for j in axes(A, 2) + dja = _add(D.diag[j]) + @simd for i in axes(A, 1) + _modify!(_add_aisone, A[i,j] * dja, out, (i,j)) + end + end + out +end +function __muldiag_nonzeroalpha!(out, A::UpperOrLowerTriangular, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + isunit = A isa UnitUpperOrUnitLowerTriangular + beta = _add.beta + # since alpha is multiplied to the diagonal element of D, + # we may skip alpha in the second multiplication by setting ais1 to true + _add_aisone = MulAddMul{true,bis0,Bool,typeof(beta)}(true, beta) + # if both A and out have the same upper/lower triangular structure, + # we may directly read and write from the parents + out_maybeparent, A_maybeparent = _has_matching_zeros(out, A) ? (parent(out), parent(A)) : (out, A) + for j in axes(A, 2) + dja = _add(@inbounds D.diag[j]) + # store the diagonal separately for unit triangular matrices + if isunit + @inbounds _modify!(_add_aisone, A[j,j] * dja, out, (j,j)) + end + # indices of out corresponding to the stored indices of A + rowrange = _rowrange_tri_stored(A, j) + @inbounds @simd for i in rowrange + _modify!(_add_aisone, A_maybeparent[i,j] * dja, out_maybeparent, (i,j)) + end + # Fill the indices of out corresponding to the zeros of A + # we only fill these if out and A don't have matching zeros + if !_has_matching_zeros(out, A) + rowrange = _rowrange_tri_zeros(A, j) + @inbounds @simd for i in rowrange + _modify!(_add_aisone, A[i,j] * dja, out, (i,j)) end end end - return out + out end -function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - require_one_based_indexing(out) - alpha, beta = _add.alpha, _add.beta - mA = size(D1, 1) + +@inline function __muldiag_nonzeroalpha!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul) d1 = D1.diag d2 = D2.diag - _rmul_or_fill!(out, beta) - if !iszero(alpha) - @inbounds @simd for i in 1:mA - out[i,i] += d1[i] * d2[i] * alpha - end + outd = out.diag + @inbounds @simd for i in eachindex(d1, d2, outd) + _modify!(_add, d1[i] * d2[i], outd, i) end - return out + out +end + +# ambiguity resolution +@inline function __muldiag_nonzeroalpha!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul) + @inbounds for j in axes(D2, 2), i in axes(D2, 1) + _modify!(_add, D1.diag[i] * D2[i,j], out, (i,j)) + end + out end +# muldiag mainly handles the zero-alpha case, so that we need only +# specialize the non-trivial case function _mul_diag!(out, A, B, _add) + require_one_based_indexing(out, A, B) _muldiag_size_check(size(out), size(A), size(B)) - __muldiag!(out, A, B, _add) + alpha, beta = _add.alpha, _add.beta + if iszero(alpha) + _rmul_or_fill!(out, beta) + else + __muldiag_nonzeroalpha!(out, A, B, _add) + end return out end @@ -659,31 +694,21 @@ for Tri in (:UpperTriangular, :LowerTriangular) @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D)) @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag)) end + @eval *(A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = + @invoke *(A::AbstractMatrix, D::Diagonal) + @eval *(A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = + @invoke *(A::AbstractMatrix, D::Diagonal) for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv)) @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data)) @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag)) end + @eval *(D::Diagonal, A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}) = + @invoke *(D::Diagonal, A::AbstractMatrix) + @eval *(D::Diagonal, A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}) = + @invoke *(D::Diagonal, A::AbstractMatrix) # 3-arg ldiv! @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) - # 3-arg mul! is disambiguated in special.jl - # 5-arg mul! - @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - α, β = _add.alpha, _add.beta - iszero(α) && return _rmul_or_fill!(C, β) - diag′ = bis0 ? nothing : diag(C) - data = mul!(C.data, D, A.data, α, β) - $Tri(_setdiag!(data, _add, D.diag, diag′)) - end - @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} - α, β = _add.alpha, _add.beta - iszero(α) && return _rmul_or_fill!(C, β) - diag′ = bis0 ? nothing : diag(C) - data = mul!(C.data, A.data, D, α, β) - $Tri(_setdiag!(data, _add, D.diag, diag′)) - end end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 208fa930e8ee1..fcd0b51b2e4c0 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -239,4 +239,26 @@ end end end +@testset "Diagonal scaling of a triangular matrix with a non-triangular destination" begin + for MT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) + U = MT(reshape([1:9;],3,3)) + M = Array(U) + D = Diagonal(1:3) + A = reshape([1:9;],3,3) + @test mul!(copy(A), U, D, 2, 3) == M * D * 2 + A * 3 + @test mul!(copy(A), D, U, 2, 3) == D * M * 2 + A * 3 + + # nan values with iszero(alpha) + D = Diagonal(fill(NaN,3)) + @test mul!(copy(A), U, D, 0, 3) == A * 3 + @test mul!(copy(A), D, U, 0, 3) == A * 3 + + # nan values with iszero(beta) + A = fill(NaN,3,3) + D = Diagonal(1:3) + @test mul!(copy(A), U, D, 2, 0) == M * D * 2 + @test mul!(copy(A), D, U, 2, 0) == D * M * 2 + end +end + end # module diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 1c3a9dfa676ac..16f3d2287f317 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -822,6 +822,19 @@ end @test @inferred(D[1,2]) isa typeof(s) @test all(iszero, D[1,2]) end + + @testset "mul!" begin + D1 = Diagonal(fill(ones(2,3), 2)) + D2 = Diagonal(fill(ones(3,2), 2)) + C = similar(D1, size(D1)) + mul!(C, D1, D2) + @test all(x -> size(x) == (2,2), C) + @test C == D1 * D2 + D = similar(D1) + mul!(D, D1, D2) + @test all(x -> size(x) == (2,2), D) + @test D == D1 * D2 + end end @testset "Eigensystem for block diagonal (issue #30681)" begin @@ -1188,7 +1201,7 @@ end @test oneunit(D3) isa typeof(D3) end -@testset "AbstractTriangular" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) +@testset "$Tri" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) A = randn(4, 4) TriA = Tri(A) UTriA = UTri(A) @@ -1218,6 +1231,44 @@ end @test outTri === mul!(outTri, D, UTriA, 2, 1)::Tri == mul!(out, D, Matrix(UTriA), 2, 1) @test outTri === mul!(outTri, TriA, D, 2, 1)::Tri == mul!(out, Matrix(TriA), D, 2, 1) @test outTri === mul!(outTri, UTriA, D, 2, 1)::Tri == mul!(out, Matrix(UTriA), D, 2, 1) + + # we may write to a Unit triangular if the diagonal is preserved + ID = Diagonal(ones(size(UTriA,2))) + @test mul!(copy(UTriA), UTriA, ID) == UTriA + @test mul!(copy(UTriA), ID, UTriA) == UTriA + + @testset "partly filled parents" begin + M = Matrix{BigFloat}(undef, 2, 2) + M[1,1] = M[2,2] = 3 + isupper = Tri == UpperTriangular + M[1+!isupper, 1+isupper] = 3 + D = Diagonal(1:2) + T = Tri(M) + TA = Array(T) + @test T * D == TA * D + @test D * T == D * TA + @test mul!(copy(T), T, D, 2, 3) == 2T * D + 3T + @test mul!(copy(T), D, T, 2, 3) == 2D * T + 3T + + U = UTri(M) + UA = Array(U) + @test U * D == UA * D + @test D * U == D * UA + @test mul!(copy(T), U, D, 2, 3) == 2 * UA * D + 3TA + @test mul!(copy(T), D, U, 2, 3) == 2 * D * UA + 3TA + + M2 = Matrix{BigFloat}(undef, 2, 2) + M2[1+!isupper, 1+isupper] = 3 + U = UTri(M2) + UA = Array(U) + @test U * D == UA * D + @test D * U == D * UA + ID = Diagonal(ones(size(U,2))) + @test mul!(copy(U), U, ID) == U + @test mul!(copy(U), ID, U) == U + @test mul!(copy(U), U, ID, 2, -1) == U + @test mul!(copy(U), ID, U, 2, -1) == U + end end struct SMatrix1{T} <: AbstractArray{T,2} From cb757c41102e4cdc899d76464acde0938c36de9e Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 29 Oct 2024 13:30:18 +0530 Subject: [PATCH 517/548] Add one-arg `norm` method (#56330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the latency of `norm` calls, as the single-argument method lacks branches and doesn't use aggressive constant propagation, and is therefore simpler to compile. Given that a lot of `norm` calls use `p==2`, it makes sense for us to reduce the latency on this call. ```julia julia> using LinearAlgebra julia> A = rand(2,2); julia> @time norm(A); 0.247515 seconds (390.09 k allocations: 19.993 MiB, 33.57% gc time, 99.99% compilation time) # master 0.067201 seconds (121.24 k allocations: 6.067 MiB, 99.98% compilation time) # this PR ``` An example of an improvement in ttfx because of this: ```julia julia> A = rand(2,2); julia> @time A ≈ A; 0.556475 seconds (1.16 M allocations: 59.949 MiB, 24.14% gc time, 100.00% compilation time) # master 0.333114 seconds (899.85 k allocations: 46.574 MiB, 8.11% gc time, 99.99% compilation time) # this PR ``` --- stdlib/LinearAlgebra/src/generic.jl | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 20c58e593d3f6..21719c0c50127 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -669,11 +669,9 @@ julia> norm(hcat(v,v), Inf) == norm(vcat(v,v), Inf) != norm([v,v], Inf) true ``` """ -Base.@constprop :aggressive function norm(itr, p::Real=2) +Base.@constprop :aggressive function norm(itr, p::Real) isempty(itr) && return float(norm(zero(eltype(itr)))) - v, s = iterate(itr) - !isnothing(s) && !ismissing(v) && v == itr && throw(ArgumentError( - "cannot evaluate norm recursively if the type of the initial element is identical to that of the container")) + norm_recursive_check(itr) if p == 2 return norm2(itr) elseif p == 1 @@ -688,6 +686,18 @@ Base.@constprop :aggressive function norm(itr, p::Real=2) normp(itr, p) end end +# Split into a separate method to reduce latency in norm(x) calls (#56330) +function norm(itr) + isempty(itr) && return float(norm(zero(eltype(itr)))) + norm_recursive_check(itr) + norm2(itr) +end +function norm_recursive_check(itr) + v, s = iterate(itr) + !isnothing(s) && !ismissing(v) && v == itr && throw(ArgumentError( + "cannot evaluate norm recursively if the type of the initial element is identical to that of the container")) + return nothing +end """ norm(x::Number, p::Real=2) @@ -808,7 +818,7 @@ julia> opnorm(A, 1) 5.0 ``` """ -Base.@constprop :aggressive function opnorm(A::AbstractMatrix, p::Real=2) +Base.@constprop :aggressive function opnorm(A::AbstractMatrix, p::Real) if p == 2 return opnorm2(A) elseif p == 1 @@ -819,6 +829,7 @@ Base.@constprop :aggressive function opnorm(A::AbstractMatrix, p::Real=2) throw(ArgumentError(lazy"invalid p-norm p=$p. Valid: 1, 2, Inf")) end end +opnorm(A::AbstractMatrix) = opnorm2(A) """ opnorm(x::Number, p::Real=2) From 07530bcb23d1a576b8260500833b767b81442517 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 29 Oct 2024 15:42:48 +0100 Subject: [PATCH 518/548] fix a forgotten rename `readuntil` -> `copyuntil` (#56380) Fixes https://github.com/JuliaLang/julia/issues/56352, with the repro in that issue: ``` Master: 1.114874 seconds (13.01 M allocations: 539.592 MiB, 3.80% gc time) After: 0.369492 seconds (12.99 M allocations: 485.031 MiB, 10.73% gc time) 1.10: 0.341114 seconds (8.36 M allocations: 454.242 MiB, 2.69% gc time) ``` --- base/stream.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/stream.jl b/base/stream.jl index 2f00538ad0e96..488acd41d2a9e 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -1028,7 +1028,7 @@ function readavailable(this::LibuvStream) return bytes end -function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) +function copyuntil(out::IO, x::LibuvStream, c::UInt8; keep::Bool=false) iolock_begin() buf = x.buffer @assert buf.seekable == false @@ -1058,9 +1058,9 @@ function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) end end end - bytes = readuntil(buf, c, keep=keep) + copyuntil(out, buf, c; keep) iolock_end() - return bytes + return out end uv_write(s::LibuvStream, p::Vector{UInt8}) = GC.@preserve p uv_write(s, pointer(p), UInt(sizeof(p))) From e3d5aa38a2e43726c42723b1da894e39e9f99ecd Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:44:07 +0900 Subject: [PATCH 519/548] remove unnecessary operations from `typejoin_union_tuple` (#56379) Removes the unnecessary call to `unwrap_unionall` and type assertion. --- base/promotion.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/promotion.jl b/base/promotion.jl index 689a4e4be8f39..1004c64433ec1 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -199,9 +199,8 @@ end function typejoin_union_tuple(T::DataType) @_foldable_meta - u = Base.unwrap_unionall(T) - p = (u::DataType).parameters - lr = length(p)::Int + p = T.parameters + lr = length(p) if lr == 0 return Tuple{} end From e4dc9d357a1a46ac4078ae81265f6edd9b593bdf Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 29 Oct 2024 13:18:01 -0400 Subject: [PATCH 520/548] precompile: fix performance issues with IO (#56370) The string API here rapidly becomes unusably slow if dumping much debug output during precompile. Fix the design here to use an intermediate IO instead to prevent that. --- base/precompilation.jl | 51 +++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index f597acef9b57f..92f4980c2b889 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -641,7 +641,7 @@ function _precompilepkgs(pkgs::Vector{String}, return false end end - std_outputs = Dict{PkgConfig,String}() + std_outputs = Dict{PkgConfig,IOBuffer}() taskwaiting = Set{PkgConfig}() pkgspidlocked = Dict{PkgConfig,String}() pkg_liveprinted = nothing @@ -663,7 +663,7 @@ function _precompilepkgs(pkgs::Vector{String}, print(io, ansi_cleartoendofline, str) end end - std_outputs[pkg_config] = string(get(std_outputs, pkg_config, ""), str) + write(get!(IOBuffer, std_outputs, pkg_config), str) if !in(pkg_config, taskwaiting) && occursin("waiting for IO to finish", str) !fancyprint && lock(print_lock) do println(io, pkg.name, color_string(" Waiting for background task / IO / timer.", Base.warn_color())) @@ -857,8 +857,9 @@ function _precompilepkgs(pkgs::Vector{String}, close(std_pipe.in) # close pipe to end the std output monitor wait(t_monitor) if err isa ErrorException || (err isa ArgumentError && startswith(err.msg, "Invalid header in cache file")) - failed_deps[pkg_config] = (strict || is_direct_dep) ? string(sprint(showerror, err), "\n", strip(get(std_outputs, pkg_config, ""))) : "" + errmsg = String(take!(get(IOBuffer, std_outputs, pkg_config))) delete!(std_outputs, pkg_config) # so it's not shown as warnings, given error report + failed_deps[pkg_config] = (strict || is_direct_dep) ? string(sprint(showerror, err), "\n", strip(errmsg)) : "" !fancyprint && lock(print_lock) do println(io, " "^9, color_string(" ✗ ", Base.error_color()), name) end @@ -936,20 +937,22 @@ function _precompilepkgs(pkgs::Vector{String}, end # show any stderr output, even if Pkg.precompile has been interrupted (quick_exit=true), given user may be # interrupting a hanging precompile job with stderr output. julia#48371 - filter!(kv -> !isempty(strip(last(kv))), std_outputs) # remove empty output - if !isempty(std_outputs) - plural1 = length(std_outputs) == 1 ? "y" : "ies" - plural2 = length(std_outputs) == 1 ? "" : "s" - print(iostr, "\n ", color_string("$(length(std_outputs))", Base.warn_color()), " dependenc$(plural1) had output during precompilation:") - for (pkg_config, err) in std_outputs - pkg, config = pkg_config - err = if pkg == pkg_liveprinted - "[Output was shown above]" - else - join(split(strip(err), "\n"), color_string("\n│ ", Base.warn_color())) + let std_outputs = Tuple{PkgConfig,SubString{String}}[(pkg_config, strip(String(take!(io)))) for (pkg_config,io) in std_outputs] + filter!(kv -> !isempty(last(kv)), std_outputs) + if !isempty(std_outputs) + plural1 = length(std_outputs) == 1 ? "y" : "ies" + plural2 = length(std_outputs) == 1 ? "" : "s" + print(iostr, "\n ", color_string("$(length(std_outputs))", Base.warn_color()), " dependenc$(plural1) had output during precompilation:") + for (pkg_config, err) in std_outputs + pkg, config = pkg_config + err = if pkg == pkg_liveprinted + "[Output was shown above]" + else + join(split(err, "\n"), color_string("\n│ ", Base.warn_color())) + end + name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name + print(iostr, color_string("\n┌ ", Base.warn_color()), name, color_string("\n│ ", Base.warn_color()), err, color_string("\n└ ", Base.warn_color())) end - name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name - print(iostr, color_string("\n┌ ", Base.warn_color()), name, color_string("\n│ ", Base.warn_color()), err, color_string("\n└ ", Base.warn_color())) end end end @@ -959,20 +962,26 @@ function _precompilepkgs(pkgs::Vector{String}, end end quick_exit && return - err_str = "" + err_str = IOBuffer() n_direct_errs = 0 for (pkg_config, err) in failed_deps dep, config = pkg_config if strict || (dep in direct_deps) - config_str = isempty(config[1]) ? "" : "$(join(config[1], " ")) " - err_str = string(err_str, "\n$(dep.name) $config_str\n\n$err", (n_direct_errs > 0 ? "\n" : "")) + print(err_str, "\n", dep.name, " ") + for cfg in config[1] + print(err_str, cfg, " ") + end + print(err_str, "\n\n", err) + n_direct_errs > 0 && write(err_str, "\n") n_direct_errs += 1 end end - if err_str != "" + if position(err_str) > 0 + skip(err_str, -1) + truncate(err_str, position(err_str)) pluralde = n_direct_errs == 1 ? "y" : "ies" direct = strict ? "" : "direct " - err_msg = "The following $n_direct_errs $(direct)dependenc$(pluralde) failed to precompile:\n$(err_str[1:end-1])" + err_msg = "The following $n_direct_errs $(direct)dependenc$(pluralde) failed to precompile:\n$(String(take!(err_str)))" if internal_call # aka. auto-precompilation if isinteractive() && !get(ENV, "CI", false) plural1 = length(failed_deps) == 1 ? "y" : "ies" From 9850a3881221a57a382e98c9b9ae2bf97ac3966d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 29 Oct 2024 22:02:37 +0100 Subject: [PATCH 521/548] cache the `find_all_in_cache_path` call during parallel precompilation (#56369) Before (in an environment with DifferentialEquations.jl): ```julia julia> @time Pkg.precompile() 0.733576 seconds (3.44 M allocations: 283.676 MiB, 6.24% gc time) julia> isfile_calls[1:10] 10-element Vector{Pair{String, Int64}}: "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Printf/3FQLY_zHycD.ji" => 178 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Printf/3FQLY_xxrt3.ji" => 178 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Dates/p8See_xxrt3.ji" => 158 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Dates/p8See_zHycD.ji" => 158 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/TOML/mjrwE_zHycD.ji" => 155 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/TOML/mjrwE_xxrt3.ji" => 155 "/home/kc/.julia/compiled/v1.12/Preferences/pWSk8_4Qv86.ji" => 152 "/home/kc/.julia/compiled/v1.12/Preferences/pWSk8_juhqb.ji" => 152 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/StyledStrings/UcVoM_zHycD.ji" => 144 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/StyledStrings/UcVoM_xxrt3.ji" => 144 ``` After: ```julia julia> @time Pkg.precompile() 0.460077 seconds (877.59 k allocations: 108.075 MiB, 4.77% gc time) julia> isfile_calls[1:10] 10-element Vector{Pair{String, Int64}}: "/tmp/jl_a5xFWK/Project.toml" => 15 "/tmp/jl_a5xFWK/Manifest.toml" => 7 "/home/kc/.julia/registries/General.toml" => 6 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Markdown/src/Markdown.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Serialization/src/Serialization.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Distributed/src/Distributed.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/UUIDs/src/UUIDs.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LibCURL/src/LibCURL.jl" => 3 ``` Performance is improved and we are not calling `isfile` on a bunch of the same ji files hundreds times. Benchmark is made on a linux machine so performance diff should be a lot better on Windows where these `isfile_casesensitive` call is much more expensive. Fixes https://github.com/JuliaLang/julia/issues/56366 --------- Co-authored-by: KristofferC Co-authored-by: Ian Butterworth --- base/loading.jl | 10 ++++++---- base/precompilation.jl | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 8edc1fe79c617..f7f749e334ed1 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1815,7 +1815,8 @@ const StaleCacheKey = Tuple{PkgId, UInt128, String, String} function compilecache_path(pkg::PkgId; ignore_loaded::Bool=false, stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), - cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), + cachepath_cache::Dict{PkgId, Vector{String}}=Dict{PkgId, Vector{String}}(), + cachepaths::Vector{String}=get!(() -> find_all_in_cache_path(pkg), cachepath_cache, pkg), sourcepath::Union{String,Nothing}=Base.locate_package(pkg), flags::CacheFlags=CacheFlags()) path = nothing @@ -1830,7 +1831,7 @@ function compilecache_path(pkg::PkgId; for dep in staledeps dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} - modpaths = find_all_in_cache_path(modkey) + modpaths = get!(() -> find_all_in_cache_path(modkey), cachepath_cache, modkey) for modpath_to_try in modpaths::Vector{String} stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try)::StaleCacheKey if get!(() -> stale_cachefile(stale_cache_key...; ignore_loaded, requested_flags=flags) === true, @@ -1872,10 +1873,11 @@ fresh julia session specify `ignore_loaded=true`. function isprecompiled(pkg::PkgId; ignore_loaded::Bool=false, stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), - cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), + cachepath_cache::Dict{PkgId, Vector{String}}=Dict{PkgId, Vector{String}}(), + cachepaths::Vector{String}=get!(() -> find_all_in_cache_path(pkg), cachepath_cache, pkg), sourcepath::Union{String,Nothing}=Base.locate_package(pkg), flags::CacheFlags=CacheFlags()) - path = compilecache_path(pkg; ignore_loaded, stale_cache, cachepaths, sourcepath, flags) + path = compilecache_path(pkg; ignore_loaded, stale_cache, cachepath_cache, cachepaths, sourcepath, flags) return !isnothing(path) end diff --git a/base/precompilation.jl b/base/precompilation.jl index 92f4980c2b889..54f13d298a462 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -415,6 +415,8 @@ function _precompilepkgs(pkgs::Vector{String}, color_string(cstr::String, col::Union{Int64, Symbol}) = _color_string(cstr, col, hascolor) stale_cache = Dict{StaleCacheKey, Bool}() + cachepath_cache = Dict{PkgId, Vector{String}}() + exts = Dict{PkgId, String}() # ext -> parent # make a flat map of each dep and its direct deps depsmap = Dict{PkgId, Vector{PkgId}}() @@ -785,7 +787,7 @@ function _precompilepkgs(pkgs::Vector{String}, ## precompilation loop for (pkg, deps) in depsmap - cachepaths = Base.find_all_in_cache_path(pkg) + cachepaths = get!(() -> Base.find_all_in_cache_path(pkg), cachepath_cache, pkg) sourcepath = Base.locate_package(pkg) single_requested_pkg = length(requested_pkgs) == 1 && only(requested_pkgs) == pkg.name for config in configs @@ -808,7 +810,7 @@ function _precompilepkgs(pkgs::Vector{String}, wait(was_processed[(dep,config)]) end circular = pkg in circular_deps - is_stale = !Base.isprecompiled(pkg; ignore_loaded=true, stale_cache, cachepaths, sourcepath, flags=cacheflags) + is_stale = !Base.isprecompiled(pkg; ignore_loaded=true, stale_cache, cachepath_cache, cachepaths, sourcepath, flags=cacheflags) if !circular && is_stale Base.acquire(parallel_limiter) is_direct_dep = pkg in direct_deps From a9342d679979b0f19522949b9b00200c5b2fc010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:50:25 +0000 Subject: [PATCH 522/548] [docs] Fix note admonition in llvm-passes.md (#56392) At the moment this is rendered incorrectly: https://docs.julialang.org/en/v1.11.1/devdocs/llvm-passes/#JuliaLICM --- doc/src/devdocs/llvm-passes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 736faf54c219b..da600f73fd696 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -144,6 +144,6 @@ This pass is used to hoist Julia-specific intrinsics out of loops. Specifically, 3. Hoist allocations out of loops when they do not escape the loop 1. We use a very conservative definition of escape here, the same as the one used in `AllocOptPass`. This transformation can reduce the number of allocations in the IR, even when an allocation escapes the function altogether. -!!!note +!!! note This pass is required to preserve LLVM's [MemorySSA](https://llvm.org/docs/MemorySSA.html) ([Short Video](https://www.youtube.com/watch?v=bdxWmryoHak), [Longer Video](https://www.youtube.com/watch?v=1e5y6WDbXCQ)) and [ScalarEvolution](https://baziotis.cs.illinois.edu/compilers/introduction-to-scalar-evolution.html) ([Newer Slides](https://llvm.org/devmtg/2018-04/slides/Absar-ScalarEvolution.pdf) [Older Slides](https://llvm.org/devmtg/2009-10/ScalarEvolutionAndLoopOptimization.pdf)) analyses. From 2fe65625cf787c205571a52e26b8ef970dc4f65b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 30 Oct 2024 18:23:46 +0530 Subject: [PATCH 523/548] structure-preserving broadcast for `SymTridiagonal` (#56001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this PR, certain broadcasting operations preserve the structure of a `SymTridiagonal`: ```julia julia> S = SymTridiagonal([1,2,3,4], [1,2,3]) 4×4 SymTridiagonal{Int64, Vector{Int64}}: 1 1 ⋅ ⋅ 1 2 2 ⋅ ⋅ 2 3 3 ⋅ ⋅ 3 4 julia> S .* 2 4×4 SymTridiagonal{Int64, Vector{Int64}}: 2 2 ⋅ ⋅ 2 4 4 ⋅ ⋅ 4 6 6 ⋅ ⋅ 6 8 ``` This was deliberately disabled on master, but I couldn't find any test that fails if this is enabled. --- stdlib/LinearAlgebra/src/structuredbroadcast.jl | 5 +++-- stdlib/LinearAlgebra/test/structuredbroadcast.jl | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index 0c06f84116fc7..9a4d55fd58bf0 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -171,7 +171,7 @@ end function Base.similar(bc::Broadcasted{StructuredMatrixStyle{T}}, ::Type{ElType}) where {T,ElType} inds = axes(bc) fzerobc = fzeropreserving(bc) - if isstructurepreserving(bc) || (fzerobc && !(T <: Union{SymTridiagonal,UnitLowerTriangular,UnitUpperTriangular})) + if isstructurepreserving(bc) || (fzerobc && !(T <: Union{UnitLowerTriangular,UnitUpperTriangular})) return structured_broadcast_alloc(bc, T, ElType, length(inds[1])) elseif fzerobc && T <: UnitLowerTriangular return similar(convert(Broadcasted{StructuredMatrixStyle{LowerTriangular}}, bc), ElType) @@ -240,7 +240,8 @@ function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) end for i = 1:size(dest, 1)-1 v = @inbounds bc[BandIndex(1, i)] - v == (@inbounds bc[BandIndex(-1, i)]) || throw(ArgumentError(lazy"broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) + v == transpose(@inbounds bc[BandIndex(-1, i)]) || + throw(ArgumentError(lazy"broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) dest.ev[i] = v end return dest diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl index 384ed5b3b60cf..71494aedcbef5 100644 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/test/structuredbroadcast.jl @@ -3,6 +3,10 @@ module TestStructuredBroadcast using Test, LinearAlgebra +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) +using .Main.SizedArrays + @testset "broadcast[!] over combinations of scalars, structured matrices, and dense vectors/matrices" begin N = 10 s = rand() @@ -12,10 +16,11 @@ using Test, LinearAlgebra D = Diagonal(rand(N)) B = Bidiagonal(rand(N), rand(N - 1), :U) T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) + S = SymTridiagonal(rand(N), rand(N - 1)) U = UpperTriangular(rand(N,N)) L = LowerTriangular(rand(N,N)) M = Matrix(rand(N,N)) - structuredarrays = (D, B, T, U, L, M) + structuredarrays = (D, B, T, U, L, M, S) fstructuredarrays = map(Array, structuredarrays) for (X, fX) in zip(structuredarrays, fstructuredarrays) @test (Q = broadcast(sin, X); typeof(Q) == typeof(X) && Q == broadcast(sin, fX)) @@ -166,10 +171,11 @@ end D = Diagonal(rand(N)) B = Bidiagonal(rand(N), rand(N - 1), :U) T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) + S = SymTridiagonal(rand(N), rand(N - 1)) U = UpperTriangular(rand(N,N)) L = LowerTriangular(rand(N,N)) M = Matrix(rand(N,N)) - structuredarrays = (M, D, B, T, U, L) + structuredarrays = (M, D, B, T, S, U, L) fstructuredarrays = map(Array, structuredarrays) for (X, fX) in zip(structuredarrays, fstructuredarrays) @test (Q = map(sin, X); typeof(Q) == typeof(X) && Q == map(sin, fX)) @@ -363,6 +369,11 @@ end U = UpperTriangular([(i+j)*A for i in 1:3, j in 1:3]) standardbroadcastingtests(U, UpperTriangular) end + @testset "SymTridiagonal" begin + m = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) + S = SymTridiagonal(fill(m,4), fill(m,3)) + standardbroadcastingtests(S, SymTridiagonal) + end end end From 599b7ec51acfc7e88c7668e615ec85b285ff81ba Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:39:56 -0400 Subject: [PATCH 524/548] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20116ba910c=20to=209f8e11a4c=20(#56386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: 116ba910c New commit: 9f8e11a4c Julia version: 1.12.0-DEV Pkg version: 1.12.0 Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/116ba910c74ab565d348aa8a50d6dd10148f11ab...9f8e11a4c0efb3b68a1e25a33f372f398c89cd66 ``` $ git log --oneline 116ba910c..9f8e11a4c 9f8e11a4c strip out tree_hash for stdlibs that have have been freed in newer julia versions (#4062) c0df25a47 rm dead code (#4061) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 | 1 - .../Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 | 1 - .../Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/md5 | 1 + .../Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/md5 create mode 100644 deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/sha512 diff --git a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 deleted file mode 100644 index 61dca3054d58f..0000000000000 --- a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9905cd10c29974f3b0bb47f2e40951b0 diff --git a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 b/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 deleted file mode 100644 index 3757366fd23cf..0000000000000 --- a/deps/checksums/Pkg-116ba910c74ab565d348aa8a50d6dd10148f11ab.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b99db15e6646b1eaa35df705ca39c7f3ddb05073293c779963231c22d17f4ae449739f4e8535a41ae9ae5fb1661f76c915fb2c7853a86fc695335b3e1ce3c06d diff --git a/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/md5 b/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/md5 new file mode 100644 index 0000000000000..1a0000a9d806e --- /dev/null +++ b/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/md5 @@ -0,0 +1 @@ +f8a63ab3677f5df71a93d6d0a1f6333d diff --git a/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/sha512 b/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/sha512 new file mode 100644 index 0000000000000..99020c2fa7a32 --- /dev/null +++ b/deps/checksums/Pkg-9f8e11a4c0efb3b68a1e25a33f372f398c89cd66.tar.gz/sha512 @@ -0,0 +1 @@ +3351c068974d2520a8f8fa9030d90c73cce69c87feae95c6ac6f166d3970a8096ed443280bef80b3409238a988aaea98f267bbec8978ad79594cedb0d59a37e5 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 24c73834eca22..32c6a094005f9 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 116ba910c74ab565d348aa8a50d6dd10148f11ab +PKG_SHA1 = 9f8e11a4c0efb3b68a1e25a33f372f398c89cd66 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 717bf54848376e93b13151c879ecb811077c2acc Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 30 Oct 2024 16:01:07 +0100 Subject: [PATCH 525/548] load extensions with fewer triggers earlier (#49891) Aimed to support the use case in https://github.com/JuliaLang/julia/issues/48734#issuecomment-1554626135. https://github.com/KristofferC/ExtSquared.jl is an example, see specifically https://github.com/KristofferC/ExtSquared.jl/blob/ded7c57d6f799674e3310b8174dfb07591bbe025/ext/BExt.jl#L4. I think this makes sense, happy for a second pair of eyes though. cc @termi-official --------- Co-authored-by: KristofferC Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> --- base/loading.jl | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index f7f749e334ed1..28875b8713b35 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1459,6 +1459,7 @@ end mutable struct ExtensionId const id::PkgId const parentid::PkgId # just need the name, for printing + const n_total_triggers::Int ntriggers::Int # how many more packages must be defined until this is loaded end @@ -1554,7 +1555,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} continue # extension is already primed or loaded, don't add it again end EXT_PRIMED[id] = parent - gid = ExtensionId(id, parent, 1 + length(triggers)) + gid = ExtensionId(id, parent, 1 + length(triggers), 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) for trigger in triggers @@ -1598,25 +1599,22 @@ function run_extension_callbacks(pkgid::PkgId) # take ownership of extids that depend on this pkgid extids = pop!(EXT_DORMITORY, pkgid, nothing) extids === nothing && return + extids_to_load = Vector{ExtensionId}() for extid in extids - if extid.ntriggers > 0 - # indicate pkgid is loaded - extid.ntriggers -= 1 - end - if extid.ntriggers < 0 - # indicate pkgid is loaded - extid.ntriggers += 1 - succeeded = false - else - succeeded = true - end + @assert extid.ntriggers > 0 + extid.ntriggers -= 1 if extid.ntriggers == 0 - # actually load extid, now that all dependencies are met, - # and record the result - succeeded = succeeded && run_extension_callbacks(extid) - succeeded || push!(EXT_DORMITORY_FAILED, extid) + push!(extids_to_load, extid) end end + # Load extensions with the fewest triggers first + sort!(extids_to_load, by=extid->extid.n_total_triggers) + for extid in extids_to_load + # actually load extid, now that all dependencies are met, + succeeded = run_extension_callbacks(extid) + succeeded || push!(EXT_DORMITORY_FAILED, extid) + end + return end From c0ce290b8b72ccabfa15aadc8576ab4c14e27d8f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 30 Oct 2024 22:45:13 +0530 Subject: [PATCH 526/548] Dispatch in generic_matmatmul (#56384) Replacing the branches by dispatch reduces latency, presumably because there's less dead code in the method. ```julia julia> using LinearAlgebra julia> A = rand(Int,2,2); B = copy(A); C = similar(A); julia> @time mul!(C, A, B, 1, 2); 0.363944 seconds (1.65 M allocations: 84.584 MiB, 37.57% gc time, 99.99% compilation time) # master 0.102676 seconds (176.55 k allocations: 8.904 MiB, 27.04% gc time, 99.97% compilation time) # this PR ``` The latency is now distributed between the different branches: ```julia julia> @time mul!(C, A, B, 1, 2); 0.072441 seconds (176.55 k allocations: 8.903 MiB, 99.97% compilation time) julia> @time mul!(C, A', B, 1, 2); 0.085817 seconds (116.44 k allocations: 5.913 MiB, 99.96% compilation time: 4% of which was recompilation) julia> @time mul!(C, A', B', 1, 2); 0.345337 seconds (1.07 M allocations: 54.773 MiB, 25.77% gc time, 99.99% compilation time: 40% of which was recompilation) ``` It would be good to look into why there's recompilation in the last case, but the branch is less commonly taken than the others that have significantly lower latency after this PR. --- stdlib/LinearAlgebra/src/matmul.jl | 87 +++++++++++++++++------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 74cb50f955bbb..8e90b21a4b7ce 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -994,49 +994,60 @@ _generic_matmatmul!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMa if BxN != CxN throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) end - if isbitstype(R) && sizeof(R) ≤ 16 && !(A isa Adjoint || A isa Transpose) - _rmul_or_fill!(C, beta) - (iszero(alpha) || isempty(A) || isempty(B)) && return C - @inbounds for n in BxN, k in BxK - # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) - Balpha = @stable_muladdmul MulAddMul(alpha, false)(B[k,n]) - @simd for m in AxM - C[m,n] = muladd(A[m,k], Balpha, C[m,n]) - end - end - elseif isbitstype(R) && sizeof(R) ≤ 16 && ((A isa Adjoint && B isa Adjoint) || (A isa Transpose && B isa Transpose)) - _rmul_or_fill!(C, beta) - (iszero(alpha) || isempty(A) || isempty(B)) && return C - t = wrapperop(A) - pB = parent(B) - pA = parent(A) - tmp = similar(C, CxN) - ci = first(CxM) - ta = t(alpha) - for i in AxM - mul!(tmp, pB, view(pA, :, i)) - @views C[ci,:] .+= t.(ta .* tmp) - ci += 1 - end - else - if iszero(alpha) || isempty(A) || isempty(B) - return _rmul_or_fill!(C, beta) + __generic_matmatmul!(C, A, B, alpha, beta, Val(isbitstype(R) && sizeof(R) ≤ 16)) + return C +end +__generic_matmatmul!(C, A::Adjoint, B::Adjoint, alpha, beta, ::Val{true}) = _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) +__generic_matmatmul!(C, A::Transpose, B::Transpose, alpha, beta, ::Val{true}) = _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) +__generic_matmatmul!(C, A::Union{Adjoint, Transpose}, B, alpha, beta, ::Val{true}) = _generic_matmatmul_generic!(C, A, B, alpha, beta) +__generic_matmatmul!(C, A, B, alpha, beta, ::Val{true}) = _generic_matmatmul_nonadjtrans!(C, A, B, alpha, beta) +__generic_matmatmul!(C, A, B, alpha, beta, ::Val{false}) = _generic_matmatmul_generic!(C, A, B, alpha, beta) + +function _generic_matmatmul_nonadjtrans!(C, A, B, alpha, beta) + _rmul_or_fill!(C, beta) + (iszero(alpha) || isempty(A) || isempty(B)) && return C + @inbounds for n in axes(B, 2), k in axes(B, 1) + # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) + Balpha = @stable_muladdmul MulAddMul(alpha, false)(B[k,n]) + @simd for m in axes(A, 1) + C[m,n] = muladd(A[m,k], Balpha, C[m,n]) end - a1 = first(AxK) - b1 = first(BxK) - @inbounds for i in AxM, j in BxN - z2 = zero(A[i, a1]*B[b1, j] + A[i, a1]*B[b1, j]) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - @simd for k in AxK - Ctmp = muladd(A[i, k], B[k, j], Ctmp) - end - @stable_muladdmul _modify!(MulAddMul(alpha,beta), Ctmp, C, (i,j)) + end + C +end +function _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) + _rmul_or_fill!(C, beta) + (iszero(alpha) || isempty(A) || isempty(B)) && return C + t = wrapperop(A) + pB = parent(B) + pA = parent(A) + tmp = similar(C, axes(C, 2)) + ci = firstindex(C, 1) + ta = t(alpha) + for i in axes(A, 1) + mul!(tmp, pB, view(pA, :, i)) + @views C[ci,:] .+= t.(ta .* tmp) + ci += 1 + end + C +end +function _generic_matmatmul_generic!(C, A, B, alpha, beta) + if iszero(alpha) || isempty(A) || isempty(B) + return _rmul_or_fill!(C, beta) + end + a1 = firstindex(A, 2) + b1 = firstindex(B, 1) + @inbounds for i in axes(A, 1), j in axes(B, 2) + z2 = zero(A[i, a1]*B[b1, j] + A[i, a1]*B[b1, j]) + Ctmp = convert(promote_type(eltype(C), typeof(z2)), z2) + @simd for k in axes(A, 2) + Ctmp = muladd(A[i, k], B[k, j], Ctmp) end + @stable_muladdmul _modify!(MulAddMul(alpha,beta), Ctmp, C, (i,j)) end - return C + C end - # multiply 2x2 matrices Base.@constprop :aggressive function matmul2x2(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} matmul2x2!(similar(B, promote_op(matprod, T, S), 2, 2), tA, tB, A, B) From db6e95e20095cc41307dd6b80aa66320e98cb64b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 30 Oct 2024 22:54:36 +0530 Subject: [PATCH 527/548] Add `atol` to addmul tests (#56210) This avoids the issues as in https://github.com/JuliaLang/julia/issues/55781 and https://github.com/JuliaLang/julia/issues/55779 where we compare small numbers using a relative tolerance. Also, in this PR, I have added an extra test, so now we compare both `A * B * alpha + C * beta` and `A * B * alpha - C * beta` with the corresponding in-place versions. The idea is that if the terms `A * B * alpha` and ` C * beta` have similar magnitudes, at least one of the two expressions will usually result in a large enough number that may be compared using a relative tolerance. I am unsure if the `atol` chosen here is optimal, as I have ballparked it to use the maximum `eps` by looking at all the `eltype`s involved. Fixes #55781 Fixes #55779 --- stdlib/LinearAlgebra/test/addmul.jl | 69 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index fcd0b51b2e4c0..903e3b17f0ef1 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -130,6 +130,29 @@ for cmat in mattypes, push!(testdata, (cmat{celt}, amat{aelt}, bmat{belt})) end +strongzero(α) = iszero(α) ? false : α +function compare_matmul(C, A, B, α, β, + rtol = max(rtoldefault.(real.(eltype.((C, A, B))))..., + rtoldefault.(real.(typeof.((α, β))))...); + Ac = collect(A), Bc = collect(B), Cc = collect(C)) + @testset let A=A, B=B, C=C, α=α, β=β + Ccopy = copy(C) + returned_mat = mul!(Ccopy, A, B, α, β) + @test returned_mat === Ccopy + atol = max(maximum(eps∘real∘float∘eltype, (C,A,B)), + maximum(eps∘real∘float∘typeof, (α,β))) + exp_val = Ac * Bc * strongzero(α) + Cc * strongzero(β) + @test collect(returned_mat) ≈ exp_val rtol=rtol atol=atol + rtol_match = isapprox(collect(returned_mat), exp_val, rtol=rtol) + if !(rtol_match || β isa Bool || isapprox(β, 0, atol=eps(typeof(β)))) + negβ = -β + returned_mat = mul!(copy(C), A, B, α, negβ) + exp_val = Ac * Bc * strongzero(α) + Cc * negβ + @test collect(returned_mat) ≈ exp_val rtol=rtol atol=atol + end + end +end + @testset "mul!(::$TC, ::$TA, ::$TB, α, β)" for (TC, TA, TB) in testdata if needsquare(TA) na1 = na2 = rand(sizecandidates) @@ -147,32 +170,29 @@ end bsize = (na2, nb2) csize = (na1, nb2) + C = _rand(TC, csize) + A = _rand(TA, asize) + B = _rand(TB, bsize) + Cc = Matrix(C) + Ac = Matrix(A) + Bc = Matrix(B) + @testset for α in Any[true, eltype(TC)(1), _rand(eltype(TC))], β in Any[false, eltype(TC)(0), _rand(eltype(TC))] - C = _rand(TC, csize) - A = _rand(TA, asize) - B = _rand(TB, bsize) # This is similar to how `isapprox` choose `rtol` (when # `atol=0`) but consider all number types involved: rtol = max(rtoldefault.(real.(eltype.((C, A, B))))..., rtoldefault.(real.(typeof.((α, β))))...) - Cc = copy(C) - Ac = Matrix(A) - Bc = Matrix(B) - returned_mat = mul!(C, A, B, α, β) - @test returned_mat === C - @test collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol + compare_matmul(C, A, B, α, β, rtol; Ac, Bc, Cc) y = C[:, 1] x = B[:, 1] yc = Vector(y) xc = Vector(x) - returned_vec = mul!(y, A, x, α, β) - @test returned_vec === y - @test collect(returned_vec) ≈ α * Ac * xc + β * yc rtol=rtol + compare_matmul(y, A, x, α, β, rtol; Ac, Bc=xc, Cc=yc) if TC <: Matrix @testset "adjoint and transpose" begin @@ -183,35 +203,24 @@ end Af = fa === identity ? A : fa(_rand(TA, reverse(asize))) Bf = fb === identity ? B : fb(_rand(TB, reverse(bsize))) - Ac = collect(Af) - Bc = collect(Bf) - Cc = collect(C) - - returned_mat = mul!(C, Af, Bf, α, β) - @test returned_mat === C - @test collect(returned_mat) ≈ α * Ac * Bc + β * Cc rtol=rtol + compare_matmul(C, Af, Bf, α, β, rtol) end end end if isnanfillable(C) @testset "β = 0 ignores C .= NaN" begin - parent(C) .= NaN - Ac = Matrix(A) - Bc = Matrix(B) - returned_mat = mul!(C, A, B, α, zero(eltype(C))) - @test returned_mat === C - @test collect(returned_mat) ≈ α * Ac * Bc rtol=rtol + Ccopy = copy(C) + parent(Ccopy) .= NaN + compare_matmul(Ccopy, A, B, α, zero(eltype(C)), rtol; Ac, Bc, Cc) end end if isnanfillable(A) @testset "α = 0 ignores A .= NaN" begin - parent(A) .= NaN - Cc = copy(C) - returned_mat = mul!(C, A, B, zero(eltype(A)), β) - @test returned_mat === C - @test collect(returned_mat) ≈ β * Cc rtol=rtol + Acopy = copy(A) + parent(Acopy) .= NaN + compare_matmul(C, Acopy, B, zero(eltype(A)), β, rtol; Ac, Bc, Cc) end end end From c6e7f83a6b6e9b7ec34a083983ab812278fa74a5 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 31 Oct 2024 06:49:58 +0100 Subject: [PATCH 528/548] Export jl_gc_new_weakref again via julia.h (#56373) This is how it used for at least Julia 1.0 - 1.11 Closes #56367 --- src/gc-common.h | 9 --------- src/julia.h | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/gc-common.h b/src/gc-common.h index bbac5f30c6755..32b7470b13a58 100644 --- a/src/gc-common.h +++ b/src/gc-common.h @@ -185,13 +185,4 @@ extern jl_ptls_t* gc_all_tls_states; extern int gc_logging_enabled; -// =========================================================================== // -// Misc -// =========================================================================== // - -// Allocates a new weak-reference, assigns its value and increments Julia allocation -// counters. If thread-local allocators are used, then this function should allocate in the -// thread-local allocator of the current thread. -JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value); - #endif // JL_GC_COMMON_H diff --git a/src/julia.h b/src/julia.h index 1d36dba519700..ffd669cd828a4 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1128,6 +1128,11 @@ JL_DLLEXPORT void jl_finalize(jl_value_t *o); JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, struct _jl_task_t *owner) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz); +// Allocates a new weak-reference, assigns its value and increments Julia allocation +// counters. If thread-local allocators are used, then this function should allocate in the +// thread-local allocator of the current thread. +JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value); + // GC write barriers STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT From 2a24b8f4425560f8a1a53fe11397fbf4bf5a41d9 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:53:17 +0900 Subject: [PATCH 529/548] InteractiveUtils: define `InteractiveUtils.@code_ircode` (#56390) --- stdlib/InteractiveUtils/src/macros.jl | 10 +++++++++- stdlib/InteractiveUtils/test/runtests.jl | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index e338d8626fb0f..0c272940b82d9 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -2,7 +2,7 @@ # macro wrappers for various reflection functions -import Base: typesof, insert!, replace_ref_begin_end!, infer_effects +import Base: typesof, insert!, replace_ref_begin_end!, infer_effects, code_ircode # defined in Base so it's possible to time all imports, including InteractiveUtils and its deps # via. `Base.@time_imports` etc. @@ -249,6 +249,14 @@ macro code_lowered(ex0...) end end +macro code_ircode(ex0...) + thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_ircode, ex0) + quote + local results = $thecall + length(results) == 1 ? results[1] : results + end +end + """ @functionloc diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index e729ae67bde19..bb64153818c1d 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -819,6 +819,8 @@ end end @test Base.infer_effects(sin, (Int,)) == InteractiveUtils.@infer_effects sin(42) +@test first(InteractiveUtils.@code_ircode sin(42)) isa Core.Compiler.IRCode +@test first(InteractiveUtils.@code_ircode optimize_until="Inlining" sin(42)) isa Core.Compiler.IRCode @testset "Docstrings" begin @test isempty(Docs.undocumented_names(InteractiveUtils)) From f8c6d1c106648f53874c9b0ebee1ae7382766dd0 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 31 Oct 2024 13:21:46 -0400 Subject: [PATCH 530/548] Fix some missing write barriers and add some helpful comments (#56396) I was trying some performance optimization which didn't end up working out, but in the process I found two missing write barriers and added some helpful comments for future readers, so that part is probably still useful. --- base/runtime_internals.jl | 2 +- src/gc-interface.h | 5 +++++ src/julia.h | 6 +++--- src/module.c | 10 +++++++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index ab867f8fcae6d..dd526a24d6494 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -248,7 +248,7 @@ binding_kind(m::Module, s::Symbol) = binding_kind(lookup_binding_partition(tls_w delete_binding(mod::Module, sym::Symbol) Force the binding `mod.sym` to be undefined again, allowing it be redefined. -Note that this operation is very expensive, requirinig a full scan of all code in the system, +Note that this operation is very expensive, requiring a full scan of all code in the system, as well as potential recompilation of any methods that (may) have used binding information. diff --git a/src/gc-interface.h b/src/gc-interface.h index 0b5df17a3b8c5..eb6687d52d9ab 100644 --- a/src/gc-interface.h +++ b/src/gc-interface.h @@ -192,6 +192,11 @@ JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align, // object header must be included in the object size. This object is allocated in an // immortal region that is never swept. The second parameter specifies the type of the // object being allocated and will be used to set the object header. +// +// !!! warning: Because permanently allocated objects are not swept, the GC will not +// necessarily mark any objects that would have ordinarily been rooted by +// the allocated object. All objects stored in fields of this object +// must be either permanently allocated or have other roots. struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT; // ========================================================================= // diff --git a/src/julia.h b/src/julia.h index ffd669cd828a4..a710192d5756c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1138,15 +1138,15 @@ JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value); STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT { // parent and ptr isa jl_value_t* - if (__unlikely(jl_astaggedvalue(parent)->bits.gc == 3 && // parent is old and not in remset - (jl_astaggedvalue(ptr)->bits.gc & 1) == 0)) // ptr is young + if (__unlikely(jl_astaggedvalue(parent)->bits.gc == 3 /* GC_OLD_MARKED */ && // parent is old and not in remset + (jl_astaggedvalue(ptr)->bits.gc & 1 /* GC_MARKED */) == 0)) // ptr is young jl_gc_queue_root((jl_value_t*)parent); } STATIC_INLINE void jl_gc_wb_back(const void *ptr) JL_NOTSAFEPOINT // ptr isa jl_value_t* { // if ptr is old - if (__unlikely(jl_astaggedvalue(ptr)->bits.gc == 3)) { + if (__unlikely(jl_astaggedvalue(ptr)->bits.gc == 3 /* GC_OLD_MARKED */)) { jl_gc_queue_root((jl_value_t*)ptr); } } diff --git a/src/module.c b/src/module.c index 9b4d26cc7b000..bdacd487e978d 100644 --- a/src/module.c +++ b/src/module.c @@ -37,6 +37,7 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) _Atomic(jl_binding_partition_t *)*insert = &b->partitions; jl_binding_partition_t *bpart = jl_atomic_load_relaxed(insert); size_t max_world = (size_t)-1; + jl_binding_partition_t *new_bpart = NULL; while (1) { while (bpart && world < bpart->min_world) { insert = &bpart->next; @@ -46,11 +47,11 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) } if (bpart && world <= jl_atomic_load_relaxed(&bpart->max_world)) return bpart; - jl_binding_partition_t *new_bpart = new_binding_partition(); + if (!new_bpart) + new_bpart = new_binding_partition(); jl_atomic_store_relaxed(&new_bpart->next, bpart); jl_gc_wb_fresh(new_bpart, bpart); - if (bpart) - new_bpart->min_world = jl_atomic_load_relaxed(&bpart->max_world) + 1; + new_bpart->min_world = bpart ? jl_atomic_load_relaxed(&bpart->max_world) + 1 : 0; jl_atomic_store_relaxed(&new_bpart->max_world, max_world); if (jl_atomic_cmpswap(insert, &bpart, new_bpart)) { jl_gc_wb(parent, new_bpart); @@ -548,6 +549,7 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * // changing, for example if this var is assigned to later. if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction((jl_value_t*)b2, BINDING_KIND_IMPLICIT))) goto retry; + jl_gc_wb(bpart, b2); if (b2->deprecated) { b->deprecated = 1; // we will warn about this below, but we might want to warn at the use sites too if (m != jl_main_module && m != jl_base_module && @@ -740,6 +742,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)b, (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) goto retry; + jl_gc_wb(btopart, b); bto->deprecated |= b->deprecated; // we already warned about this above, but we might want to warn at the use sites too } else { @@ -749,6 +752,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_ptr_kind_union_t new_pku = encode_restriction(decode_restriction_value(bto_pku), (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) goto retry; + // No wb, because the value is unchanged } } else if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { From da74ef1933b12410b217748e0f7fbcbe52e10d29 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 31 Oct 2024 14:15:39 -0400 Subject: [PATCH 531/548] compiler: fix specialization mistake introduced by #40985 (#56404) Hopefully there aren't any others like this hiding around? Not useful to make a new closure for every method that we inline, since we just called `===` inside it --- base/compiler/ssair/inlining.jl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 5017b619469ff..dfdd317f74d87 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -939,7 +939,6 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, allow_typevars::Bool, invokesig::Union{Nothing,Vector{Any}}=nothing, volatile_inf_result::Union{Nothing,VolatileInferenceResult}=nothing) method = match.method - spec_types = match.spec_types # Check that we have the correct number of arguments na = Int(method.nargs) @@ -954,6 +953,7 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, if !match.fully_covers # type-intersection was not able to give us a simple list of types, so # ir_inline_unionsplit won't be able to deal with inlining this + spec_types = match.spec_types if !(spec_types isa DataType && length(spec_types.parameters) == npassedargs && !isvarargtype(spec_types.parameters[end])) return nothing @@ -1428,14 +1428,13 @@ function handle_match!(cases::Vector{InliningCase}, match::MethodMatch, argtypes::Vector{Any}, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState; allow_typevars::Bool, volatile_inf_result::Union{Nothing,VolatileInferenceResult}) - spec_types = match.spec_types # We may see duplicated dispatch signatures here when a signature gets widened # during abstract interpretation: for the purpose of inlining, we can just skip # processing this dispatch candidate (unless unmatched type parameters are present) - !allow_typevars && any(case::InliningCase->case.sig === spec_types, cases) && return true + !allow_typevars && any(case::InliningCase->case.sig === match.spec_types, cases) && return true item = analyze_method!(match, argtypes, info, flag, state; allow_typevars, volatile_inf_result) item === nothing && return false - push!(cases, InliningCase(spec_types, item)) + push!(cases, InliningCase(match.spec_types, item)) return true end @@ -1443,13 +1442,12 @@ function handle_const_prop_result!(cases::Vector{InliningCase}, result::ConstPro match::MethodMatch, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState; allow_typevars::Bool) mi = result.result.linfo - spec_types = match.spec_types if !validate_sparams(mi.sparam_vals) (allow_typevars && !may_have_fcalls(mi.def::Method)) || return false end item = resolve_todo(mi, result.result, info, flag, state) item === nothing && return false - push!(cases, InliningCase(spec_types, item)) + push!(cases, InliningCase(match.spec_types, item)) return true end @@ -1479,11 +1477,10 @@ end function handle_semi_concrete_result!(cases::Vector{InliningCase}, result::SemiConcreteResult, match::MethodMatch, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState) mi = result.mi - spec_types = match.spec_types validate_sparams(mi.sparam_vals) || return false item = semiconcrete_result_item(result, info, flag, state) item === nothing && return false - push!(cases, InliningCase(spec_types, item)) + push!(cases, InliningCase(match.spec_types, item)) return true end From 7715cf287a9920ba86cf7405f636b18b85eede47 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 31 Oct 2024 19:07:10 -0400 Subject: [PATCH 532/548] Avoid racy double-load of binding restriction in `import_module` (#56395) Fixes #56333 --- src/toplevel.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/toplevel.c b/src/toplevel.c index c2fbc38d067eb..6dcab3095e320 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -665,11 +665,16 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym jl_sym_t *name = asname ? asname : import->name; // TODO: this is a bit race-y with what error message we might print jl_binding_t *b = jl_get_module_binding(m, name, 1); - if (jl_get_binding_value_if_const(b) == (jl_value_t*)import) - return; jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (decode_restriction_kind(pku) != BINDING_KIND_GUARD && decode_restriction_kind(pku) != BINDING_KIND_FAILED) { + // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. + pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (decode_restriction_kind(pku) == BINDING_KIND_CONST || decode_restriction_kind(pku) == BINDING_KIND_CONST_IMPORT) { + // Already declared (e.g. on another thread) or imported. + if (decode_restriction_value(pku) == (jl_value_t*)import) + return; + } jl_errorf("importing %s into %s conflicts with an existing global", jl_symbol_name(name), jl_symbol_name(m->name)); } From 040174c446ba63b89a13b6de267fced9ad270848 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:56:36 +0900 Subject: [PATCH 533/548] define `InteractiveUtils.@infer_[return|exception]_type` (#56398) Also simplifies the definitions of `@code_typed` and the other similar macros. --- stdlib/InteractiveUtils/src/macros.jl | 40 ++++++++---------------- stdlib/InteractiveUtils/test/runtests.jl | 2 ++ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 0c272940b82d9..a21bf30dbcd6c 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -2,7 +2,8 @@ # macro wrappers for various reflection functions -import Base: typesof, insert!, replace_ref_begin_end!, infer_effects, code_ircode +using Base: typesof, insert!, replace_ref_begin_end!, + infer_return_type, infer_exception_type, infer_effects, code_ircode # defined in Base so it's possible to time all imports, including InteractiveUtils and its deps # via. `Base.@time_imports` etc. @@ -225,35 +226,20 @@ macro which(ex0::Symbol) return :(which($__module__, $ex0)) end -for fname in [:code_warntype, :code_llvm, :code_native, :infer_effects] - @eval begin - macro ($fname)(ex0...) - gen_call_with_extracted_types_and_kwargs(__module__, $(Expr(:quote, fname)), ex0) - end - end -end - -macro code_typed(ex0...) - thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_typed, ex0) - quote - local results = $thecall - length(results) == 1 ? results[1] : results +for fname in [:code_warntype, :code_llvm, :code_native, + :infer_return_type, :infer_effects, :infer_exception_type] + @eval macro ($fname)(ex0...) + gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0) end end -macro code_lowered(ex0...) - thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_lowered, ex0) - quote - local results = $thecall - length(results) == 1 ? results[1] : results - end -end - -macro code_ircode(ex0...) - thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_ircode, ex0) - quote - local results = $thecall - length(results) == 1 ? results[1] : results +for fname in [:code_typed, :code_lowered, :code_ircode] + @eval macro ($fname)(ex0...) + thecall = gen_call_with_extracted_types_and_kwargs(__module__, $(QuoteNode(fname)), ex0) + quote + local results = $thecall + length(results) == 1 ? results[1] : results + end end end diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index bb64153818c1d..0de67fea69dea 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -819,6 +819,8 @@ end end @test Base.infer_effects(sin, (Int,)) == InteractiveUtils.@infer_effects sin(42) +@test Base.infer_return_type(sin, (Int,)) == InteractiveUtils.@infer_return_type sin(42) +@test Base.infer_exception_type(sin, (Int,)) == InteractiveUtils.@infer_exception_type sin(42) @test first(InteractiveUtils.@code_ircode sin(42)) isa Core.Compiler.IRCode @test first(InteractiveUtils.@code_ircode optimize_until="Inlining" sin(42)) isa Core.Compiler.IRCode From 671e1d87595d706ec2fa1659d990c507596f8fbc Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:02:11 +0900 Subject: [PATCH 534/548] irinterp: set `IR_FLAG_REFINED` for narrowed `PhiNode`s (#56391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `adce_pass!` can transform a `Union`-type `PhiNode` into a narrower `PhiNode`, but in such cases, the `IR_FLAG_REFINED` flag isn’t set on that `PhiNode` statement. By setting this flag, irinterp can perform statement reprocessing using the narrowed `PhiNode`, enabling type stability in cases like JuliaLang/julia#56387. - fixes JuliaLang/julia#56387 --- base/compiler/ssair/passes.jl | 12 +++++++----- test/compiler/inference.jl | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index e3f294c4e91fe..4ad5dcfb2a3c8 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -2114,18 +2114,19 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) unionphi = unionphis[i] phi = unionphi[1] t = unionphi[2] + inst = compact.result[phi] if t === Union{} - stmt = compact[SSAValue(phi)][:stmt]::PhiNode + stmt = inst[:stmt]::PhiNode kill_phi!(compact, phi_uses, 1:length(stmt.values), SSAValue(phi), stmt, true) made_changes = true continue elseif t === Any continue - elseif ⊑(𝕃ₒ, compact.result[phi][:type], t) - continue end + ⊏ = strictpartialorder(𝕃ₒ) + t ⊏ inst[:type] || continue to_drop = Int[] - stmt = compact[SSAValue(phi)][:stmt] + stmt = inst[:stmt] stmt === nothing && continue stmt = stmt::PhiNode for i = 1:length(stmt.values) @@ -2137,7 +2138,8 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) push!(to_drop, i) end end - compact.result[phi][:type] = t + inst[:type] = t + add_flag!(inst, IR_FLAG_REFINED) # t ⊏ inst[:type] kill_phi!(compact, phi_uses, to_drop, SSAValue(phi), stmt, false) made_changes = true end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index dab8e57aa2309..2fc7e917186f4 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -6078,3 +6078,15 @@ let src = code_typed((Union{Nothing,AtomicModifySafety},)) do x end |> only |> first @test any(@nospecialize(x)->Meta.isexpr(x, :invoke_modify), src.code) end + +function issue56387(nt::NamedTuple, field::Symbol=:a) + NT = typeof(nt) + names = fieldnames(NT) + types = fieldtypes(NT) + index = findfirst(==(field), names) + if index === nothing + throw(ArgumentError("Field $field not found")) + end + types[index] +end +@test Base.infer_return_type(issue56387, (typeof((;a=1)),)) == Type{Int} From dc57caf2470fcdc21a9bb11c0f97924577d7c5c8 Mon Sep 17 00:00:00 2001 From: Micah Rufsvold <86363075+mrufsvold@users.noreply.github.com> Date: Fri, 1 Nov 2024 02:03:42 -0400 Subject: [PATCH 535/548] document isopen(::Channel) (#56376) This PR has two purposes -- 1) Add some documentation for public API 2) Add a small note about a footgun I've hit a few times: `!isopen(ch)` does not mean that you are "done" with the channel because buffered channels can still have items left in them that need to be taken. --------- Co-authored-by: CY Han --- base/channels.jl | 50 ++++++++++++++++++++++++++++++++++++---- doc/src/base/parallel.md | 1 + 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/base/channels.jl b/base/channels.jl index 3acbf37246a58..8882171095e7a 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -212,11 +212,51 @@ function close(c::Channel, @nospecialize(excp::Exception)) nothing end -# Use acquire here to pair with release store in `close`, so that subsequent `isready` calls -# are forced to see `isready == true` if they see `isopen == false`. This means users must -# call `isopen` before `isready` if you are using the race-y APIs (or call `iterate`, which -# does this right for you). -isopen(c::Channel) = ((@atomic :acquire c.state) === :open) +""" + isopen(c::Channel) +Determines whether a [`Channel`](@ref) is open for new [`put!`](@ref) operations. +Notice that a `Channel`` can be closed and still have +buffered elements which can be consumed with [`take!`](@ref). + +# Examples + +Buffered channel with task: +```jldoctest +julia> c = Channel(ch -> put!(ch, 1), 1); + +julia> isopen(c) # The channel is closed to new `put!`s +false + +julia> isready(c) # The channel is closed but still contains elements +true + +julia> take!(c) +1 + +julia> isready(c) +false +``` + +Unbuffered channel: +```jldoctest +julia> c = Channel{Int}(); + +julia> isopen(c) +true + +julia> close(c) + +julia> isopen(c) +false +``` +""" +function isopen(c::Channel) + # Use acquire here to pair with release store in `close`, so that subsequent `isready` calls + # are forced to see `isready == true` if they see `isopen == false`. This means users must + # call `isopen` before `isready` if you are using the race-y APIs (or call `iterate`, which + # does this right for you). + return ((@atomic :acquire c.state) === :open) +end """ empty!(c::Channel) diff --git a/doc/src/base/parallel.md b/doc/src/base/parallel.md index 9f24db176b538..cd5c95f17994a 100644 --- a/doc/src/base/parallel.md +++ b/doc/src/base/parallel.md @@ -67,6 +67,7 @@ Base.put!(::Channel, ::Any) Base.take!(::Channel) Base.isfull(::Channel) Base.isready(::Channel) +Base.isopen(::Channel) Base.fetch(::Channel) Base.close(::Channel) Base.bind(c::Channel, task::Task) From 706a4f6c5d159366bed25e8217ce80748e3963fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 1 Nov 2024 06:13:06 +0000 Subject: [PATCH 536/548] Make build system respect `FORCE_COLOR` and `NO_COLOR` settings (#56346) Follow up to #53742, but for the build system. CC: @omus. --- Make.inc | 63 ++++++++++++++++++++++++++++++++++++++++++-------------- Makefile | 2 +- NEWS.md | 4 ++-- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/Make.inc b/Make.inc index cb79e3ca1b5a9..9f6535ae05885 100644 --- a/Make.inc +++ b/Make.inc @@ -118,6 +118,51 @@ SPACE:=$(eval) $(eval) export LC_ALL=C export LANG=C +# Respect `FORCE_COLOR` environment variable: . +ifndef FORCE_COLOR +FORCE_COLOR := "" +endif + +# Respect `NO_COLOR` environment variable: . +ifndef NO_COLOR +NO_COLOR := "" +endif + +# When both `FORCE_COLOR` and `NO_COLOR` are defined, the former has precedence. +ifneq ($(FORCE_COLOR), "") +NO_COLOR = "" +endif + +WARNCOLOR:="\033[33;1m" +ENDCOLOR:="\033[0m" + +CCCOLOR:="\033[34m" +LINKCOLOR:="\033[34;1m" +PERLCOLOR:="\033[35m" +FLISPCOLOR:="\033[32m" +JULIACOLOR:="\033[32;1m" +DTRACECOLOR:="\033[32;1m" + +SRCCOLOR:="\033[33m" +BINCOLOR:="\033[37;1m" +JULCOLOR:="\033[34;1m" + +ifneq ($(NO_COLOR), "") +WARNCOLOR:="" +ENDCOLOR:="" + +CCCOLOR:="" +LINKCOLOR:="" +PERLCOLOR:="" +FLISPCOLOR:="" +JULIACOLOR:="" +DTRACECOLOR:="" + +SRCCOLOR:="" +BINCOLOR:="" +JULCOLOR:="" +endif + # We need python for things like BB triplet recognition and relative path computation. # We don't really care about version, generally, so just find something that works: PYTHON := "$(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo "{python|python3|python2} not found")" @@ -140,7 +185,7 @@ ifeq ($(BUILDROOT),) ifeq ("$(origin O)", "command line") BUILDROOT := $(abspath $O) BUILDDIR := $(abspath $(BUILDROOT)/$(call rel_path,$(JULIAHOME),$(SRCDIR))) - $(info $(shell printf '\033[32;1mBuilding into $(BUILDROOT)\033[0m')) # use printf to expand the escape sequences + $(info $(shell printf '$(JULIACOLOR)Building into $(BUILDROOT)$(ENDCOLOR)')) # use printf to expand the escape sequences else BUILDROOT:=$(JULIAHOME) endif @@ -1759,24 +1804,10 @@ ifndef VERBOSE VERBOSE := 0 endif -WARNCOLOR:="\033[33;1m" -ENDCOLOR:="\033[0m" - ifeq ($(VERBOSE), 0) QUIET_MAKE = -s -CCCOLOR:="\033[34m" -LINKCOLOR:="\033[34;1m" -PERLCOLOR:="\033[35m" -FLISPCOLOR:="\033[32m" -JULIACOLOR:="\033[32;1m" -DTRACECOLOR:="\033[32;1m" - -SRCCOLOR:="\033[33m" -BINCOLOR:="\033[37;1m" -JULCOLOR:="\033[34;1m" - GOAL=$(subst ','\'',$(subst $(abspath $(JULIAHOME))/,,$(abspath $@))) PRINT_CC = printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$(GOAL)$(ENDCOLOR); $(1) @@ -1797,7 +1828,7 @@ PRINT_FLISP = echo '$(subst ','\'',$(1))'; $(1) PRINT_JULIA = echo '$(subst ','\'',$(1))'; $(1) PRINT_DTRACE = echo '$(subst ','\'',$(1))'; $(1) -endif +endif # VERBOSE # Makefile debugging trick: # call print-VARIABLE to see the runtime value of any variable diff --git a/Makefile b/Makefile index 4fd8b878c5d1f..d1e5b31f85b1c 100644 --- a/Makefile +++ b/Makefile @@ -650,7 +650,7 @@ testall1: check-whitespace $(JULIA_BUILD_MODE) test-%: check-whitespace $(JULIA_BUILD_MODE) .FORCE @([ $$(( $$(date +%s) - $$(date -r $(build_private_libdir)/sys.$(SHLIB_EXT) +%s) )) -le 100 ] && \ - printf '\033[93m HINT The system image was recently rebuilt. Are you aware of the test-revise-* targets? See CONTRIBUTING.md. \033[0m\n') || true + printf '$(WARNCOLOR) HINT The system image was recently rebuilt. Are you aware of the test-revise-* targets? See CONTRIBUTING.md. $(ENDCOLOR)\n') || true @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test $* JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) test-revise-%: .FORCE diff --git a/NEWS.md b/NEWS.md index 5e066ffd9cdcf..ba9ca1c521c55 100644 --- a/NEWS.md +++ b/NEWS.md @@ -57,8 +57,8 @@ Command-line option changes * The `-m/--module` flag can be passed to run the `main` function inside a package with a set of arguments. This `main` function should be declared using `@main` to indicate that it is an entry point. * Enabling or disabling color text in Julia can now be controlled with the -[`NO_COLOR`](https://no-color.org/) or [`FORCE_COLOR`](https://force-color.org/) environment -variables. ([#53742]). + [`NO_COLOR`](https://no-color.org/) or [`FORCE_COLOR`](https://force-color.org/) environment + variables. These variables are also honored by Julia's build system ([#53742], [#56346]). * `--project=@temp` starts Julia with a temporary environment. * New `--trace-compile-timing` option to report how long each method reported by `--trace-compile` took to compile, in ms. ([#54662]) From 76351909e2daf7cb07cffe9fa76d3e8e4d38137f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 1 Nov 2024 06:15:16 -0400 Subject: [PATCH 537/548] Add `edges` vector to CodeInstance/CodeInfo to keep backedges as edges (#54894) Appears to add about 11MB (128MB to 139MB) to the system image, and to decrease the stdlib size by 55 MB (325MB to 270MB), so seems overall favorable right now. The edges are computed following the encoding to correctly reflect the backedges. Co-authored-by: Shuhei Kadowaki --- base/Base.jl | 1 - base/boot.jl | 6 +- base/compiler/abstractinterpretation.jl | 215 +++--- base/compiler/inferencestate.jl | 62 +- base/compiler/optimize.jl | 4 +- base/compiler/ssair/inlining.jl | 92 ++- base/compiler/ssair/irinterp.jl | 6 - base/compiler/ssair/passes.jl | 4 +- base/compiler/stmtinfo.jl | 220 ++++++- base/compiler/tfuncs.jl | 29 +- base/compiler/typeinfer.jl | 268 ++++---- base/compiler/types.jl | 53 +- base/compiler/utilities.jl | 37 +- base/expr.jl | 2 +- base/show.jl | 6 +- src/common_symbols1.inc | 2 - src/common_symbols2.inc | 4 +- src/gf.c | 126 ++-- src/ircode.c | 31 +- src/jltypes.c | 14 +- src/julia.h | 5 +- src/julia_internal.h | 15 +- src/method.c | 70 +- src/opaque_closure.c | 12 +- src/serialize.h | 90 +-- src/staticdata.c | 75 +-- src/staticdata_utils.c | 827 +++++++++--------------- src/toplevel.c | 2 +- stdlib/REPL/src/REPLCompletions.jl | 6 +- test/compiler/AbstractInterpreter.jl | 11 +- test/compiler/EscapeAnalysis/EAUtils.jl | 5 +- test/compiler/contextual.jl | 4 +- test/compiler/invalidation.jl | 11 +- test/core.jl | 2 +- test/precompile.jl | 32 +- test/precompile_absint1.jl | 10 +- test/precompile_absint2.jl | 17 +- test/stacktraces.jl | 5 +- 38 files changed, 1159 insertions(+), 1222 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index c5e318ffe5e38..3b56dca166cee 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -204,7 +204,6 @@ function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has return Core._hasmethod(tt) end - # core operations & types include("promotion.jl") include("tuple.jl") diff --git a/base/boot.jl b/base/boot.jl index ed3e22391f215..5d40191ecab21 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -535,11 +535,11 @@ function CodeInstance( mi::MethodInstance, owner, @nospecialize(rettype), @nospecialize(exctype), @nospecialize(inferred_const), @nospecialize(inferred), const_flags::Int32, min_world::UInt, max_world::UInt, effects::UInt32, @nospecialize(analysis_results), - relocatability::UInt8, edges::Union{DebugInfo,Nothing}) + relocatability::UInt8, di::Union{DebugInfo,Nothing}, edges::SimpleVector) return ccall(:jl_new_codeinst, Ref{CodeInstance}, - (Any, Any, Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, UInt8, Any), + (Any, Any, Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, UInt8, Any, Any), mi, owner, rettype, exctype, inferred_const, inferred, const_flags, min_world, max_world, - effects, analysis_results, relocatability, edges) + effects, analysis_results, relocatability, di, edges) end GlobalRef(m::Module, s::Symbol) = ccall(:jl_module_globalref, Ref{GlobalRef}, (Any, Any), m, s) Module(name::Symbol=:anonymous, std_imports::Bool=true, default_names::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool, Bool), name, std_imports, default_names) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 777240adf581b..e20b74454bb22 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -57,7 +57,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), gfresult = Future{CallMeta}() # intermediate work for computing gfresult rettype = exctype = Bottom - edges = MethodInstance[] conditionals = nothing # keeps refinement information of call argument types when the return type is boolean seenall = true const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available @@ -73,7 +72,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), napplicable = length(applicable) multiple_matches = napplicable > 1 while i <= napplicable - match = applicable[i]::MethodMatch + (; match, edges, edge_idx) = applicable[i] method = match.method sig = match.spec_types if bail_out_toplevel_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) @@ -95,7 +94,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), #end mresult = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv)::Future function handle1(interp, sv) - local (; rt, exct, edge, effects, volatile_inf_result) = mresult[] + local (; rt, exct, effects, edge, volatile_inf_result) = mresult[] this_conditional = ignorelimited(rt) this_rt = widenwrappedconditional(rt) this_exct = exct @@ -109,6 +108,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if const_call_result !== nothing this_const_conditional = ignorelimited(const_call_result.rt) this_const_rt = widenwrappedconditional(const_call_result.rt) + const_edge = nothing if this_const_rt ⊑ₚ this_rt # As long as the const-prop result we have is not *worse* than # what we found out on types, we'd like to use it. Even if the @@ -119,9 +119,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # e.g. in cases when there are cycles but cached result is still accurate this_conditional = this_const_conditional this_rt = this_const_rt - (; effects, const_result, edge) = const_call_result + (; effects, const_result, const_edge) = const_call_result elseif is_better_effects(const_call_result.effects, effects) - (; effects, const_result, edge) = const_call_result + (; effects, const_result, const_edge) = const_call_result else add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") end @@ -129,10 +129,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # because consistent-cy does not apply to exceptions. if const_call_result.exct ⋤ this_exct this_exct = const_call_result.exct - (; const_result, edge) = const_call_result + (; const_result, const_edge) = const_call_result else add_remark!(interp, sv, "[constprop] Discarded exception type because result was wider than inference") end + if const_edge !== nothing + edge = const_edge + end end all_effects = merge_effects(all_effects, effects) @@ -142,7 +145,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end const_results[i] = const_result end - edge === nothing || push!(edges, edge) @assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context" if can_propagate_conditional(this_conditional, argtypes) # The only case where we need to keep this in rt is where @@ -165,6 +167,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), conditionals[2][i] = conditionals[2][i] ⊔ᵢ cnd.elsetype end end + edges[edge_idx] = edge if i < napplicable && bail_out_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) add_remark!(interp, sv, "Call inference reached maximally imprecise information. Bailing on.") seenall = false @@ -172,7 +175,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end i += 1 return true - end + end # function handle1 if isready(mresult) && handle1(interp, sv) continue else @@ -208,7 +211,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if (isa(sv, InferenceState) && infer_compilation_signature(interp) && (seenall && 1 == napplicable) && rettype !== Any && rettype !== Bottom && !is_removable_if_unused(all_effects)) - match = applicable[1]::MethodMatch + (; match) = applicable[1] method = match.method sig = match.spec_types mi = specialize_method(match; preexisting=true) @@ -230,8 +233,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # and avoid keeping track of a more complex result type. rettype = Any end - any_slot_refined = slotrefinements !== nothing - add_call_backedges!(interp, rettype, all_effects, any_slot_refined, edges, matches, atype.contents, sv) if isa(sv, InferenceState) # TODO (#48913) implement a proper recursion handling for irinterp: # This works just because currently the `:terminate` condition guarantees that @@ -247,7 +248,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), gfresult[] = CallMeta(rettype, exctype, all_effects, info, slotrefinements) return true - end # infercalls + end # function infercalls # start making progress on the first call infercalls(interp, sv) || push!(sv.tasks, infercalls) return gfresult @@ -257,8 +258,14 @@ struct FailedMethodMatch reason::String end +struct MethodMatchTarget + match::MethodMatch + edges::Vector{Union{Nothing,CodeInstance}} + edge_idx::Int +end + struct MethodMatches - applicable::Vector{Any} + applicable::Vector{MethodMatchTarget} info::MethodMatchInfo valid_worlds::WorldRange end @@ -267,15 +274,9 @@ any_ambig(info::MethodMatchInfo) = any_ambig(info.results) any_ambig(m::MethodMatches) = any_ambig(m.info) fully_covering(info::MethodMatchInfo) = info.fullmatch fully_covering(m::MethodMatches) = fully_covering(m.info) -function add_uncovered_edges!(sv::AbsIntState, info::MethodMatchInfo, @nospecialize(atype)) - fully_covering(info) || add_mt_backedge!(sv, info.mt, atype) - nothing -end -add_uncovered_edges!(sv::AbsIntState, matches::MethodMatches, @nospecialize(atype)) = - add_uncovered_edges!(sv, matches.info, atype) struct UnionSplitMethodMatches - applicable::Vector{Any} + applicable::Vector{MethodMatchTarget} applicable_argtypes::Vector{Vector{Any}} info::UnionSplitInfo valid_worlds::WorldRange @@ -284,23 +285,14 @@ any_ambig(info::UnionSplitInfo) = any(any_ambig, info.split) any_ambig(m::UnionSplitMethodMatches) = any_ambig(m.info) fully_covering(info::UnionSplitInfo) = all(fully_covering, info.split) fully_covering(m::UnionSplitMethodMatches) = fully_covering(m.info) -function add_uncovered_edges!(sv::AbsIntState, info::UnionSplitInfo, @nospecialize(atype)) - all(fully_covering, info.split) && return nothing - # add mt backedges with removing duplications - for mt in uncovered_method_tables(info) - add_mt_backedge!(sv, mt, atype) - end -end -add_uncovered_edges!(sv::AbsIntState, matches::UnionSplitMethodMatches, @nospecialize(atype)) = - add_uncovered_edges!(sv, matches.info, atype) -function uncovered_method_tables(info::UnionSplitInfo) - mts = MethodTable[] + +nmatches(info::MethodMatchInfo) = length(info.results) +function nmatches(info::UnionSplitInfo) + n = 0 for mminfo in info.split - fully_covering(mminfo) && continue - any(mt′::MethodTable->mt′===mminfo.mt, mts) && continue - push!(mts, mminfo.mt) + n += nmatches(mminfo) end - return mts + return n end function find_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype); @@ -320,7 +312,7 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: @nospecialize(atype), max_methods::Int) split_argtypes = switchtupleunion(typeinf_lattice(interp), argtypes) infos = MethodMatchInfo[] - applicable = Any[] + applicable = MethodMatchTarget[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match valid_worlds = WorldRange() for i in 1:length(split_argtypes) @@ -333,14 +325,14 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: if thismatches === nothing return FailedMethodMatch("For one of the union split cases, too many methods matched") end - for m in thismatches - push!(applicable, m) - push!(applicable_argtypes, arg_n) - end valid_worlds = intersect(valid_worlds, thismatches.valid_worlds) thisfullmatch = any(match::MethodMatch->match.fully_covers, thismatches) - thisinfo = MethodMatchInfo(thismatches, mt, thisfullmatch) + thisinfo = MethodMatchInfo(thismatches, mt, sig_n, thisfullmatch) push!(infos, thisinfo) + for idx = 1:length(thismatches) + push!(applicable, MethodMatchTarget(thismatches[idx], thisinfo.edges, idx)) + push!(applicable_argtypes, arg_n) + end end info = UnionSplitInfo(infos) return UnionSplitMethodMatches( @@ -360,8 +352,9 @@ function find_simple_method_matches(interp::AbstractInterpreter, @nospecialize(a return FailedMethodMatch("Too many methods matched") end fullmatch = any(match::MethodMatch->match.fully_covers, matches) - info = MethodMatchInfo(matches, mt, fullmatch) - return MethodMatches(matches.matches, info, matches.valid_worlds) + info = MethodMatchInfo(matches, mt, atype, fullmatch) + applicable = MethodMatchTarget[MethodMatchTarget(matches[idx], info.edges, idx) for idx = 1:length(matches)] + return MethodMatches(applicable, info, matches.valid_worlds) end """ @@ -532,7 +525,7 @@ function conditional_argtype(𝕃ᵢ::AbstractLattice, @nospecialize(rt), @nospe end end -function collect_slot_refinements(𝕃ᵢ::AbstractLattice, applicable::Vector{Any}, +function collect_slot_refinements(𝕃ᵢ::AbstractLattice, applicable::Vector{MethodMatchTarget}, argtypes::Vector{Any}, fargs::Vector{Any}, sv::InferenceState) ⊏, ⊔ = strictpartialorder(𝕃ᵢ), join(𝕃ᵢ) slotrefinements = nothing @@ -546,7 +539,7 @@ function collect_slot_refinements(𝕃ᵢ::AbstractLattice, applicable::Vector{A end sigt = Bottom for j = 1:length(applicable) - match = applicable[j]::MethodMatch + (;match) = applicable[j] valid_as_lattice(match.spec_types, true) || continue sigt = sigt ⊔ fieldtype(match.spec_types, i) end @@ -561,31 +554,6 @@ function collect_slot_refinements(𝕃ᵢ::AbstractLattice, applicable::Vector{A return slotrefinements end -function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype), - all_effects::Effects, any_slot_refined::Bool, edges::Vector{MethodInstance}, - matches::Union{MethodMatches,UnionSplitMethodMatches}, @nospecialize(atype), - sv::AbsIntState) - # don't bother to add backedges when both type and effects information are already - # maximized to the top since a new method couldn't refine or widen them anyway - if rettype === Any - # 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=ALWAYS_FALSE) - end - if all_effects === Effects() && !any_slot_refined - return nothing - end - end - for edge in edges - add_backedge!(sv, edge) - end - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - add_uncovered_edges!(sv, matches, atype) - return nothing -end - const RECURSION_UNUSED_MSG = "Bounded recursion detected with unused result. Annotated return type may be wider than true result." const RECURSION_MSG = "Bounded recursion detected. Call was widened to force convergence." const RECURSION_MSG_HARDLIMIT = "Bounded recursion detected under hardlimit. Call was widened to force convergence." @@ -595,9 +563,9 @@ function abstract_call_method(interp::AbstractInterpreter, hardlimit::Bool, si::StmtInfo, sv::AbsIntState) sigtuple = unwrap_unionall(sig) sigtuple isa DataType || - return Future(MethodCallResult(Any, Any, false, false, nothing, Effects())) + return Future(MethodCallResult(Any, Any, Effects(), nothing, false, false)) all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) || - return Future(MethodCallResult(Union{}, Any, false, false, nothing, EFFECTS_THROWS)) # catch bad type intersections early + return Future(MethodCallResult(Union{}, Any, EFFECTS_THROWS, nothing, false, false)) # catch bad type intersections early if is_nospecializeinfer(method) sig = get_nospecializeinfer_sig(method, sig, sparams) @@ -622,7 +590,7 @@ function abstract_call_method(interp::AbstractInterpreter, # we have a self-cycle in the call-graph, but not in the inference graph (typically): # break this edge now (before we record it) by returning early # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) - return Future(MethodCallResult(Any, Any, true, true, nothing, Effects())) + return Future(MethodCallResult(Any, Any, Effects(), nothing, true, true)) end topmost = nothing edgecycle = true @@ -677,7 +645,7 @@ function abstract_call_method(interp::AbstractInterpreter, # since it's very unlikely that we'll try to inline this, # or want make an invoke edge to its calling convention return type. # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) - return Future(MethodCallResult(Any, Any, true, true, nothing, Effects())) + return Future(MethodCallResult(Any, Any, Effects(), nothing, true, true)) end add_remark!(interp, sv, washardlimit ? RECURSION_MSG_HARDLIMIT : RECURSION_MSG) # TODO (#48913) implement a proper recursion handling for irinterp: @@ -803,9 +771,9 @@ function matches_sv(parent::AbsIntState, sv::AbsIntState) method_for_inference_limit_heuristics(sv) === method_for_inference_limit_heuristics(parent)) end -function is_edge_recursed(edge::MethodInstance, caller::AbsIntState) +function is_edge_recursed(edge::CodeInstance, caller::AbsIntState) return any(AbsIntStackUnwind(caller)) do sv::AbsIntState - return edge === frame_instance(sv) + return edge.def === frame_instance(sv) end end @@ -832,18 +800,15 @@ end struct MethodCallResult rt exct + effects::Effects + edge::Union{Nothing,CodeInstance} edgecycle::Bool edgelimited::Bool - edge::Union{Nothing,MethodInstance} - effects::Effects volatile_inf_result::Union{Nothing,VolatileInferenceResult} - function MethodCallResult(@nospecialize(rt), @nospecialize(exct), - edgecycle::Bool, - edgelimited::Bool, - edge::Union{Nothing,MethodInstance}, - effects::Effects, + function MethodCallResult(@nospecialize(rt), @nospecialize(exct), effects::Effects, + edge::Union{Nothing,CodeInstance}, edgecycle::Bool, edgelimited::Bool, volatile_inf_result::Union{Nothing,VolatileInferenceResult}=nothing) - return new(rt, exct, edgecycle, edgelimited, edge, effects, volatile_inf_result) + return new(rt, exct, effects, edge, edgecycle, edgelimited, volatile_inf_result) end end @@ -853,18 +818,17 @@ struct InvokeCall InvokeCall(@nospecialize(types), @nospecialize(lookupsig)) = new(types, lookupsig) end -struct ConstCallResults +struct ConstCallResult rt::Any exct::Any const_result::ConstResult effects::Effects - edge::MethodInstance - function ConstCallResults( + const_edge::Union{Nothing,CodeInstance} + function ConstCallResult( @nospecialize(rt), @nospecialize(exct), - const_result::ConstResult, - effects::Effects, - edge::MethodInstance) - return new(rt, exct, const_result, effects, edge) + const_result::ConstResult, effects::Effects, + const_edge::Union{Nothing,CodeInstance}) + return new(rt, exct, const_result, effects, const_edge) end end @@ -947,8 +911,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter, return :none end end - mi = result.edge - if mi !== nothing && is_foldable(effects, #=check_rtcall=#true) + if result.edge !== nothing && is_foldable(effects, #=check_rtcall=#true) if f !== nothing && is_all_const_arg(arginfo, #=start=#2) if (is_nonoverlayed(interp) || is_nonoverlayed(effects) || # Even if overlay methods are involved, when `:consistent_overlay` is @@ -1010,15 +973,17 @@ function concrete_eval_call(interp::AbstractInterpreter, f = invoke end world = get_inference_world(interp) - edge = result.edge::MethodInstance + edge = result.edge::CodeInstance value = try Core._call_in_world_total(world, f, args...) catch e # The evaluation threw. By :consistent-cy, we're guaranteed this would have happened at runtime. # Howevever, at present, :consistency does not mandate the type of the exception - return ConstCallResults(Bottom, Any, ConcreteResult(edge, result.effects), result.effects, edge) + concrete_result = ConcreteResult(edge, result.effects) + return ConstCallResult(Bottom, Any, concrete_result, result.effects, #=const_edge=#nothing) end - return ConstCallResults(Const(value), Union{}, ConcreteResult(edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL, edge) + concrete_result = ConcreteResult(edge, EFFECTS_TOTAL, value) + return ConstCallResult(Const(value), Bottom, concrete_result, EFFECTS_TOTAL, #=const_edge=#nothing) end # check if there is a cycle and duplicated inference of `mi` @@ -1262,9 +1227,9 @@ function semi_concrete_eval_call(interp::AbstractInterpreter, mi::MethodInstance, result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState) world = frame_world(sv) mi_cache = WorldView(code_cache(interp), world) - code = get(mi_cache, mi, nothing) - if code !== nothing - irsv = IRInterpretationState(interp, code, mi, arginfo.argtypes, world) + codeinst = get(mi_cache, mi, nothing) + if codeinst !== nothing + irsv = IRInterpretationState(interp, codeinst, mi, arginfo.argtypes, world) if irsv !== nothing assign_parentchild!(irsv, sv) rt, (nothrow, noub) = ir_abstract_constant_propagation(interp, irsv) @@ -1283,16 +1248,21 @@ function semi_concrete_eval_call(interp::AbstractInterpreter, effects = Effects(effects; noub=ALWAYS_TRUE) end exct = refine_exception_type(result.exct, effects) - return ConstCallResults(rt, exct, SemiConcreteResult(mi, ir, effects, spec_info(irsv)), effects, mi) + semi_concrete_result = SemiConcreteResult(codeinst, ir, effects, spec_info(irsv)) + const_edge = nothing # TODO use the edges from irsv? + return ConstCallResult(rt, exct, semi_concrete_result, effects, const_edge) end end end return nothing end -const_prop_result(inf_result::InferenceResult) = - ConstCallResults(inf_result.result, inf_result.exc_result, ConstPropResult(inf_result), - inf_result.ipo_effects, inf_result.linfo) +function const_prop_result(inf_result::InferenceResult) + @assert isdefined(inf_result, :ci_as_edge) "InferenceResult without ci_as_edge" + const_prop_result = ConstPropResult(inf_result) + return ConstCallResult(inf_result.result, inf_result.exc_result, const_prop_result, + inf_result.ipo_effects, inf_result.ci_as_edge) +end # return cached result of constant analysis return_localcache_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) = @@ -1305,7 +1275,7 @@ end function const_prop_call(interp::AbstractInterpreter, mi::MethodInstance, result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState, - concrete_eval_result::Union{Nothing, ConstCallResults}=nothing) + concrete_eval_result::Union{Nothing,ConstCallResult}=nothing) inf_cache = get_inference_cache(interp) 𝕃ᵢ = typeinf_lattice(interp) forwarded_argtypes = compute_forwarded_argtypes(interp, arginfo, sv) @@ -1353,6 +1323,7 @@ function const_prop_call(interp::AbstractInterpreter, pop!(callstack) return nothing end + inf_result.ci_as_edge = codeinst_as_edge(interp, frame) @assert frame.frameid != 0 && frame.cycleid == frame.frameid @assert frame.parentid == sv.frameid @assert inf_result.result !== nothing @@ -1691,7 +1662,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n end iterateresult[] = AbstractIterationResult(ret, AbstractIterationInfo(calls, false)) return true - end # inferiterate_2arg + end # function inferiterate_2arg # continue making progress as much as possible, on iterate(arg, state) inferiterate_2arg(interp, sv) || push!(sv.tasks, inferiterate_2arg) return true @@ -1861,7 +1832,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: # For now, only propagate info if we don't also union-split the iteration applyresult[] = CallMeta(res, exctype, all_effects, retinfo) return true - end + end # function infercalls # start making progress on the first call infercalls(interp, sv) || push!(sv.tasks, infercalls) return applyresult @@ -2230,7 +2201,7 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt mresult = abstract_call_method(interp, method, ti, env, false, si, sv)::Future match = MethodMatch(ti, env, method, argtype <: method.sig) return Future{CallMeta}(mresult, interp, sv) do result, interp, sv - (; rt, exct, edge, effects, volatile_inf_result) = result + (; rt, exct, effects, edge, volatile_inf_result) = result res = nothing sig = match.spec_types argtypes′ = invoke_rewrite(argtypes) @@ -2250,16 +2221,19 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt result, f, arginfo, si, match, sv, invokecall) const_result = volatile_inf_result if const_call_result !== nothing + const_edge = nothing if const_call_result.rt ⊑ rt - (; rt, effects, const_result, edge) = const_call_result + (; rt, effects, const_result, const_edge) = const_call_result end if const_call_result.exct ⋤ exct - (; exct, const_result, edge) = const_call_result + (; exct, const_result, const_edge) = const_call_result + end + if const_edge !== nothing + edge = const_edge end end rt = from_interprocedural!(interp, rt, sv, arginfo, sig) - info = InvokeCallInfo(match, const_result) - edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) + info = InvokeCallInfo(edge, match, const_result, lookupsig) if !match.fully_covers effects = Effects(effects; nothrow=false) exct = exct ⊔ TypeError @@ -2328,7 +2302,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_apply(interp, argtypes, si, sv, max_methods) elseif f === invoke return abstract_invoke(interp, arginfo, si, sv) - elseif f === modifyfield! || f === Core.modifyglobal! || f === Core.memoryrefmodify! || f === atomic_pointermodify + elseif f === modifyfield! || f === Core.modifyglobal! || + f === Core.memoryrefmodify! || f === atomic_pointermodify return abstract_modifyop!(interp, f, argtypes, si, sv) elseif f === Core.finalizer return abstract_finalizer(interp, argtypes, sv) @@ -2455,19 +2430,23 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, mresult = abstract_call_method(interp, ocmethod, sig, Core.svec(), false, si, sv) ocsig_box = Core.Box(ocsig) return Future{CallMeta}(mresult, interp, sv) do result, interp, sv - (; rt, exct, edge, effects, volatile_inf_result, edgecycle) = result + (; rt, exct, effects, volatile_inf_result, edge, edgecycle) = result 𝕃ₚ = ipo_lattice(interp) ⊑, ⋤, ⊔ = partialorder(𝕃ₚ), strictneqpartialorder(𝕃ₚ), join(𝕃ₚ) const_result = volatile_inf_result if !edgecycle const_call_result = abstract_call_method_with_const_args(interp, result, - nothing, arginfo, si, match, sv) + #=f=#nothing, arginfo, si, match, sv) if const_call_result !== nothing + const_edge = nothing if const_call_result.rt ⊑ rt - (; rt, effects, const_result, edge) = const_call_result + (; rt, effects, const_result, const_edge) = const_call_result end if const_call_result.exct ⋤ exct - (; exct, const_result, edge) = const_call_result + (; exct, const_result, const_edge) = const_call_result + end + if const_edge !== nothing + edge = const_edge end end end @@ -2481,8 +2460,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, end end rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) - info = OpaqueClosureCallInfo(match, const_result) - edge !== nothing && add_backedge!(sv, edge) + info = OpaqueClosureCallInfo(edge, match, const_result) return CallMeta(rt, exct, effects, info) end end @@ -3432,7 +3410,6 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr while currpc < bbend currpc += 1 frame.currpc = currpc - empty_backedges!(frame, currpc) stmt = frame.src.code[currpc] # If we're at the end of the basic block ... if currpc == bbend diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index a200d5ced4d93..43ada89f23133 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -247,7 +247,7 @@ mutable struct InferenceState # TODO: Could keep this sparsely by doing structural liveness analysis ahead of time. bb_vartables::Vector{Union{Nothing,VarTable}} # nothing if not analyzed yet ssavaluetypes::Vector{Any} - stmt_edges::Vector{Vector{Any}} + edges::Vector{Any} stmt_info::Vector{CallInfo} #= intermediate states for interprocedural abstract interpretation =# @@ -302,7 +302,7 @@ mutable struct InferenceState nssavalues = src.ssavaluetypes::Int ssavalue_uses = find_ssavalue_uses(code, nssavalues) nstmts = length(code) - stmt_edges = Vector{Vector{Any}}(undef, nstmts) + edges = [] stmt_info = CallInfo[ NoCallInfo() for i = 1:nstmts ] nslots = length(src.slotflags) @@ -327,7 +327,7 @@ mutable struct InferenceState unreachable = BitSet() pclimitations = IdSet{InferenceState}() limitations = IdSet{InferenceState}() - cycle_backedges = Vector{Tuple{InferenceState,Int}}() + cycle_backedges = Tuple{InferenceState,Int}[] callstack = AbsIntState[] tasks = WorkThunk[] @@ -350,10 +350,12 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) + parentid = frameid = cycleid = 0 + this = new( mi, world, mod, sptypes, slottypes, src, cfg, spec_info, - currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, - tasks, pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, + currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, edges, stmt_info, + tasks, pclimitations, limitations, cycle_backedges, callstack, parentid, frameid, cycleid, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, interp) @@ -754,30 +756,6 @@ function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize return nothing end -function add_cycle_backedge!(caller::InferenceState, frame::InferenceState) - update_valid_age!(caller, frame.valid_worlds) - backedge = (caller, caller.currpc) - contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) - add_backedge!(caller, frame.linfo) - return frame -end - -function get_stmt_edges!(caller::InferenceState, currpc::Int=caller.currpc) - stmt_edges = caller.stmt_edges - if !isassigned(stmt_edges, currpc) - return stmt_edges[currpc] = Any[] - else - return stmt_edges[currpc] - end -end - -function empty_backedges!(frame::InferenceState, currpc::Int=frame.currpc) - if isassigned(frame.stmt_edges, currpc) - empty!(frame.stmt_edges[currpc]) - end - return nothing -end - function narguments(sv::InferenceState, include_va::Bool=true) nargs = Int(sv.src.nargs) if !include_va @@ -1008,32 +986,6 @@ function callers_in_cycle(sv::InferenceState) end callers_in_cycle(sv::IRInterpretationState) = AbsIntCycle(sv.callstack::Vector{AbsIntState}, 0, 0) -# temporarily accumulate our edges to later add as backedges in the callee -function add_backedge!(caller::InferenceState, mi::MethodInstance) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), mi) -end -function add_backedge!(irsv::IRInterpretationState, mi::MethodInstance) - return push!(irsv.edges, mi) -end - -function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), mi::MethodInstance) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), invokesig, mi) -end -function add_invoke_backedge!(irsv::IRInterpretationState, @nospecialize(invokesig::Type), mi::MethodInstance) - return push!(irsv.edges, invokesig, mi) -end - -# used to temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge!(caller::InferenceState, mt::MethodTable, @nospecialize(typ)) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), mt, typ) -end -function add_mt_backedge!(irsv::IRInterpretationState, mt::MethodTable, @nospecialize(typ)) - return push!(irsv.edges, mt, typ) -end - get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] get_curr_ssaflag(sv::IRInterpretationState) = sv.ir.stmts[sv.curridx][:flag] diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index c5606f80468c0..e8508ade88b6c 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -141,8 +141,7 @@ struct InliningState{Interp<:AbstractInterpreter} interp::Interp end function InliningState(sv::InferenceState, interp::AbstractInterpreter) - edges = sv.stmt_edges[1] - return InliningState(edges, sv.world, interp) + return InliningState(sv.edges, sv.world, interp) end function InliningState(interp::AbstractInterpreter) return InliningState(Any[], get_inference_world(interp), interp) @@ -225,6 +224,7 @@ include("compiler/ssair/irinterp.jl") function ir_to_codeinf!(opt::OptimizationState) (; linfo, src) = opt src = ir_to_codeinf!(src, opt.ir::IRCode) + src.edges = opt.inlining.edges opt.ir = nothing maybe_validate_code(linfo, src, "optimized") return src diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index dfdd317f74d87..ae4c04241fa13 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -28,7 +28,8 @@ end struct ConstantCase val::Any - ConstantCase(@nospecialize val) = new(val) + edge::CodeInstance + ConstantCase(@nospecialize(val), edge::CodeInstance) = new(val, edge) end struct SomeCase @@ -68,11 +69,12 @@ struct InliningEdgeTracker new(state.edges, invokesig) end -function add_inlining_backedge!((; edges, invokesig)::InliningEdgeTracker, mi::MethodInstance) +function add_inlining_edge!(et::InliningEdgeTracker, edge::Union{CodeInstance,MethodInstance}) + (; edges, invokesig) = et if invokesig === nothing - push!(edges, mi) + add_one_edge!(edges, edge) else # invoke backedge - push!(edges, invoke_signature(invokesig), mi) + add_invoke_edge!(edges, invoke_signature(invokesig), edge) end return nothing end @@ -784,10 +786,10 @@ function rewrite_apply_exprargs!(todo::Vector{Pair{Int,Any}}, end function compileable_specialization(mi::MethodInstance, effects::Effects, - et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) + et::InliningEdgeTracker, @nospecialize(info::CallInfo), state::InliningState) mi_invoke = mi method, atype, sparams = mi.def::Method, mi.specTypes, mi.sparam_vals - if compilesig_invokes + if OptimizationParams(state.interp).compilesig_invokes new_atype = get_compileable_sig(method, atype, sparams) new_atype === nothing && return nothing if atype !== new_atype @@ -805,43 +807,41 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, return nothing end end - add_inlining_backedge!(et, mi) # to the dispatch lookup - mi_invoke !== mi && push!(et.edges, method.sig, mi_invoke) # add_inlining_backedge to the invoke call, if that is different + add_inlining_edge!(et, mi) # to the dispatch lookup + if mi_invoke !== mi + add_invoke_edge!(et.edges, method.sig, mi_invoke) # add_inlining_edge to the invoke call, if that is different + end return InvokeCase(mi_invoke, effects, info) end -function compileable_specialization(match::MethodMatch, effects::Effects, - et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) - mi = specialize_method(match) - return compileable_specialization(mi, effects, et, info; compilesig_invokes) -end - struct InferredResult src::Any # CodeInfo or IRCode effects::Effects - InferredResult(@nospecialize(src), effects::Effects) = new(src, effects) + edge::CodeInstance + InferredResult(@nospecialize(src), effects::Effects, edge::CodeInstance) = new(src, effects, edge) end @inline function get_cached_result(state::InliningState, mi::MethodInstance) code = get(code_cache(state), mi, nothing) if code isa CodeInstance if use_const_api(code) # in this case function can be inlined to a constant - return ConstantCase(quoted(code.rettype_const)) + return ConstantCase(quoted(code.rettype_const), code) end return code end return nothing end @inline function get_local_result(inf_result::InferenceResult) + @assert isdefined(inf_result, :ci_as_edge) "InferenceResult without ci_as_edge" effects = inf_result.ipo_effects if is_foldable_nothrow(effects) res = inf_result.result if isa(res, Const) && is_inlineable_constant(res.val) # use constant calling convention - return ConstantCase(quoted(res.val)) + return ConstantCase(quoted(res.val), inf_result.ci_as_edge) end end - return InferredResult(inf_result.src, effects) + return InferredResult(inf_result.src, effects, inf_result.ci_as_edge) end # the general resolver for usual and const-prop'ed calls @@ -861,30 +861,28 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, inferred_result = get_cached_result(state, mi) end if inferred_result isa ConstantCase - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, inferred_result.edge) return inferred_result elseif inferred_result isa InferredResult - (; src, effects) = inferred_result + (; src, effects, edge) = inferred_result elseif inferred_result isa CodeInstance src = @atomic :monotonic inferred_result.inferred effects = decode_effects(inferred_result.ipo_purity_bits) + edge = inferred_result else # there is no cached source available, bail out - return compileable_specialization(mi, Effects(), et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(mi, Effects(), et, info, state) end # the duplicated check might have been done already within `analyze_method!`, but still # we need it here too since we may come here directly using a constant-prop' result if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) - return compileable_specialization(mi, effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(edge.def, effects, et, info, state) end src_inlining_policy(state.interp, src, info, flag) || - return compileable_specialization(mi, effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(edge.def, effects, et, info, state) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, edge) if inferred_result isa CodeInstance ir, spec_info, debuginfo = retrieve_ir_for_inlining(inferred_result, src) else @@ -904,7 +902,7 @@ function resolve_todo(mi::MethodInstance, @nospecialize(info::CallInfo), flag::U cached_result = get_cached_result(state, mi) if cached_result isa ConstantCase - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, cached_result.edge) return cached_result elseif cached_result isa CodeInstance src = @atomic :monotonic cached_result.inferred @@ -915,7 +913,7 @@ function resolve_todo(mi::MethodInstance, @nospecialize(info::CallInfo), flag::U src_inlining_policy(state.interp, src, info, flag) || return nothing ir, spec_info, debuginfo = retrieve_ir_for_inlining(cached_result, src) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, cached_result) return InliningTodo(mi, ir, spec_info, debuginfo, effects) end @@ -1119,7 +1117,7 @@ function inline_apply!(todo::Vector{Pair{Int,Any}}, # e.g. rewrite `((t::Tuple)...,)` to `t` nonempty_idx = 0 𝕃ₒ = optimizer_lattice(state.interp) - for i = (arg_start + 1):length(argtypes) + for i = (arg_start+1):length(argtypes) ti = argtypes[i] ⊑(𝕃ₒ, ti, Tuple{}) && continue if ⊑(𝕃ₒ, ti, Tuple) && nonempty_idx == 0 @@ -1137,7 +1135,7 @@ function inline_apply!(todo::Vector{Pair{Int,Any}}, # Try to figure out the signature of the function being called # and if rewrite_apply_exprargs can deal with this form arginfos = MaybeAbstractIterationInfo[] - for i = (arg_start + 1):length(argtypes) + for i = (arg_start+1):length(argtypes) thisarginfo = nothing if !is_valid_type_for_apply_rewrite(argtypes[i], OptimizationParams(state.interp)) isa(info, ApplyCallInfo) || return nothing @@ -1403,9 +1401,7 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig result, match, argtypes, info, flag, state; allow_typevars=true) end if !fully_covered - atype = argtypes_to_type(sig.argtypes) - # We will emit an inline MethodError so we need a backedge to the MethodTable - add_uncovered_edges!(state.edges, info, atype) + # We will emit an inline MethodError in this case, but that info already came inference, so we must already have the uncovered edge for it end elseif !isempty(cases) # if we've not seen all candidates, union split is valid only for dispatch tuples @@ -1453,7 +1449,7 @@ end function semiconcrete_result_item(result::SemiConcreteResult, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState) - mi = result.mi + mi = result.edge.def et = InliningEdgeTracker(state) if (!OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) || @@ -1461,14 +1457,12 @@ function semiconcrete_result_item(result::SemiConcreteResult, # a `@noinline`-declared method when it's marked as `@constprop :aggressive`. # Suppress the inlining here (unless inlining is requested at the callsite). (is_declared_noinline(mi.def::Method) && !is_stmt_inline(flag))) - return compileable_specialization(mi, result.effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(mi, result.effects, et, info, state) end src_inlining_policy(state.interp, result.ir, info, flag) || - return compileable_specialization(mi, result.effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(mi, result.effects, et, info, state) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, result.edge) preserve_local_sources = OptimizationParams(state.interp).preserve_local_sources ir, _, debuginfo = retrieve_ir_for_inlining(mi, result.ir, preserve_local_sources) return InliningTodo(mi, ir, result.spec_info, debuginfo, result.effects) @@ -1476,7 +1470,7 @@ end function handle_semi_concrete_result!(cases::Vector{InliningCase}, result::SemiConcreteResult, match::MethodMatch, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState) - mi = result.mi + mi = result.edge.def validate_sparams(mi.sparam_vals) || return false item = semiconcrete_result_item(result, info, flag, state) item === nothing && return false @@ -1499,11 +1493,10 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn invokesig::Union{Nothing,Vector{Any}}=nothing) if !may_inline_concrete_result(result) et = InliningEdgeTracker(state, invokesig) - return compileable_specialization(result.mi, result.effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + return compileable_specialization(result.edge.def, result.effects, et, info, state) end @assert result.effects === EFFECTS_TOTAL - return ConstantCase(quoted(result.result)) + return ConstantCase(quoted(result.result), result.edge) end function handle_cases!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stmt::Expr, @@ -1552,11 +1545,16 @@ function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOp info isa MethodResultPure && (info = info.info) info isa ConstCallInfo && (info = info.call) info isa MethodMatchInfo || return nothing - length(info.results) == 1 || return nothing + length(info.edges) == length(info.results) == 1 || return nothing match = info.results[1]::MethodMatch match.fully_covers || return nothing - case = compileable_specialization(match, Effects(), InliningEdgeTracker(state), info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) + edge = info.edges[1] + if edge === nothing + edge = specialize_method(match) + else + edge = edge.def + end + case = compileable_specialization(edge, Effects(), InliningEdgeTracker(state), info, state) case === nothing && return nothing stmt.head = :invoke_modify pushfirst!(stmt.args, case.invoke) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index f9565f3971733..0a8239dc590db 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -450,12 +450,6 @@ function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRI (nothrow | noub) || break end - if last(irsv.valid_worlds) >= get_world_counter() - # if we aren't cached, we don't need this edge - # but our caller might, so let's just make it anyways - store_backedges(frame_instance(irsv), irsv.edges) - end - if irsv.frameid != 0 callstack = irsv.callstack::Vector{AbsIntState} @assert callstack[end] === irsv && length(callstack) == irsv.frameid diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 4ad5dcfb2a3c8..bfe33c23871fe 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1529,7 +1529,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, if code isa CodeInstance if use_const_api(code) # No code in the function - Nothing to do - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, code) return true end src = @atomic :monotonic code.inferred @@ -1544,7 +1544,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, length(src.cfg.blocks) == 1 || return false # Ok, we're committed to inlining the finalizer - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, code) # TODO: Should there be a special line number node for inlined finalizers? inline_at = ir[SSAValue(idx)][:line] diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 9dba7a4459f9e..4cbd2ab39fd46 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -22,27 +22,114 @@ struct CallMeta end struct NoCallInfo <: CallInfo end +add_edges_impl(::Vector{Any}, ::NoCallInfo) = nothing """ info::MethodMatchInfo <: CallInfo -Captures the result of a `:jl_matching_methods` lookup for the given call (`info.results`). -This info may then be used by the optimizer to inline the matches, without having -to re-consult the method table. This info is illegal on any statement that is -not a call to a generic function. +Captures the essential arguments and result of a `:jl_matching_methods` lookup +for the given call (`info.results`). This info may then be used by the +optimizer, without having to re-consult the method table. +This info is illegal on any statement that is not a call to a generic function. """ struct MethodMatchInfo <: CallInfo results::MethodLookupResult mt::MethodTable + atype fullmatch::Bool + edges::Vector{Union{Nothing,CodeInstance}} + function MethodMatchInfo( + results::MethodLookupResult, mt::MethodTable, @nospecialize(atype), fullmatch::Bool) + edges = fill!(Vector{Union{Nothing,CodeInstance}}(undef, length(results)), nothing) + return new(results, mt, atype, fullmatch, edges) + end +end +add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo) = _add_edges_impl(edges, info) +function _add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, mi_edge::Bool=false) + if !fully_covering(info) + # add legacy-style missing backedge info also + exists = false + for i in 1:length(edges) + if edges[i] === info.mt && edges[i+1] == info.atype + exists = true + break + end + end + if !exists + push!(edges, info.mt, info.atype) + end + end + nmatches = length(info.results) + if nmatches == length(info.edges) == 1 + # try the optimized format for the representation, if possible and applicable + # if this doesn't succeed, the backedge will be less precise, + # but the forward edge will maintain the precision + edge = info.edges[1] + m = info.results[1] + if edge === nothing + mi = specialize_method(m) # don't allow `Method`-edge for this optimized format + edge = mi + else + mi = edge.def + end + if mi.specTypes === m.spec_types + add_one_edge!(edges, edge) + return nothing + end + end + # add check for whether this lookup already existed in the edges list + for i in 1:length(edges) + if edges[i] === nmatches && edges[i+1] == info.atype + # TODO: must also verify the CodeInstance match too + return nothing + end + end + push!(edges, nmatches, info.atype) + for i = 1:nmatches + edge = info.edges[i] + m = info.results[i] + if edge === nothing + edge = mi_edge ? specialize_method(m) : m.method + else + @assert edge.def.def === m.method + end + push!(edges, edge) + end + nothing +end +function add_one_edge!(edges::Vector{Any}, edge::MethodInstance) + for i in 1:length(edges) + edgeᵢ = edges[i] + edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def) + edgeᵢ isa MethodInstance || continue + if edgeᵢ === edge && !(i > 1 && edges[i-1] isa Type) + return # found existing covered edge + end + end + push!(edges, edge) + nothing +end +function add_one_edge!(edges::Vector{Any}, edge::CodeInstance) + for i in 1:length(edges) + edgeᵢ_orig = edgeᵢ = edges[i] + edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def) + edgeᵢ isa MethodInstance || continue + if edgeᵢ === edge.def && !(i > 1 && edges[i-1] isa Type) + if edgeᵢ_orig isa MethodInstance + # found edge we can upgrade + edges[i] = edge + return + elseif true # XXX compare `CodeInstance` identify? + return + end + end + end + push!(edges, edge) + nothing end nsplit_impl(info::MethodMatchInfo) = 1 getsplit_impl(info::MethodMatchInfo, idx::Int) = (@assert idx == 1; info.results) getresult_impl(::MethodMatchInfo, ::Int) = nothing -function add_uncovered_edges_impl(edges::Vector{Any}, info::MethodMatchInfo, @nospecialize(atype)) - fully_covering(info) || push!(edges, info.mt, atype) - nothing -end """ info::UnionSplitInfo <: CallInfo @@ -56,25 +143,13 @@ This info is illegal on any statement that is not a call to a generic function. struct UnionSplitInfo <: CallInfo split::Vector{MethodMatchInfo} end - -nmatches(info::MethodMatchInfo) = length(info.results) -function nmatches(info::UnionSplitInfo) - n = 0 - for mminfo in info.split - n += nmatches(mminfo) - end - return n -end +add_edges_impl(edges::Vector{Any}, info::UnionSplitInfo) = + _add_edges_impl(edges, info) +_add_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, mi_edge::Bool=false) = + for split in info.split; _add_edges_impl(edges, split, mi_edge); end nsplit_impl(info::UnionSplitInfo) = length(info.split) getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit(info.split[idx], 1) getresult_impl(::UnionSplitInfo, ::Int) = nothing -function add_uncovered_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, @nospecialize(atype)) - all(fully_covering, info.split) && return nothing - # add mt backedges with removing duplications - for mt in uncovered_method_tables(info) - push!(edges, mt, atype) - end -end abstract type ConstResult end @@ -83,15 +158,15 @@ struct ConstPropResult <: ConstResult end struct ConcreteResult <: ConstResult - mi::MethodInstance + edge::CodeInstance effects::Effects result - ConcreteResult(mi::MethodInstance, effects::Effects) = new(mi, effects) - ConcreteResult(mi::MethodInstance, effects::Effects, @nospecialize val) = new(mi, effects, val) + ConcreteResult(edge::CodeInstance, effects::Effects) = new(edge, effects) + ConcreteResult(edge::CodeInstance, effects::Effects, @nospecialize val) = new(edge, effects, val) end struct SemiConcreteResult <: ConstResult - mi::MethodInstance + edge::CodeInstance ir::IRCode effects::Effects spec_info::SpecInfo @@ -116,17 +191,15 @@ struct ConstCallInfo <: CallInfo call::Union{MethodMatchInfo,UnionSplitInfo} results::Vector{Union{Nothing,ConstResult}} end +add_edges_impl(edges::Vector{Any}, info::ConstCallInfo) = add_edges!(edges, info.call) nsplit_impl(info::ConstCallInfo) = nsplit(info.call) getsplit_impl(info::ConstCallInfo, idx::Int) = getsplit(info.call, idx) getresult_impl(info::ConstCallInfo, idx::Int) = info.results[idx] -add_uncovered_edges_impl(edges::Vector{Any}, info::ConstCallInfo, @nospecialize(atype)) = add_uncovered_edges!(edges, info.call, atype) """ info::MethodResultPure <: CallInfo -This struct represents a method result constant was proven to be -effect-free, including being no-throw (typically because the value was computed -by calling an `@pure` function). +This struct represents a method result constant was proven to be effect-free. """ struct MethodResultPure <: CallInfo info::CallInfo @@ -135,6 +208,7 @@ let instance = MethodResultPure(NoCallInfo()) global MethodResultPure MethodResultPure() = instance end +add_edges_impl(edges::Vector{Any}, info::MethodResultPure) = add_edges!(edges, info.info) """ ainfo::AbstractIterationInfo @@ -161,10 +235,19 @@ not an `_apply_iterate` call. """ struct ApplyCallInfo <: CallInfo # The info for the call itself - call::Any + call::CallInfo # AbstractIterationInfo for each argument, if applicable arginfo::Vector{MaybeAbstractIterationInfo} end +function add_edges_impl(edges::Vector{Any}, info::ApplyCallInfo) + add_edges!(edges, info.call) + for arg in info.arginfo + arg === nothing && continue + for edge in arg.each + add_edges!(edges, edge.info) + end + end +end """ info::UnionSplitApplyCallInfo <: CallInfo @@ -175,6 +258,8 @@ This info is illegal on any statement that is not an `_apply_iterate` call. struct UnionSplitApplyCallInfo <: CallInfo infos::Vector{ApplyCallInfo} end +add_edges_impl(edges::Vector{Any}, info::UnionSplitApplyCallInfo) = + for split in info.infos; add_edges!(edges, split); end """ info::InvokeCallInfo @@ -184,8 +269,56 @@ the method that has been processed. Optionally keeps `info.result::InferenceResult` that keeps constant information. """ struct InvokeCallInfo <: CallInfo + edge::Union{Nothing,CodeInstance} match::MethodMatch result::Union{Nothing,ConstResult} + atype # ::Type +end +add_edges_impl(edges::Vector{Any}, info::InvokeCallInfo) = + _add_edges_impl(edges, info) +function _add_edges_impl(edges::Vector{Any}, info::InvokeCallInfo, mi_edge::Bool=false) + edge = info.edge + if edge === nothing + edge = mi_edge ? specialize_method(info.match) : info.match.method + end + add_invoke_edge!(edges, info.atype, edge) + nothing +end +function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::Union{MethodInstance,Method}) + for i in 2:length(edges) + edgeᵢ = edges[i] + edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def) + edgeᵢ isa MethodInstance || edgeᵢ isa Method || continue + if edgeᵢ === edge + edge_minus_1 = edges[i-1] + if edge_minus_1 isa Type && edge_minus_1 == atype + return # found existing covered edge + end + end + end + push!(edges, atype, edge) + nothing +end +function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), edge::CodeInstance) + for i in 2:length(edges) + edgeᵢ_orig = edgeᵢ = edges[i] + edgeᵢ isa CodeInstance && (edgeᵢ = edgeᵢ.def) + if ((edgeᵢ isa MethodInstance && edgeᵢ === edge.def) || + (edgeᵢ isa Method && edgeᵢ === edge.def.def)) + edge_minus_1 = edges[i-1] + if edge_minus_1 isa Type && edge_minus_1 == atype + if edgeᵢ_orig isa MethodInstance || edgeᵢ_orig isa Method + # found edge we can upgrade + edges[i] = edge + return + elseif true # XXX compare `CodeInstance` identify? + return + end + end + end + end + push!(edges, atype, edge) + nothing end """ @@ -196,9 +329,17 @@ the method that has been processed. Optionally keeps `info.result::InferenceResult` that keeps constant information. """ struct OpaqueClosureCallInfo <: CallInfo + edge::Union{Nothing,CodeInstance} match::MethodMatch result::Union{Nothing,ConstResult} end +function add_edges_impl(edges::Vector{Any}, info::OpaqueClosureCallInfo) + edge = info.edge + if edge !== nothing + add_one_edge!(edges, edge) + end + nothing +end """ info::OpaqueClosureCreateInfo <: CallInfo @@ -215,6 +356,9 @@ struct OpaqueClosureCreateInfo <: CallInfo return new(unspec) end end +# merely creating the object implies edges for OC, unlike normal objects, +# since calling them doesn't normally have edges in contrast +add_edges_impl(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = add_edges!(edges, info.unspec.info) # Stmt infos that are used by external consumers, but not by optimization. # These are not produced by default and must be explicitly opted into by @@ -230,6 +374,7 @@ was supposed to analyze. struct ReturnTypeCallInfo <: CallInfo info::CallInfo end +add_edges_impl(edges::Vector{Any}, info::ReturnTypeCallInfo) = add_edges!(edges, info.info) """ info::FinalizerInfo <: CallInfo @@ -241,6 +386,8 @@ struct FinalizerInfo <: CallInfo info::CallInfo # the callinfo for the finalizer call effects::Effects # the effects for the finalizer call end +# merely allocating a finalizer does not imply edges (unless it gets inlined later) +add_edges_impl(::Vector{Any}, ::FinalizerInfo) = nothing """ info::ModifyOpInfo <: CallInfo @@ -256,5 +403,12 @@ Represents a resolved call of one of: struct ModifyOpInfo <: CallInfo info::CallInfo # the callinfo for the `op(getval(), x)` call end +add_edges_impl(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.info) + +struct VirtualMethodMatchInfo <: CallInfo + info::Union{MethodMatchInfo,UnionSplitInfo,InvokeCallInfo} +end +add_edges_impl(edges::Vector{Any}, info::VirtualMethodMatchInfo) = + _add_edges_impl(edges, info.info, #=mi_edge=#true) @specialize diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 2f78348b79844..80e252dde3a02 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1432,7 +1432,7 @@ end return Future{CallMeta}(callinfo, interp, sv) do callinfo, interp, sv TF = TF.contents RT = RT.contents - TF2 = tmeet(callinfo.rt, widenconst(TF)) + TF2 = tmeet(ipo_lattice(interp), callinfo.rt, widenconst(TF)) if TF2 === Bottom RT = Bottom elseif isconcretetype(RT) && has_nontrivial_extended_info(𝕃ᵢ, TF2) # isconcrete condition required to form a PartialStruct @@ -2959,7 +2959,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s if isa(sv, InferenceState) sv.restrict_abstract_call_sites = old_restrict end - info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() + info = MethodResultPure(ReturnTypeCallInfo(call.info)) rt = widenslotwrapper(call.rt) if isa(rt, Const) # output was computed to be constant @@ -2989,11 +2989,12 @@ end # a simplified model of abstract_call_gf_by_type for applicable function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState, max_methods::Int) - length(argtypes) < 2 && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) - isvarargtype(argtypes[2]) && return Future(CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo())) + length(argtypes) < 2 && return Future(CallMeta(Bottom, ArgumentError, EFFECTS_THROWS, NoCallInfo())) + isvarargtype(argtypes[2]) && return Future(CallMeta(Bool, ArgumentError, EFFECTS_THROWS, NoCallInfo())) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) matches = find_method_matches(interp, argtypes, atype; max_methods) + info = NoCallInfo() if isa(matches, FailedMethodMatch) rt = Bool # too many matches to analyze else @@ -3009,17 +3010,10 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, rt = Const(true) # has applicable matches end if rt !== Bool - for i in 1:napplicable - match = applicable[i]::MethodMatch - edge = specialize_method(match) - add_backedge!(sv, edge) - end - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - add_uncovered_edges!(sv, matches, atype) + info = VirtualMethodMatchInfo(matches.info) end end - return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo())) + return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, info)) end add_tfunc(applicable, 1, INT_INF, @nospecs((𝕃::AbstractLattice, f, args...)->Bool), 40) @@ -3053,13 +3047,14 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv update_valid_age!(sv, valid_worlds) if match === nothing rt = Const(false) - add_mt_backedge!(sv, mt, types) # this should actually be an invoke-type backedge + vresults = MethodLookupResult(Any[], valid_worlds, true) + vinfo = MethodMatchInfo(vresults, mt, types, false) # XXX: this should actually be an info with invoke-type edge else rt = Const(true) - edge = specialize_method(match)::MethodInstance - add_invoke_backedge!(sv, types, edge) + vinfo = InvokeCallInfo(nothing, match, nothing, types) end - return CallMeta(rt, Any, EFFECTS_TOTAL, NoCallInfo()) + info = VirtualMethodMatchInfo(vinfo) + return CallMeta(rt, Union{}, EFFECTS_TOTAL, info) end # N.B.: typename maps type equivalence classes to a single value diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 2a3bbf3854302..11337d5a4d047 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -95,26 +95,34 @@ const __measure_typeinf__ = RefValue{Bool}(false) function finish!(interp::AbstractInterpreter, caller::InferenceState; can_discard_trees::Bool=may_discard_trees(interp)) result = caller.result - valid_worlds = result.valid_worlds - if last(valid_worlds) >= get_world_counter() - # if we aren't cached, we don't need this edge - # but our caller might, so let's just make it anyways - store_backedges(result, caller.stmt_edges[1]) - end opt = result.src if opt isa OptimizationState - result.src = opt = ir_to_codeinf!(opt) + result.src = ir_to_codeinf!(opt) end + #@assert last(result.valid_worlds) <= get_world_counter() || isempty(caller.edges) if isdefined(result, :ci) ci = result.ci + # if we aren't cached, we don't need this edge + # but our caller might, so let's just make it anyways + if last(result.valid_worlds) >= get_world_counter() + # TODO: this should probably come after all store_backedges (after optimizations) for the entire graph in finish_cycle + # since we should be requiring that all edges first get their backedges set, as a batch + result.valid_worlds = WorldRange(first(result.valid_worlds), typemax(UInt)) + end + if last(result.valid_worlds) == typemax(UInt) + # if we can record all of the backedges in the global reverse-cache, + # we can now widen our applicability in the global cache too + store_backedges(ci, caller.edges) + end inferred_result = nothing relocatability = 0x1 const_flag = is_result_constabi_eligible(result) if !can_discard_trees || (is_cached(caller) && !const_flag) - inferred_result = transform_result_for_cache(interp, result.linfo, result.valid_worlds, result, can_discard_trees) + inferred_result = transform_result_for_cache(interp, result) + # TODO: do we want to augment edges here with any :invoke targets that we got from inlining (such that we didn't have a direct edge to it already)? relocatability = 0x0 if inferred_result isa CodeInfo - edges = inferred_result.debuginfo + di = inferred_result.debuginfo uncompressed = inferred_result inferred_result = maybe_compress_codeinfo(interp, result.linfo, inferred_result, can_discard_trees) result.is_src_volatile |= uncompressed !== inferred_result @@ -129,14 +137,12 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState; end end # n.b. relocatability = isa(inferred_result, String) && inferred_result[end] - if !@isdefined edges - edges = DebugInfo(result.linfo) + if !@isdefined di + di = DebugInfo(result.linfo) end - ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, UInt8, Any), - ci, inferred_result, const_flag, - first(result.valid_worlds), last(result.valid_worlds), - encode_effects(result.ipo_effects), result.analysis_results, - relocatability, edges) + ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, UInt8, Any, Any), + ci, inferred_result, const_flag, first(result.valid_worlds), last(result.valid_worlds), encode_effects(result.ipo_effects), + result.analysis_results, relocatability, di, Core.svec(caller.edges...)) engine_reject(interp, ci) end return nothing @@ -160,8 +166,8 @@ end function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int) cycle_valid_worlds = WorldRange() cycle_valid_effects = EFFECTS_TOTAL - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState @assert caller.cycleid == cycleid # 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` @@ -170,20 +176,20 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finishinfer!(caller, caller.interp) end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState opt = caller.result.src if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` optimize(caller.interp, opt, caller.result) end end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState finish!(caller.interp, caller) end resize!(frames, cycleid - 1) @@ -210,12 +216,7 @@ function is_result_constabi_eligible(result::InferenceResult) return isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val) end - -function transform_result_for_cache(interp::AbstractInterpreter, - ::MethodInstance, valid_worlds::WorldRange, result::InferenceResult, - can_discard_trees::Bool=may_discard_trees(interp)) - return result.src -end +transform_result_for_cache(::AbstractInterpreter, result::InferenceResult) = result.src function maybe_compress_codeinfo(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInfo, can_discard_trees::Bool=may_discard_trees(interp)) @@ -239,29 +240,20 @@ function maybe_compress_codeinfo(interp::AbstractInterpreter, mi::MethodInstance end end -function cache_result!(interp::AbstractInterpreter, result::InferenceResult) - if last(result.valid_worlds) == get_world_counter() - # if we've successfully recorded all of the backedges in the global reverse-cache, - # we can now widen our applicability in the global cache too - result.valid_worlds = WorldRange(first(result.valid_worlds), typemax(UInt)) - end - @assert isdefined(result.ci, :inferred) +function cache_result!(interp::AbstractInterpreter, result::InferenceResult, ci::CodeInstance) + @assert isdefined(ci, :inferred) # check if the existing linfo metadata is also sufficient to describe the current inference result # to decide if it is worth caching this right now mi = result.linfo - cache_results = true cache = WorldView(code_cache(interp), result.valid_worlds) - if cache_results && haskey(cache, mi) + if haskey(cache, mi) ci = cache[mi] # n.b.: accurate edge representation might cause the CodeInstance for this to be constructed later @assert isdefined(ci, :inferred) - cache_results = false + return false end - - if cache_results - code_cache(interp)[mi] = result.ci - end - return cache_results + code_cache(interp)[mi] = ci + return true end function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) @@ -387,21 +379,13 @@ function refine_exception_type(@nospecialize(exc_bestguess), ipo_effects::Effect return exc_bestguess end +const empty_edges = Core.svec() + # inference completed on `me` # update the MethodInstance function finishinfer!(me::InferenceState, interp::AbstractInterpreter) # prepare to run optimization passes on fulltree @assert isempty(me.ip) - s_edges = get_stmt_edges!(me, 1) - for i = 2:length(me.stmt_edges) - isassigned(me.stmt_edges, i) || continue - edges = me.stmt_edges[i] - append!(s_edges, edges) - empty!(edges) - end - if me.src.edges !== nothing - append!(s_edges, me.src.edges::Vector) - end # inspect whether our inference had a limited result accuracy, # else it may be suitable to cache bestguess = me.bestguess = cycle_fix_limited(me.bestguess, me) @@ -426,6 +410,8 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) me.src.rettype = widenconst(ignorelimited(bestguess)) me.src.min_world = first(me.valid_worlds) me.src.max_world = last(me.valid_worlds) + istoplevel = !(me.linfo.def isa Method) + istoplevel || compute_edges!(me) # don't add backedges to toplevel method instance if limited_ret # a parent may be cached still, but not this intermediate work: @@ -481,17 +467,19 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) const_flags = 0x2 else rettype_const = nothing - const_flags = 0x00 + const_flags = 0x0 end relocatability = 0x0 - edges = nothing - ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any), - result.ci, widenconst(result_type), widenconst(result.exc_result), rettype_const, const_flags, - first(result.valid_worlds), last(result.valid_worlds), - encode_effects(result.ipo_effects), result.analysis_results, edges) + di = nothing + edges = empty_edges # `edges` will be updated within `finish!` + ci = result.ci + ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any), + ci, widenconst(result_type), widenconst(result.exc_result), rettype_const, const_flags, + first(result.valid_worlds), last(result.valid_worlds), + encode_effects(result.ipo_effects), result.analysis_results, di, edges) if is_cached(me) - cached_results = cache_result!(me.interp, me.result) - if !cached_results + cached_result = cache_result!(me.interp, result, ci) + if !cached_result me.cache_mode = CACHE_MODE_NULL end end @@ -500,19 +488,29 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) end # record the backedges -store_backedges(caller::InferenceResult, edges::Vector{Any}) = store_backedges(caller.linfo, edges) -function store_backedges(caller::MethodInstance, edges::Vector{Any}) - isa(caller.def, Method) || return nothing # don't add backedges to toplevel method instance +function store_backedges(caller::CodeInstance, edges::Vector{Any}) + isa(caller.def.def, Method) || return # don't add backedges to toplevel method instance for itr in BackedgeIterator(edges) callee = itr.caller if isa(callee, MethodInstance) ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any, Any), callee, itr.sig, caller) else - typeassert(callee, MethodTable) ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), callee, itr.sig, caller) end end - return nothing + nothing +end + +function compute_edges!(sv::InferenceState) + edges = sv.edges + for i in 1:length(sv.stmt_info) + add_edges!(edges, sv.stmt_info[i]) + end + user_edges = sv.src.edges + if user_edges !== nothing && user_edges !== empty_edges + append!(edges, user_edges) + end + nothing end function record_slot_assign!(sv::InferenceState) @@ -617,7 +615,7 @@ function type_annotate!(interp::AbstractInterpreter, sv::InferenceState) return nothing end -function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, child::InferenceState) +function merge_call_chain!(::AbstractInterpreter, parent::InferenceState, child::InferenceState) # add backedge of parent <- child # then add all backedges of parent <- parent.parent frames = parent.callstack::Vector{AbsIntState} @@ -630,14 +628,21 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, parent = frame_parent(child)::InferenceState end # ensure that walking the callstack has the same cycleid (DAG) - for frame = reverse(ancestorid:length(frames)) - frame = frames[frame]::InferenceState + for frameid = reverse(ancestorid:length(frames)) + frame = frames[frameid]::InferenceState frame.cycleid == ancestorid && break @assert frame.cycleid > ancestorid frame.cycleid = ancestorid end end +function add_cycle_backedge!(caller::InferenceState, frame::InferenceState) + update_valid_age!(caller, frame.valid_worlds) + backedge = (caller, caller.currpc) + contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) + return frame +end + function is_same_frame(interp::AbstractInterpreter, mi::MethodInstance, frame::InferenceState) return mi === frame_instance(frame) && cache_owner(interp) === cache_owner(frame.interp) end @@ -661,8 +666,8 @@ function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, pa parent isa InferenceState || return false frames = parent.callstack::Vector{AbsIntState} uncached = false - for frame = reverse(1:length(frames)) - frame = frames[frame] + for frameid = reverse(1:length(frames)) + frame = frames[frameid] isa(frame, InferenceState) || break uncached |= !is_cached(frame) # ensure we never add an uncached frame to a cycle if is_same_frame(interp, mi, frame) @@ -682,31 +687,20 @@ end ipo_effects(code::CodeInstance) = decode_effects(code.ipo_purity_bits) -struct EdgeCallResult - rt - exct - edge::Union{Nothing,MethodInstance} - effects::Effects - volatile_inf_result::Union{Nothing,VolatileInferenceResult} - function EdgeCallResult(@nospecialize(rt), @nospecialize(exct), - edge::Union{Nothing,MethodInstance}, - effects::Effects, - volatile_inf_result::Union{Nothing,VolatileInferenceResult} = nothing) - return new(rt, exct, edge, effects, volatile_inf_result) - end -end - # return cached result of regular inference function return_cached_result(interp::AbstractInterpreter, method::Method, codeinst::CodeInstance, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool) rt = cached_return_type(codeinst) + exct = codeinst.exctype effects = ipo_effects(codeinst) + edge = codeinst update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst))) - return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects), edgecycle, edgelimited)) + return Future(MethodCallResult(interp, caller, method, rt, exct, effects, edge, edgecycle, edgelimited)) end -function EdgeCall_to_MethodCall_Result(interp::AbstractInterpreter, sv::AbsIntState, method::Method, result::EdgeCallResult, edgecycle::Bool, edgelimited::Bool) - (; rt, exct, edge, effects, volatile_inf_result) = result - +function MethodCallResult(::AbstractInterpreter, sv::AbsIntState, method::Method, + @nospecialize(rt), @nospecialize(exct), effects::Effects, + edge::Union{Nothing,CodeInstance}, edgecycle::Bool, edgelimited::Bool, + volatile_inf_result::Union{Nothing,VolatileInferenceResult}=nothing) if edge === nothing edgecycle = edgelimited = true end @@ -729,14 +723,34 @@ function EdgeCall_to_MethodCall_Result(interp::AbstractInterpreter, sv::AbsIntSt end end - return MethodCallResult(rt, exct, edgecycle, edgelimited, edge, effects, volatile_inf_result) + return MethodCallResult(rt, exct, effects, edge, edgecycle, edgelimited, volatile_inf_result) +end + +# allocate a dummy `edge::CodeInstance` to be added by `add_edges!` +function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState) + mi = sv.linfo + owner = cache_owner(interp) + min_world, max_world = first(sv.valid_worlds), last(sv.valid_worlds) + if max_world >= get_world_counter() + max_world = typemax(UInt) + end + ci = CodeInstance(mi, owner, Any, Any, nothing, nothing, zero(Int32), + min_world, max_world, zero(UInt32), nothing, zero(UInt8), nothing, Core.svec(sv.edges...)) + if max_world == typemax(UInt) + # if we can record all of the backedges in the global reverse-cache, + # we can now widen our applicability in the global cache too + # TODO: this should probably come after we decide this edge is even useful + store_backedges(ci, sv.edges) + end + return ci end # compute (and cache) an inferred AST and return the current best estimate of the result type function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool) - mi = specialize_method(method, atype, sparams)::MethodInstance + mi = specialize_method(method, atype, sparams) cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default force_inline = is_stmt_inline(get_curr_ssaflag(caller)) + edge_ci = nothing let codeinst = get(code_cache(interp), mi, nothing) if codeinst isa CodeInstance # return existing rettype if the code is already inferred inferred = @atomic :monotonic codeinst.inferred @@ -745,6 +759,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize # nevertheless we re-infer it here again in order to propagate the re-inferred # source to the inliner as a volatile result cache_mode = CACHE_MODE_VOLATILE + edge_ci = codeinst else @assert codeinst.def === mi "MethodInstance for cached edge does not match" return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited) @@ -753,7 +768,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_output(#=incremental=#false) add_remark!(interp, caller, "[typeinf_edge] Inference is disabled for the target module") - return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) + return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited)) end if !is_cached(caller) && frame_parent(caller) === nothing # this caller exists to return to the user @@ -765,32 +780,36 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize if frame === false # completely new, but check again after reserving in the engine if cache_mode == CACHE_MODE_GLOBAL - ci = engine_reserve(interp, mi) - let codeinst = get(code_cache(interp), mi, nothing) - if codeinst isa CodeInstance # return existing rettype if the code is already inferred - engine_reject(interp, ci) - inferred = @atomic :monotonic codeinst.inferred - if inferred === nothing && force_inline - cache_mode = CACHE_MODE_VOLATILE - else - @assert codeinst.def === mi "MethodInstance for cached edge does not match" - return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited) - end + ci_from_engine = engine_reserve(interp, mi) + edge_ci = ci_from_engine + codeinst = get(code_cache(interp), mi, nothing) + if codeinst isa CodeInstance # return existing rettype if the code is already inferred + engine_reject(interp, ci_from_engine) + ci_from_engine = nothing + inferred = @atomic :monotonic codeinst.inferred + if inferred === nothing && force_inline + cache_mode = CACHE_MODE_VOLATILE + edge_ci = codeinst + else + @assert codeinst.def === mi "MethodInstance for cached edge does not match" + return return_cached_result(interp, method, codeinst, caller, edgecycle, edgelimited) end end + else + ci_from_engine = nothing end result = InferenceResult(mi, typeinf_lattice(interp)) - if cache_mode == CACHE_MODE_GLOBAL - result.ci = ci + if ci_from_engine !== nothing + result.ci = ci_from_engine end frame = InferenceState(result, cache_mode, interp) # always use the cache for edge targets if frame === nothing add_remark!(interp, caller, "[typeinf_edge] Failed to retrieve source") # can't get the source for this, so we know nothing - if cache_mode == CACHE_MODE_GLOBAL - engine_reject(interp, ci) + if ci_from_engine !== nothing + engine_reject(interp, ci_from_engine) end - return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) + return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited)) end assign_parentchild!(frame, caller) # the actual inference task for this edge is going to be scheduled within `typeinf_local` via the callstack queue @@ -799,15 +818,19 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize push!(caller.tasks, function get_infer_result(interp, caller) update_valid_age!(caller, frame.valid_worlds) local isinferred = is_inferred(frame) - local edge = isinferred ? mi : nothing + local edge = isinferred ? edge_ci : nothing local effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects adjust_effects(effects_for_cycle(frame.ipo_effects), method) + local bestguess = frame.bestguess local 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, so we can use this local result destructively - local volatile_inf_result = isinferred ? VolatileInferenceResult(result) : nothing - local edgeresult = EdgeCallResult(frame.bestguess, exc_bestguess, edge, effects, volatile_inf_result) - mresult[] = EdgeCall_to_MethodCall_Result(interp, caller, method, edgeresult, edgecycle, edgelimited) + local volatile_inf_result = if isinferred && edge_ci isa CodeInstance + result.ci_as_edge = edge_ci # set the edge for the inliner usage + VolatileInferenceResult(result) + end + mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, + edge, edgecycle, edgelimited, volatile_inf_result) return true end) return mresult @@ -815,15 +838,15 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize elseif frame === true # unresolvable cycle add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle") - return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, EdgeCallResult(Any, Any, nothing, Effects()), edgecycle, edgelimited)) + return Future(MethodCallResult(interp, caller, method, Any, Any, Effects(), nothing, edgecycle, edgelimited)) end # return the current knowledge about this cycle frame = frame::InferenceState update_valid_age!(caller, frame.valid_worlds) effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) + bestguess = frame.bestguess exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) - edgeresult = EdgeCallResult(frame.bestguess, exc_bestguess, nothing, effects) - return Future(EdgeCall_to_MethodCall_Result(interp, caller, method, edgeresult, edgecycle, edgelimited)) + return Future(MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, nothing, edgecycle, edgelimited)) end # The `:terminates` effect bit must be conservatively tainted unless recursion cycle has @@ -871,6 +894,7 @@ function codeinfo_for_const(interp::AbstractInterpreter, mi::MethodInstance, @no tree.debuginfo = DebugInfo(mi) tree.ssaflags = UInt32[0] tree.rettype = Core.Typeof(val) + tree.edges = Core.svec() set_inlineable!(tree, true) tree.parent = mi return tree @@ -888,7 +912,7 @@ function codeinstance_for_const_with_code(interp::AbstractInterpreter, code::Cod return CodeInstance(code.def, cache_owner(interp), code.rettype, code.exctype, code.rettype_const, src, Int32(0x3), code.min_world, code.max_world, code.ipo_purity_bits, code.analysis_results, - code.relocatability, src.debuginfo) + code.relocatability, src.debuginfo, src.edges) end result_is_constabi(interp::AbstractInterpreter, result::InferenceResult) = @@ -951,13 +975,13 @@ function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_opti if run_optimizer if result_is_constabi(interp, frame.result) rt = frame.result.result::Const - opt = codeinfo_for_const(interp, frame.linfo, rt.val) + src = codeinfo_for_const(interp, frame.linfo, rt.val) else opt = OptimizationState(frame, interp) optimize(interp, opt, frame.result) - opt = ir_to_codeinf!(opt) + src = ir_to_codeinf!(opt) end - result.src = frame.src = opt + result.src = frame.src = src end return frame end @@ -1050,7 +1074,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod src isa CodeInfo || return nothing return CodeInstance(mi, cache_owner(interp), Any, Any, nothing, src, Int32(0), get_inference_world(interp), get_inference_world(interp), - UInt32(0), nothing, UInt8(0), src.debuginfo) + UInt32(0), nothing, UInt8(0), src.debuginfo, src.edges) end end ci = engine_reserve(interp, mi) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index b6c976da48f67..8899e7673d753 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -91,21 +91,31 @@ There are two constructor available: for constant inference, with extended lattice information included in `result.argtypes`. """ mutable struct InferenceResult + #=== constant fields ===# const linfo::MethodInstance const argtypes::Vector{Any} const overridden_by_const::Union{Nothing,BitVector} - result # extended lattice element if inferred, nothing otherwise - exc_result # like `result`, but for the thrown value - src # ::Union{CodeInfo, IRCode, OptimizationState} if inferred copy is available, nothing otherwise - valid_worlds::WorldRange # if inference and optimization is finished - ipo_effects::Effects # if inference is finished - effects::Effects # if optimization is finished + + #=== mutable fields ===# + result # extended lattice element if inferred, nothing otherwise + exc_result # like `result`, but for the thrown value + src # ::Union{CodeInfo, IRCode, OptimizationState} if inferred copy is available, nothing otherwise + valid_worlds::WorldRange # if inference and optimization is finished + ipo_effects::Effects # if inference is finished + effects::Effects # if optimization is finished analysis_results::AnalysisResults # AnalysisResults with e.g. result::ArgEscapeCache if optimized, otherwise NULL_ANALYSIS_RESULTS - is_src_volatile::Bool # `src` has been cached globally as the compressed format already, allowing `src` to be used destructively - ci::CodeInstance # CodeInstance if this result may be added to the cache + is_src_volatile::Bool # `src` has been cached globally as the compressed format already, allowing `src` to be used destructively + + #=== uninitialized fields ===# + ci::CodeInstance # CodeInstance if this result may be added to the cache + ci_as_edge::CodeInstance # CodeInstance as the edge representing locally cached result function InferenceResult(mi::MethodInstance, argtypes::Vector{Any}, overridden_by_const::Union{Nothing,BitVector}) - return new(mi, argtypes, overridden_by_const, nothing, nothing, nothing, - WorldRange(), Effects(), Effects(), NULL_ANALYSIS_RESULTS, false) + result = exc_result = src = nothing + valid_worlds = WorldRange() + ipo_effects = effects = Effects() + analysis_results = NULL_ANALYSIS_RESULTS + return new(mi, argtypes, overridden_by_const, result, exc_result, src, + valid_worlds, ipo_effects, effects, analysis_results, #=is_src_volatile=#false) end end function InferenceResult(mi::MethodInstance, 𝕃::AbstractLattice=fallback_lattice) @@ -399,7 +409,6 @@ engine_reserve(mi::MethodInstance, @nospecialize owner) = ccall(:jl_engine_reser # engine_fulfill(::AbstractInterpreter, ci::CodeInstance, src::CodeInfo) = ccall(:jl_engine_fulfill, Cvoid, (Any, Any), ci, src) # currently the same as engine_reject, so just use that one engine_reject(::AbstractInterpreter, ci::CodeInstance) = ccall(:jl_engine_fulfill, Cvoid, (Any, Ptr{Cvoid}), ci, C_NULL) - function already_inferred_quick_test end function lock_mi_inference end function unlock_mi_inference end @@ -416,7 +425,6 @@ function add_remark! end may_optimize(::AbstractInterpreter) = true may_compress(::AbstractInterpreter) = true may_discard_trees(::AbstractInterpreter) = true -verbose_stmt_info(::AbstractInterpreter) = false """ method_table(interp::AbstractInterpreter) -> MethodTableView @@ -463,18 +471,23 @@ abstract type CallInfo end @nospecialize +function add_edges!(edges::Vector{Any}, info::CallInfo) + if info === NoCallInfo() + return nothing # just a minor optimization to avoid dynamic dispatch + end + add_edges_impl(edges, info) + nothing +end nsplit(info::CallInfo) = nsplit_impl(info)::Union{Nothing,Int} getsplit(info::CallInfo, idx::Int) = getsplit_impl(info, idx)::MethodLookupResult -add_uncovered_edges!(edges::Vector{Any}, info::CallInfo, @nospecialize(atype)) = add_uncovered_edges_impl(edges, info, atype) - -getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx) +getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx)#=::Union{Nothing,ConstResult}=# -# must implement `nsplit`, `getsplit`, and `add_uncovered_edges!` to opt in to inlining +add_edges_impl(::Vector{Any}, ::CallInfo) = error(""" + All `CallInfo` is required to implement `add_edges_impl(::Vector{Any}, ::CallInfo)`""") nsplit_impl(::CallInfo) = nothing -getsplit_impl(::CallInfo, ::Int) = error("unexpected call into `getsplit`") -add_uncovered_edges_impl(::Vector{Any}, ::CallInfo, _) = error("unexpected call into `add_uncovered_edges!`") - -# must implement `getresult` to opt in to extended lattice return information +getsplit_impl(::CallInfo, ::Int) = error(""" + A `info::CallInfo` that implements `nsplit_impl(info::CallInfo) -> Int` must implement `getsplit_impl(info::CallInfo, idx::Int) -> MethodLookupResult` + in order to correctly opt in to inlining""") getresult_impl(::CallInfo, ::Int) = nothing @specialize diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index f9202788b6360..5361ff26f997c 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -252,8 +252,6 @@ struct BackedgeIterator backedges::Vector{Any} end -const empty_backedge_iter = BackedgeIterator(Any[]) - struct BackedgePair sig # ::Union{Nothing,Type} caller::Union{MethodInstance,MethodTable} @@ -262,11 +260,36 @@ end function iterate(iter::BackedgeIterator, i::Int=1) backedges = iter.backedges - i > length(backedges) && return nothing - item = backedges[i] - isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch - isa(item, MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch - return BackedgePair(item, backedges[i+1]::MethodInstance), i+2 # `invoke` calls + while true + i > length(backedges) && return nothing + item = backedges[i] + if item isa Int + i += 2 + continue # ignore the query information if present + elseif isa(item, Method) + # ignore `Method`-edges (from e.g. failed `abstract_call_method`) + i += 1 + continue + end + if isa(item, CodeInstance) + item = item.def + end + if isa(item, MethodInstance) # regular dispatch + return BackedgePair(nothing, item), i+1 + elseif isa(item, MethodTable) # abstract dispatch (legacy style edges) + return BackedgePair(backedges[i+1], item), i+2 + else # `invoke` call + callee = backedges[i+1] + if isa(callee, Method) + i += 2 + continue + end + if isa(callee, CodeInstance) + callee = callee.def + end + return BackedgePair(item, callee::MethodInstance), i+2 + end + end end ######### diff --git a/base/expr.jl b/base/expr.jl index e281d9b677297..f57331ef02e74 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -79,7 +79,7 @@ function copy(c::CodeInfo) cnew.slottypes = copy(cnew.slottypes::Vector{Any}) end cnew.ssaflags = copy(cnew.ssaflags) - cnew.edges = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector) + cnew.edges = cnew.edges === nothing || cnew.edges isa Core.SimpleVector ? cnew.edges : copy(cnew.edges::Vector) ssavaluetypes = cnew.ssavaluetypes ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes)) return cnew diff --git a/base/show.jl b/base/show.jl index dbdd85f0608da..627982b2bcb1a 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1350,7 +1350,11 @@ function sourceinfo_slotnames(slotnames::Vector{Symbol}) return printnames end -show(io::IO, l::Core.MethodInstance) = show_mi(io, l) +show(io::IO, mi::Core.MethodInstance) = show_mi(io, mi) +function show(io::IO, codeinst::Core.CodeInstance) + print(io, "CodeInstance for ") + show_mi(io, codeinst.def) +end function show_mi(io::IO, mi::Core.MethodInstance, from_stackframe::Bool=false) def = mi.def diff --git a/src/common_symbols1.inc b/src/common_symbols1.inc index f54be52729a4f..3dfcf17a07b5c 100644 --- a/src/common_symbols1.inc +++ b/src/common_symbols1.inc @@ -88,5 +88,3 @@ jl_symbol("ifelse"), jl_symbol("Array"), jl_symbol("eq_int"), jl_symbol("throw_inexacterror"), -jl_symbol("|"), -jl_symbol("setproperty!"), diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index ee2a0e2edd9fe..2a6990bac52ff 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -1,3 +1,5 @@ +jl_symbol("|"), +jl_symbol("setproperty!"), jl_symbol("sext_int"), jl_symbol("String"), jl_symbol("Int"), @@ -244,5 +246,3 @@ jl_symbol("invokelatest"), jl_symbol("jl_array_del_end"), jl_symbol("_mod64"), jl_symbol("parameters"), -jl_symbol("monotonic"), -jl_symbol("regex.jl"), diff --git a/src/gf.c b/src/gf.c index 285942cd157c5..18141410fa625 100644 --- a/src/gf.c +++ b/src/gf.c @@ -322,7 +322,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a jl_code_instance_t *codeinst = jl_new_codeinst(mi, jl_nothing, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, jl_nothing, jl_nothing, - 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL); + 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL, NULL); jl_mi_cache_insert(mi, codeinst); jl_atomic_store_relaxed(&codeinst->specptr.fptr1, fptr); jl_atomic_store_relaxed(&codeinst->invoke, jl_fptr_args); @@ -480,7 +480,7 @@ JL_DLLEXPORT jl_value_t *jl_call_in_typeinf_world(jl_value_t **args, int nargs) JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, - size_t min_world, size_t max_world, jl_debuginfo_t *edges) + size_t min_world, size_t max_world, jl_debuginfo_t *di, jl_svec_t *edges) { jl_value_t *owner = jl_nothing; // TODO: owner should be arg jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mi->cache); @@ -489,27 +489,30 @@ JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_atomic_load_relaxed(&codeinst->max_world) == max_world && jl_egal(codeinst->owner, owner) && jl_egal(codeinst->rettype, rettype)) { - if (edges == NULL) + if (di == NULL) return codeinst; jl_debuginfo_t *debuginfo = jl_atomic_load_relaxed(&codeinst->debuginfo); - if (edges == debuginfo) - return codeinst; - if (debuginfo == NULL && jl_atomic_cmpswap_relaxed(&codeinst->debuginfo, &debuginfo, edges)) - return codeinst; - if (debuginfo && jl_egal((jl_value_t*)debuginfo, (jl_value_t*)edges)) + if (di != debuginfo) { + if (!(debuginfo == NULL && jl_atomic_cmpswap_relaxed(&codeinst->debuginfo, &debuginfo, di))) + if (!(debuginfo && jl_egal((jl_value_t*)debuginfo, (jl_value_t*)di))) + continue; + } + // TODO: this is implied by the matching worlds, since it is intrinsic, so do we really need to verify it? + jl_svec_t *e = jl_atomic_load_relaxed(&codeinst->edges); + if (e && jl_egal((jl_value_t*)e, (jl_value_t*)edges)) return codeinst; } codeinst = jl_atomic_load_relaxed(&codeinst->next); } codeinst = jl_new_codeinst( mi, owner, rettype, (jl_value_t*)jl_any_type, NULL, NULL, - 0, min_world, max_world, 0, jl_nothing, 0, edges); + 0, min_world, max_world, 0, jl_nothing, 0, di, edges); jl_mi_cache_insert(mi, codeinst); return codeinst; } JL_DLLEXPORT int jl_mi_cache_has_ci(jl_method_instance_t *mi, - jl_code_instance_t *ci) + jl_code_instance_t *ci) { jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mi->cache); while (codeinst) { @@ -527,14 +530,16 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( int32_t const_flags, size_t min_world, size_t max_world, uint32_t effects, jl_value_t *analysis_results, uint8_t relocatability, - jl_debuginfo_t *edges /* , int absolute_max*/) + jl_debuginfo_t *di, jl_svec_t *edges /*, int absolute_max*/) { - jl_task_t *ct = jl_current_task; assert(min_world <= max_world && "attempting to set invalid world constraints"); + //assert((!jl_is_method(mi->def.value) || max_world != ~(size_t)0 || min_world <= 1 || edges == NULL || jl_svec_len(edges) != 0) && "missing edges"); + jl_task_t *ct = jl_current_task; jl_code_instance_t *codeinst = (jl_code_instance_t*)jl_gc_alloc(ct->ptls, sizeof(jl_code_instance_t), jl_code_instance_type); codeinst->def = mi; codeinst->owner = owner; + jl_atomic_store_relaxed(&codeinst->edges, edges); jl_atomic_store_relaxed(&codeinst->min_world, min_world); jl_atomic_store_relaxed(&codeinst->max_world, max_world); codeinst->rettype = rettype; @@ -543,7 +548,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( if ((const_flags & 2) == 0) inferred_const = NULL; codeinst->rettype_const = inferred_const; - jl_atomic_store_relaxed(&codeinst->debuginfo, (jl_value_t*)edges == jl_nothing ? NULL : edges); + jl_atomic_store_relaxed(&codeinst->debuginfo, (jl_value_t*)di == jl_nothing ? NULL : di); jl_atomic_store_relaxed(&codeinst->specptr.fptr, NULL); jl_atomic_store_relaxed(&codeinst->invoke, NULL); if ((const_flags & 1) != 0) { @@ -563,13 +568,17 @@ JL_DLLEXPORT void jl_update_codeinst( jl_code_instance_t *codeinst, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world, uint32_t effects, jl_value_t *analysis_results, - uint8_t relocatability, jl_debuginfo_t *edges /* , int absolute_max*/) + uint8_t relocatability, jl_debuginfo_t *di, jl_svec_t *edges /* , int absolute_max*/) { + assert(min_world <= max_world && "attempting to set invalid world constraints"); + //assert((!jl_is_method(codeinst->def->def.value) || max_world != ~(size_t)0 || min_world <= 1 || jl_svec_len(edges) != 0) && "missing edges"); codeinst->relocatability = relocatability; codeinst->analysis_results = analysis_results; jl_gc_wb(codeinst, analysis_results); jl_atomic_store_relaxed(&codeinst->ipo_purity_bits, effects); - jl_atomic_store_relaxed(&codeinst->debuginfo, edges); + jl_atomic_store_relaxed(&codeinst->debuginfo, di); + jl_gc_wb(codeinst, di); + jl_atomic_store_relaxed(&codeinst->edges, edges); jl_gc_wb(codeinst, edges); if ((const_flags & 1) != 0) { assert(codeinst->rettype_const); @@ -587,9 +596,10 @@ JL_DLLEXPORT void jl_fill_codeinst( jl_value_t *inferred_const, int32_t const_flags, size_t min_world, size_t max_world, uint32_t effects, jl_value_t *analysis_results, - jl_debuginfo_t *edges /* , int absolute_max*/) + jl_debuginfo_t *di, jl_svec_t *edges /* , int absolute_max*/) { assert(min_world <= max_world && "attempting to set invalid world constraints"); + //assert((!jl_is_method(codeinst->def->def.value) || max_world != ~(size_t)0 || min_world <= 1 || jl_svec_len(edges) != 0) && "missing edges"); codeinst->rettype = rettype; jl_gc_wb(codeinst, rettype); codeinst->exctype = exctype; @@ -598,8 +608,12 @@ JL_DLLEXPORT void jl_fill_codeinst( codeinst->rettype_const = inferred_const; jl_gc_wb(codeinst, inferred_const); } - jl_atomic_store_relaxed(&codeinst->debuginfo, (jl_value_t*)edges == jl_nothing ? NULL : edges); + jl_atomic_store_relaxed(&codeinst->edges, edges); jl_gc_wb(codeinst, edges); + if ((jl_value_t*)di != jl_nothing) { + jl_atomic_store_relaxed(&codeinst->debuginfo, di); + jl_gc_wb(codeinst, di); + } if ((const_flags & 1) != 0) { // TODO: may want to follow ordering restrictions here (see jitlayers.cpp) assert(const_flags & 2); @@ -616,7 +630,7 @@ JL_DLLEXPORT void jl_fill_codeinst( JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst_uninit(jl_method_instance_t *mi, jl_value_t *owner) { - jl_code_instance_t *codeinst = jl_new_codeinst(mi, owner, NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL, 0, NULL); + jl_code_instance_t *codeinst = jl_new_codeinst(mi, owner, NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL); jl_atomic_store_relaxed(&codeinst->min_world, 1); // make temporarily invalid before returning, so that jl_fill_codeinst is valid later return codeinst; } @@ -1745,35 +1759,29 @@ JL_DLLEXPORT jl_value_t *jl_debug_method_invalidation(int state) static void _invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_world, int depth); // recursively invalidate cached methods that had an edge to a replaced method -static void invalidate_method_instance(jl_method_instance_t *replaced, size_t max_world, int depth) +static void invalidate_code_instance(jl_code_instance_t *replaced, size_t max_world, int depth) { jl_timing_counter_inc(JL_TIMING_COUNTER_Invalidations, 1); if (_jl_debug_method_invalidation) { jl_value_t *boxeddepth = NULL; JL_GC_PUSH1(&boxeddepth); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)replaced); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)replaced->def); boxeddepth = jl_box_int32(depth); jl_array_ptr_1d_push(_jl_debug_method_invalidation, boxeddepth); JL_GC_POP(); } - //jl_static_show(JL_STDERR, (jl_value_t*)replaced); - if (!jl_is_method(replaced->def.method)) + //jl_static_show(JL_STDERR, (jl_value_t*)replaced->def); + if (!jl_is_method(replaced->def->def.method)) return; // shouldn't happen, but better to be safe - JL_LOCK(&replaced->def.method->writelock); - jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&replaced->cache); - while (codeinst) { - if (jl_atomic_load_relaxed(&codeinst->max_world) == ~(size_t)0) { - assert(jl_atomic_load_relaxed(&codeinst->min_world) - 1 <= max_world && "attempting to set illogical world constraints (probable race condition)"); - jl_atomic_store_release(&codeinst->max_world, max_world); - } - assert(jl_atomic_load_relaxed(&codeinst->max_world) <= max_world); - codeinst = jl_atomic_load_relaxed(&codeinst->next); + JL_LOCK(&replaced->def->def.method->writelock); + if (jl_atomic_load_relaxed(&replaced->max_world) == ~(size_t)0) { + assert(jl_atomic_load_relaxed(&replaced->min_world) - 1 <= max_world && "attempting to set illogical world constraints (probable race condition)"); + jl_atomic_store_release(&replaced->max_world, max_world); } - JL_GC_PUSH1(&replaced); + assert(jl_atomic_load_relaxed(&replaced->max_world) <= max_world); // recurse to all backedges to update their valid range also - _invalidate_backedges(replaced, max_world, depth + 1); - JL_GC_POP(); - JL_UNLOCK(&replaced->def.method->writelock); + _invalidate_backedges(replaced->def, max_world, depth + 1); + JL_UNLOCK(&replaced->def->def.method->writelock); } static void _invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_world, int depth) { @@ -1783,10 +1791,11 @@ static void _invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_ replaced_mi->backedges = NULL; JL_GC_PUSH1(&backedges); size_t i = 0, l = jl_array_nrows(backedges); - jl_method_instance_t *replaced; + jl_code_instance_t *replaced; while (i < l) { i = get_next_edge(backedges, i, NULL, &replaced); - invalidate_method_instance(replaced, max_world, depth); + JL_GC_PROMISE_ROOTED(replaced); // propagated by get_next_edge from backedges + invalidate_code_instance(replaced, max_world, depth); } JL_GC_POP(); } @@ -1808,11 +1817,14 @@ static void invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_w } // add a backedge from callee to caller -JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_method_instance_t *caller) +JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_code_instance_t *caller) { - JL_LOCK(&callee->def.method->writelock); if (invokesig == jl_nothing) invokesig = NULL; // julia uses `nothing` but C uses NULL (#undef) + assert(jl_is_method_instance(callee)); + assert(jl_is_code_instance(caller)); + assert(invokesig == NULL || jl_is_type(invokesig)); + JL_LOCK(&callee->def.method->writelock); int found = 0; // TODO: use jl_cache_type_(invokesig) like cache_method does to save memory if (!callee->backedges) { @@ -1843,8 +1855,9 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, } // add a backedge from a non-existent signature to caller -JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller) +JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_code_instance_t *caller) { + assert(jl_is_code_instance(caller)); JL_LOCK(&mt->writelock); if (!mt->backedges) { // lazy-init the backedges array @@ -1857,7 +1870,7 @@ JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *t // check if the edge is already present and avoid adding a duplicate size_t i, l = jl_array_nrows(mt->backedges); for (i = 1; i < l; i += 2) { - if (jl_array_ptr_ref(mt->backedges, i) == caller) { + if (jl_array_ptr_ref(mt->backedges, i) == (jl_value_t*)caller) { if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { JL_UNLOCK(&mt->writelock); return; @@ -1867,7 +1880,7 @@ JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *t // reuse an already cached instance of this type, if possible // TODO: use jl_cache_type_(tt) like cache_method does, instead of this linear scan? for (i = 1; i < l; i += 2) { - if (jl_array_ptr_ref(mt->backedges, i) != caller) { + if (jl_array_ptr_ref(mt->backedges, i) != (jl_value_t*)caller) { if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { typ = jl_array_ptr_ref(mt->backedges, i - 1); break; @@ -1875,7 +1888,7 @@ JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *t } } jl_array_ptr_1d_push(mt->backedges, typ); - jl_array_ptr_1d_push(mt->backedges, caller); + jl_array_ptr_1d_push(mt->backedges, (jl_value_t*)caller); } JL_UNLOCK(&mt->writelock); } @@ -2161,6 +2174,7 @@ void jl_method_table_activate(jl_methtable_t *mt, jl_typemap_entry_t *newentry) size_t ins = 0; for (i = 1; i < na; i += 2) { jl_value_t *backedgetyp = backedges[i - 1]; + JL_GC_PROMISE_ROOTED(backedgetyp); int missing = 0; if (jl_type_intersection2(backedgetyp, (jl_value_t*)type, &isect, &isect2)) { // See if the intersection was actually already fully @@ -2189,8 +2203,9 @@ void jl_method_table_activate(jl_methtable_t *mt, jl_typemap_entry_t *newentry) } } if (missing) { - jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i]; - invalidate_method_instance(backedge, max_world, 0); + jl_code_instance_t *backedge = (jl_code_instance_t*)backedges[i]; + JL_GC_PROMISE_ROOTED(backedge); + invalidate_code_instance(backedge, max_world, 0); invalidated = 1; if (_jl_debug_method_invalidation) jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)backedgetyp); @@ -2253,13 +2268,14 @@ void jl_method_table_activate(jl_methtable_t *mt, jl_typemap_entry_t *newentry) if (backedges) { size_t ib = 0, insb = 0, nb = jl_array_nrows(backedges); jl_value_t *invokeTypes; - jl_method_instance_t *caller; + jl_code_instance_t *caller; while (ib < nb) { ib = get_next_edge(backedges, ib, &invokeTypes, &caller); + JL_GC_PROMISE_ROOTED(caller); // propagated by get_next_edge from backedges int replaced_edge; if (invokeTypes) { // n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes - if (jl_egal(invokeTypes, caller->def.method->sig)) + if (jl_egal(invokeTypes, caller->def->def.method->sig)) replaced_edge = 0; // if invokeTypes == m.sig, then the only way to change this invoke is to replace the method itself else replaced_edge = jl_subtype(invokeTypes, type) && is_replacing(ambig, type, m, d, n, invokeTypes, NULL, morespec); @@ -2268,7 +2284,7 @@ void jl_method_table_activate(jl_methtable_t *mt, jl_typemap_entry_t *newentry) replaced_edge = replaced_dispatch; } if (replaced_edge) { - invalidate_method_instance(caller, max_world, 1); + invalidate_code_instance(caller, max_world, 1); invalidated = 1; } else { @@ -2685,8 +2701,10 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_code_instance_t *codeinst2 = jl_compile_method_internal(mi2, world); jl_code_instance_t *codeinst = jl_get_method_inferred( mi, codeinst2->rettype, - jl_atomic_load_relaxed(&codeinst2->min_world), jl_atomic_load_relaxed(&codeinst2->max_world), - jl_atomic_load_relaxed(&codeinst2->debuginfo)); + jl_atomic_load_relaxed(&codeinst2->min_world), + jl_atomic_load_relaxed(&codeinst2->max_world), + jl_atomic_load_relaxed(&codeinst2->debuginfo), + jl_atomic_load_relaxed(&codeinst2->edges)); if (jl_atomic_load_relaxed(&codeinst->invoke) == NULL) { codeinst->rettype_const = codeinst2->rettype_const; jl_gc_wb(codeinst, codeinst->rettype_const); @@ -2743,7 +2761,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (unspec && (unspec_invoke = jl_atomic_load_acquire(&unspec->invoke))) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, jl_nothing, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL); + 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL, NULL); codeinst->rettype_const = unspec->rettype_const; uint8_t specsigflags; jl_callptr_t invoke; @@ -2768,7 +2786,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (!jl_code_requires_compiler(src, 0)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, jl_nothing, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL); + 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL, NULL); jl_atomic_store_release(&codeinst->invoke, jl_fptr_interpret_call); jl_mi_cache_insert(mi, codeinst); record_precompile_statement(mi, 0, 0); @@ -2828,7 +2846,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_method_instance_t *unspec = jl_get_unspecialized(def); if (unspec == NULL) unspec = mi; - jl_code_instance_t *ucache = jl_get_method_inferred(unspec, (jl_value_t*)jl_any_type, 1, ~(size_t)0, NULL); + jl_code_instance_t *ucache = jl_get_method_inferred(unspec, (jl_value_t*)jl_any_type, 1, ~(size_t)0, NULL, NULL); // ask codegen to make the fptr for unspec jl_callptr_t ucache_invoke = jl_atomic_load_acquire(&ucache->invoke); if (ucache_invoke == NULL) { @@ -2848,7 +2866,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t } codeinst = jl_new_codeinst(mi, jl_nothing, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL); + 0, 1, ~(size_t)0, 0, jl_nothing, 0, NULL, NULL); codeinst->rettype_const = ucache->rettype_const; uint8_t specsigflags; jl_callptr_t invoke; diff --git a/src/ircode.c b/src/ircode.c index 873d33d2d7523..bec8d46513eef 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -25,6 +25,7 @@ typedef struct { ios_t *s; // method we're compressing for jl_method_t *method; + jl_svec_t *edges; jl_ptls_t ptls; uint8_t relocatability; } jl_ircode_state; @@ -72,7 +73,7 @@ static void literal_val_id(rle_reference *rr, jl_ircode_state *s, jl_value_t *v) { jl_array_t *rs = s->method->roots; int i, l = jl_array_nrows(rs); - if (jl_is_symbol(v) || jl_is_concrete_type(v)) { + if (jl_is_symbol(v) || jl_is_concrete_type(v)) { // TODO: or more generally, any ptr-egal value for (i = 0; i < l; i++) { if (jl_array_ptr_ref(rs, i) == v) return tagged_root(rr, s, i); @@ -84,6 +85,12 @@ static void literal_val_id(rle_reference *rr, jl_ircode_state *s, jl_value_t *v) return tagged_root(rr, s, i); } } + for (size_t i = 0; i < jl_svec_len(s->edges); i++) { + if (jl_svecref(s->edges, i) == v) { + rr->index = i; + return; + } + } jl_add_method_root(s->method, jl_precompile_toplevel_module, v); return tagged_root(rr, s, jl_array_nrows(rs) - 1); } @@ -102,13 +109,24 @@ static void jl_encode_int32(jl_ircode_state *s, int32_t x) static void jl_encode_as_indexed_root(jl_ircode_state *s, jl_value_t *v) { - rle_reference rr; + rle_reference rr = {.key = -1, .index = -1}; if (jl_is_string(v)) v = jl_as_global_root(v, 1); literal_val_id(&rr, s, v); int id = rr.index; assert(id >= 0); + if (rr.key == -1) { + if (id <= UINT8_MAX) { + write_uint8(s->s, TAG_EDGE); + write_uint8(s->s, id); + } + else { + write_uint8(s->s, TAG_LONG_EDGE); + write_uint32(s->s, id); + } + return; + } if (rr.key) { write_uint8(s->s, TAG_RELOC_METHODROOT); write_uint64(s->s, rr.key); @@ -689,6 +707,10 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED return lookup_root(s->method, 0, read_uint8(s->s)); case TAG_LONG_METHODROOT: return lookup_root(s->method, 0, read_uint32(s->s)); + case TAG_EDGE: + return jl_svecref(s->edges, read_uint8(s->s)); + case TAG_LONG_EDGE: + return jl_svecref(s->edges, read_uint32(s->s)); case TAG_SVEC: JL_FALLTHROUGH; case TAG_LONG_SVEC: return jl_decode_value_svec(s, tag); case TAG_COMMONSYM: @@ -865,9 +887,11 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) m->roots = jl_alloc_vec_any(0); jl_gc_wb(m, m->roots); } + jl_value_t *edges = code->edges; jl_ircode_state s = { &dest, m, + (!isdef && jl_is_svec(edges)) ? (jl_svec_t*)edges : jl_emptysvec, jl_current_task->ptls, 1 }; @@ -950,6 +974,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t jl_ircode_state s = { &src, m, + metadata == NULL ? NULL : jl_atomic_load_relaxed(&metadata->edges), jl_current_task->ptls, 1 }; @@ -1015,6 +1040,8 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t jl_gc_wb(code, code->rettype); code->min_world = jl_atomic_load_relaxed(&metadata->min_world); code->max_world = jl_atomic_load_relaxed(&metadata->max_world); + code->edges = (jl_value_t*)s.edges; + jl_gc_wb(code, s.edges); } return code; diff --git a/src/jltypes.c b/src/jltypes.c index 11f1d11a14edc..71eaa003d7d4a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3638,7 +3638,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_instance_type = jl_new_datatype(jl_symbol("CodeInstance"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(17, + jl_perm_symsvec(18, "def", "owner", "next", @@ -3648,13 +3648,14 @@ void jl_init_types(void) JL_GC_DISABLED "exctype", "rettype_const", "inferred", - "debuginfo", // TODO: rename to edges? + "debuginfo", + "edges", //"absolute_max", "ipo_purity_bits", "analysis_results", "specsigflags", "precompile", "relocatability", "invoke", "specptr"), // function object decls - jl_svec(17, + jl_svec(18, jl_method_instance_type, jl_any_type, jl_any_type, @@ -3665,6 +3666,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type, jl_debuginfo_type, + jl_simplevector_type, //jl_bool_type, jl_uint32_type, jl_any_type, @@ -3675,8 +3677,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_emptysvec, 0, 1, 1); jl_svecset(jl_code_instance_type->types, 2, jl_code_instance_type); - const static uint32_t code_instance_constfields[1] = { 0b00000100011100011 }; // Set fields 1, 2, 6-8, 12 as const - const static uint32_t code_instance_atomicfields[1] = { 0b11011011100011100 }; // Set fields 3-5, 9, 10, 11, 13-14, 16-17 as atomic + const static uint32_t code_instance_constfields[1] = { 0b000001000011100011 }; // Set fields 1, 2, 6-8, 13 as const + const static uint32_t code_instance_atomicfields[1] = { 0b110110111100011100 }; // Set fields 3-5, 9-12, 14-15, 17-18 as atomic // Fields 4-5 are only operated on by construction and deserialization, so are effectively const at runtime // Fields ipo_purity_bits and analysis_results are not currently threadsafe or reliable, as they get mutated after optimization, but are not declared atomic // and there is no way to tell (during inference) if their value is finalized yet (to wait for them to be narrowed if applicable) @@ -3826,8 +3828,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_method_type->types, 13, jl_method_instance_type); //jl_svecset(jl_debuginfo_type->types, 0, jl_method_instance_type); // union(jl_method_instance_type, jl_method_type, jl_symbol_type) jl_svecset(jl_method_instance_type->types, 4, jl_code_instance_type); - jl_svecset(jl_code_instance_type->types, 15, jl_voidpointer_type); jl_svecset(jl_code_instance_type->types, 16, jl_voidpointer_type); + jl_svecset(jl_code_instance_type->types, 17, jl_voidpointer_type); jl_svecset(jl_binding_type->types, 0, jl_globalref_type); jl_svecset(jl_binding_partition_type->types, 3, jl_binding_partition_type); diff --git a/src/julia.h b/src/julia.h index a710192d5756c..bfb641d38374b 100644 --- a/src/julia.h +++ b/src/julia.h @@ -408,7 +408,7 @@ struct _jl_method_instance_t { } def; // pointer back to the context for this code jl_value_t *specTypes; // argument types this was specialized for jl_svec_t *sparam_vals; // static parameter values, indexed by def.method->sig - jl_array_t *backedges; // list of method-instances which call this method-instance; `invoke` records (invokesig, caller) pairs + jl_array_t *backedges; // list of code-instances which call this method-instance; `invoke` records (invokesig, caller) pairs _Atomic(struct _jl_code_instance_t*) cache; uint8_t cache_with_orig; // !cache_with_specTypes @@ -453,6 +453,7 @@ typedef struct _jl_code_instance_t { // - null, indicating that inference was not yet completed or did not succeed _Atomic(jl_value_t *) inferred; _Atomic(jl_debuginfo_t *) debuginfo; // stored information about edges from this object (set once, with a happens-before both source and invoke) + _Atomic(jl_svec_t *) edges; // forward edge info //TODO: uint8_t absolute_max; // whether true max world is unknown // purity results @@ -789,7 +790,7 @@ typedef struct _jl_methtable_t { _Atomic(jl_typemap_t*) cache; _Atomic(intptr_t) max_args; // max # of non-vararg arguments in a signature jl_module_t *module; // sometimes used for debug printing - jl_array_t *backedges; // (sig, caller::MethodInstance) pairs + jl_array_t *backedges; // (sig, caller::CodeInstance) pairs jl_mutex_t writelock; uint8_t offs; // 0, or 1 to skip splitting typemap on first (function) argument uint8_t frozen; // whether this accepts adding new methods diff --git a/src/julia_internal.h b/src/julia_internal.h index ade5940f30687..9a8750bbc2500 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -675,7 +675,7 @@ JL_DLLEXPORT jl_code_info_t *jl_gdbcodetyped1(jl_method_instance_t *mi, size_t w JL_DLLEXPORT jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, - size_t min_world, size_t max_world, jl_debuginfo_t *edges); + size_t min_world, size_t max_world, jl_debuginfo_t *di, jl_svec_t *edges); JL_DLLEXPORT jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_read_codeinst_invoke(jl_code_instance_t *ci, uint8_t *specsigflags, jl_callptr_t *invoke, void **specptr, int waitcompile) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *match, size_t world, size_t min_valid, size_t max_valid, int mt_cache); @@ -687,7 +687,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world, uint32_t effects, jl_value_t *analysis_results, - uint8_t relocatability, jl_debuginfo_t *edges /* , int absolute_max*/); + uint8_t relocatability, jl_debuginfo_t *di, jl_svec_t *edges /* , int absolute_max*/); JL_DLLEXPORT const char *jl_debuginfo_file(jl_debuginfo_t *debuginfo) JL_NOTSAFEPOINT; JL_DLLEXPORT const char *jl_debuginfo_file1(jl_debuginfo_t *debuginfo) JL_NOTSAFEPOINT; @@ -705,9 +705,9 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); JL_DLLEXPORT void jl_resolve_globals_in_ir(jl_array_t *stmts, jl_module_t *m, jl_svec_t *sparam_vals, int binding_effects); -int get_next_edge(jl_array_t *list, int i, jl_value_t** invokesig, jl_method_instance_t **caller) JL_NOTSAFEPOINT; -int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_method_instance_t *caller); -void push_edge(jl_array_t *list, jl_value_t *invokesig, jl_method_instance_t *caller); +int get_next_edge(jl_array_t *list, int i, jl_value_t** invokesig, jl_code_instance_t **caller) JL_NOTSAFEPOINT; +int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_code_instance_t *caller); +void push_edge(jl_array_t *list, jl_value_t *invokesig, jl_code_instance_t *caller); JL_DLLEXPORT void jl_add_method_root(jl_method_t *m, jl_module_t *mod, jl_value_t* root); void jl_append_method_roots(jl_method_t *m, uint64_t modid, jl_array_t* roots); @@ -727,6 +727,7 @@ JL_DLLEXPORT void jl_typeassert(jl_value_t *x, jl_value_t *t); #define JL_CALLABLE(name) \ JL_DLLEXPORT jl_value_t *name(jl_value_t *F, jl_value_t **args, uint32_t nargs) +JL_CALLABLE(jl_f_svec); JL_CALLABLE(jl_f_tuple); JL_CALLABLE(jl_f_intrinsic_call); JL_CALLABLE(jl_f_opaque_closure_call); @@ -1185,8 +1186,8 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt JL_PROPAGATES_RO JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo( jl_method_t *m JL_PROPAGATES_ROOT, jl_value_t *type, jl_svec_t *sparams); jl_method_instance_t *jl_specializations_get_or_insert(jl_method_instance_t *mi_ins); -JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_method_instance_t *caller); -JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller); +JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_code_instance_t *caller); +JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_code_instance_t *caller); JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); JL_DLLEXPORT int jl_mi_try_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, diff --git a/src/method.c b/src/method.c index 629816319b334..8e3bb7d0060b7 100644 --- a/src/method.c +++ b/src/method.c @@ -650,10 +650,10 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void) src->slotnames = NULL; src->slottypes = jl_nothing; src->rettype = (jl_value_t*)jl_any_type; + src->edges = (jl_value_t*)jl_emptysvec; src->parent = (jl_method_instance_t*)jl_nothing; src->min_world = 1; src->max_world = ~(size_t)0; - src->edges = jl_nothing; src->propagate_inbounds = 0; src->has_fcall = 0; src->nospecializeinfer = 0; @@ -754,9 +754,10 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *mi, size_t assert(jl_is_method(def)); jl_code_info_t *func = NULL; jl_value_t *ex = NULL; + jl_value_t *kind = NULL; jl_code_info_t *uninferred = NULL; jl_code_instance_t *ci = NULL; - JL_GC_PUSH4(&ex, &func, &uninferred, &ci); + JL_GC_PUSH5(&ex, &func, &uninferred, &ci, &kind); jl_task_t *ct = jl_current_task; int last_lineno = jl_lineno; int last_in = ct->ptls->in_pure_callback; @@ -792,6 +793,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *mi, size_t // but currently our isva determination is non-syntactic func->isva = def->isva; } + ex = NULL; // If this generated function has an opaque closure, cache it for // correctness of method identity. In particular, other methods that call @@ -815,7 +817,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *mi, size_t } } - if (func->edges == jl_nothing && func->max_world == ~(size_t)0) { + if ((func->edges == jl_nothing || func->edges == (jl_value_t*)jl_emptysvec) && func->max_world == ~(size_t)0) { if (func->min_world != 1) { jl_error("Generated function result with `edges == nothing` and `max_world == typemax(UInt)` must have `min_world == 1`"); } @@ -824,27 +826,41 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *mi, size_t if (cache || needs_cache_for_correctness) { uninferred = (jl_code_info_t*)jl_copy_ast((jl_value_t*)func); ci = jl_new_codeinst_for_uninferred(mi, uninferred); - - if (uninferred->edges != jl_nothing) { - // N.B.: This needs to match `store_backedges` on the julia side - jl_array_t *edges = (jl_array_t*)uninferred->edges; - for (size_t i = 0; i < jl_array_len(edges); ++i) { - jl_value_t *kind = jl_array_ptr_ref(edges, i); - if (jl_is_method_instance(kind)) { - jl_method_instance_add_backedge((jl_method_instance_t*)kind, jl_nothing, mi); - } else if (jl_is_mtable(kind)) { - jl_method_table_add_backedge((jl_methtable_t*)kind, jl_array_ptr_ref(edges, ++i), (jl_value_t*)mi); - } else { - jl_method_instance_add_backedge((jl_method_instance_t*)jl_array_ptr_ref(edges, ++i), kind, mi); - } - } - } - jl_code_instance_t *cached_ci = jl_cache_uninferred(mi, cache_ci, world, ci); if (cached_ci != ci) { func = (jl_code_info_t*)jl_copy_ast(jl_atomic_load_relaxed(&cached_ci->inferred)); assert(jl_is_code_info(func)); } + else if (uninferred->edges != jl_nothing) { + // N.B.: This needs to match `store_backedges` on the julia side + jl_value_t *edges = uninferred->edges; + size_t l; + jl_value_t **data; + if (jl_is_svec(edges)) { + l = jl_svec_len(edges); + data = jl_svec_data(edges); + } + else { + l = jl_array_dim0(edges); + data = jl_array_data(edges, jl_value_t*); + } + for (size_t i = 0; i < l; ) { + kind = data[i++]; + if (jl_is_method_instance(kind)) { + jl_method_instance_add_backedge((jl_method_instance_t*)kind, jl_nothing, ci); + } + else if (jl_is_mtable(kind)) { + assert(i < l); + ex = data[i++]; + jl_method_table_add_backedge((jl_methtable_t*)kind, ex, ci); + } + else { + assert(i < l); + ex = data[i++]; + jl_method_instance_add_backedge((jl_method_instance_t*)ex, kind, ci); + } + } + } if (cache) *cache = cached_ci; } @@ -1056,27 +1072,27 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) // it will be the signature supplied in an `invoke` call. // If you don't need `invokesig`, you can set it to NULL on input. // Initialize iteration with `i = 0`. Returns `i` for the next backedge to be extracted. -int get_next_edge(jl_array_t *list, int i, jl_value_t** invokesig, jl_method_instance_t **caller) JL_NOTSAFEPOINT +int get_next_edge(jl_array_t *list, int i, jl_value_t** invokesig, jl_code_instance_t **caller) JL_NOTSAFEPOINT { jl_value_t *item = jl_array_ptr_ref(list, i); - if (jl_is_method_instance(item)) { - // Not an `invoke` call, it's just the MethodInstance + if (jl_is_code_instance(item)) { + // Not an `invoke` call, it's just the CodeInstance if (invokesig != NULL) *invokesig = NULL; - *caller = (jl_method_instance_t*)item; + *caller = (jl_code_instance_t*)item; return i + 1; } assert(jl_is_type(item)); // An `invoke` call, it's a (sig, MethodInstance) pair if (invokesig != NULL) *invokesig = item; - *caller = (jl_method_instance_t*)jl_array_ptr_ref(list, i + 1); + *caller = (jl_code_instance_t*)jl_array_ptr_ref(list, i + 1); if (*caller) - assert(jl_is_method_instance(*caller)); + assert(jl_is_code_instance(*caller)); return i + 2; } -int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_method_instance_t *caller) +int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_code_instance_t *caller) { if (invokesig) jl_array_ptr_set(list, i++, invokesig); @@ -1084,7 +1100,7 @@ int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_method_inst return i; } -void push_edge(jl_array_t *list, jl_value_t *invokesig, jl_method_instance_t *caller) +void push_edge(jl_array_t *list, jl_value_t *invokesig, jl_code_instance_t *caller) { if (invokesig) jl_array_ptr_1d_push(list, invokesig); diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 9fe36f32d2030..65773f88a3951 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -113,8 +113,8 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t if (specptr == NULL) { jl_method_instance_t *mi_generic = jl_specializations_get_linfo(jl_opaque_closure_method, sigtype, jl_emptysvec); - // OC wrapper methods are not world dependent - ci = jl_get_method_inferred(mi_generic, selected_rt, 1, ~(size_t)0, NULL); + // OC wrapper methods are not world dependent and have no edges or other info + ci = jl_get_method_inferred(mi_generic, selected_rt, 1, ~(size_t)0, NULL, NULL); if (!jl_atomic_load_acquire(&ci->invoke)) jl_compile_codeinst(ci); specptr = jl_atomic_load_relaxed(&ci->specptr.fptr); @@ -145,7 +145,8 @@ JL_DLLEXPORT jl_opaque_closure_t *jl_new_opaque_closure_from_code_info(jl_tuplet { jl_value_t *root = NULL, *sigtype = NULL; jl_code_instance_t *inst = NULL; - JL_GC_PUSH3(&root, &sigtype, &inst); + jl_svec_t *edges = NULL; + JL_GC_PUSH4(&root, &sigtype, &inst, &edges); root = jl_box_long(lineno); root = jl_new_struct(jl_linenumbernode_type, root, file); jl_method_t *meth = jl_make_opaque_closure_method(mod, jl_nothing, nargs, root, ci, isva, isinferred); @@ -159,8 +160,11 @@ JL_DLLEXPORT jl_opaque_closure_t *jl_new_opaque_closure_from_code_info(jl_tuplet jl_value_t *argslotty = jl_array_ptr_ref(ci->slottypes, 0); sigtype = jl_argtype_with_function_type(argslotty, (jl_value_t*)argt); jl_method_instance_t *mi = jl_specializations_get_linfo((jl_method_t*)root, sigtype, jl_emptysvec); + edges = (jl_svec_t*)ci->edges; + if (!jl_is_svec(edges)) + edges = jl_emptysvec; // OC doesn't really have edges, so just drop them for now inst = jl_new_codeinst(mi, jl_nothing, rt_ub, (jl_value_t*)jl_any_type, NULL, (jl_value_t*)ci, - 0, world, world, 0, jl_nothing, 0, ci->debuginfo); + 0, world, world, 0, jl_nothing, 0, ci->debuginfo, edges); jl_mi_cache_insert(mi, inst); } diff --git a/src/serialize.h b/src/serialize.h index 3d3eb4df5e862..3aa82a1d09a9b 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -23,50 +23,52 @@ extern "C" { #define TAG_LONG_PHINODE 15 #define TAG_LONG_PHICNODE 16 #define TAG_METHODROOT 17 -#define TAG_STRING 18 -#define TAG_SHORT_INT64 19 -#define TAG_SHORT_GENERAL 20 -#define TAG_CNULL 21 -#define TAG_ARRAY1D 22 -#define TAG_SINGLETON 23 -#define TAG_MODULE 24 -#define TAG_TVAR 25 -#define TAG_METHOD_INSTANCE 26 -#define TAG_METHOD 27 -#define TAG_CODE_INSTANCE 28 -#define TAG_COMMONSYM 29 -#define TAG_NEARBYGLOBAL 30 -#define TAG_GLOBALREF 31 -#define TAG_CORE 32 -#define TAG_BASE 33 -#define TAG_BITYPENAME 34 -#define TAG_NEARBYMODULE 35 -#define TAG_INT32 36 -#define TAG_INT64 37 -#define TAG_UINT8 38 -#define TAG_VECTORTY 39 -#define TAG_PTRTY 40 -#define TAG_LONG_SSAVALUE 41 -#define TAG_LONG_METHODROOT 42 -#define TAG_SHORTER_INT64 43 -#define TAG_SHORT_INT32 44 -#define TAG_CALL1 45 -#define TAG_CALL2 46 -#define TAG_SHORT_BACKREF 47 -#define TAG_BACKREF 48 -#define TAG_UNIONALL 49 -#define TAG_GOTONODE 50 -#define TAG_QUOTENODE 51 -#define TAG_GENERAL 52 -#define TAG_GOTOIFNOT 53 -#define TAG_RETURNNODE 54 -#define TAG_ARGUMENT 55 -#define TAG_RELOC_METHODROOT 56 -#define TAG_BINDING 57 -#define TAG_MEMORYT 58 -#define TAG_ENTERNODE 59 - -#define LAST_TAG 59 +#define TAG_EDGE 18 +#define TAG_STRING 19 +#define TAG_SHORT_INT64 20 +#define TAG_SHORT_GENERAL 21 +#define TAG_CNULL 22 +#define TAG_ARRAY1D 23 +#define TAG_SINGLETON 24 +#define TAG_MODULE 25 +#define TAG_TVAR 26 +#define TAG_METHOD_INSTANCE 27 +#define TAG_METHOD 28 +#define TAG_CODE_INSTANCE 29 +#define TAG_COMMONSYM 30 +#define TAG_NEARBYGLOBAL 31 +#define TAG_GLOBALREF 32 +#define TAG_CORE 33 +#define TAG_BASE 34 +#define TAG_BITYPENAME 35 +#define TAG_NEARBYMODULE 36 +#define TAG_INT32 37 +#define TAG_INT64 38 +#define TAG_UINT8 39 +#define TAG_VECTORTY 40 +#define TAG_PTRTY 41 +#define TAG_LONG_SSAVALUE 42 +#define TAG_LONG_METHODROOT 43 +#define TAG_LONG_EDGE 44 +#define TAG_SHORTER_INT64 45 +#define TAG_SHORT_INT32 46 +#define TAG_CALL1 47 +#define TAG_CALL2 48 +#define TAG_SHORT_BACKREF 49 +#define TAG_BACKREF 50 +#define TAG_UNIONALL 51 +#define TAG_GOTONODE 52 +#define TAG_QUOTENODE 53 +#define TAG_GENERAL 54 +#define TAG_GOTOIFNOT 55 +#define TAG_RETURNNODE 56 +#define TAG_ARGUMENT 57 +#define TAG_RELOC_METHODROOT 58 +#define TAG_BINDING 59 +#define TAG_MEMORYT 60 +#define TAG_ENTERNODE 61 + +#define LAST_TAG 61 #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc((s))) diff --git a/src/staticdata.c b/src/staticdata.c index af3477a25128e..0d609db03aebc 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -548,9 +548,6 @@ typedef struct { jl_array_t *link_ids_gvars; jl_array_t *link_ids_external_fnvars; jl_ptls_t ptls; - // Set (implemented has a hasmap of MethodInstances to themselves) of which MethodInstances have (forward) edges - // to other MethodInstances. - htable_t callers_with_edges; jl_image_t *image; int8_t incremental; } jl_serializer_state; @@ -1767,6 +1764,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED if (s->incremental) { if (jl_atomic_load_relaxed(&ci->max_world) == ~(size_t)0) { if (jl_atomic_load_relaxed(&newci->min_world) > 1) { + //assert(jl_atomic_load_relaxed(&ci->edges) != jl_emptysvec); // some code (such as !==) might add a method lookup restriction but not keep the edges jl_atomic_store_release(&newci->min_world, ~(size_t)0); jl_atomic_store_release(&newci->max_world, WORLD_AGE_REVALIDATION_SENTINEL); arraylist_push(&s->fixup_objs, (void*)reloc_offset); @@ -2801,25 +2799,21 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert) } static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key, - /* outputs */ jl_array_t **extext_methods, jl_array_t **new_ext_cis, - jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges) + /* outputs */ jl_array_t **extext_methods JL_REQUIRE_ROOTED_SLOT, + jl_array_t **new_ext_cis JL_REQUIRE_ROOTED_SLOT, + jl_array_t **method_roots_list JL_REQUIRE_ROOTED_SLOT, + jl_array_t **edges JL_REQUIRE_ROOTED_SLOT) { // extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist - // ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods - // ordinary dispatch: invokesig=NULL, callee is MethodInstance - // `invoke` dispatch: invokesig is signature, callee is MethodInstance - // abstract call: callee is signature - // edges: [caller1, ext_targets_indexes1, ...] for worklist-owned methods calling external methods - assert(edges_map == NULL); + // edges: [caller1, ext_targets, ...] for worklist-owned methods calling external methods // Save the inferred code from newly inferred, external methods *new_ext_cis = queue_external_cis(newly_inferred); // Collect method extensions and edges data - JL_GC_PUSH1(&edges_map); - if (edges) - edges_map = jl_alloc_memory_any(0); *extext_methods = jl_alloc_vec_any(0); + internal_methods = jl_alloc_vec_any(0); + JL_GC_PUSH1(&internal_methods); jl_collect_methtable_from_mod(jl_type_type_mt, *extext_methods); jl_collect_methtable_from_mod(jl_nonfunction_mt, *extext_methods); size_t i, len = jl_array_len(mod_array); @@ -2832,18 +2826,14 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new if (edges) { size_t world = jl_atomic_load_acquire(&jl_world_counter); - jl_collect_missing_backedges(jl_type_type_mt); - jl_collect_missing_backedges(jl_nonfunction_mt); - // jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges. - // Process this to extract `edges` and `ext_targets`. - *ext_targets = jl_alloc_vec_any(0); - *edges = jl_alloc_vec_any(0); + // Extract `new_ext_cis` and `edges` now (from info prepared by jl_collect_methcache_from_mod) *method_roots_list = jl_alloc_vec_any(0); // Collect the new method roots for external specializations 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); + *edges = jl_alloc_vec_any(0); + jl_collect_internal_cis(*edges, world); } - assert(edges_map == NULL); // jl_collect_edges clears this when done + internal_methods = NULL; JL_GC_POP(); } @@ -2852,7 +2842,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_array_t *worklist, jl_array_t *extext_methods, jl_array_t *new_ext_cis, jl_array_t *method_roots_list, - jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED + jl_array_t *edges) JL_GC_DISABLED { htable_new(&field_replace, 0); htable_new(&bits_replace, 0); @@ -2972,7 +2962,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, s.link_ids_gctags = jl_alloc_array_1d(jl_array_int32_type, 0); s.link_ids_gvars = jl_alloc_array_1d(jl_array_int32_type, 0); s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_int32_type, 0); - htable_new(&s.callers_with_edges, 0); jl_value_t **const*const tags = get_tags(); // worklist == NULL ? get_tags() : NULL; @@ -3012,12 +3001,9 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // Queue the worklist itself as the first item we serialize jl_queue_for_serialization(&s, worklist); jl_queue_for_serialization(&s, jl_module_init_order); - // Classify the CodeInstances with respect to their need for validation - classify_callers(&s.callers_with_edges, edges); } // step 1.1: as needed, serialize the data needed for insertion into the running system if (extext_methods) { - assert(ext_targets); assert(edges); // Queue method extensions jl_queue_for_serialization(&s, extext_methods); @@ -3026,7 +3012,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // Queue the new roots jl_queue_for_serialization(&s, method_roots_list); // Queue the edges - jl_queue_for_serialization(&s, ext_targets); jl_queue_for_serialization(&s, edges); } jl_serialize_reachable(&s); @@ -3117,7 +3102,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, ); jl_exit(1); } - htable_free(&s.callers_with_edges); // step 3: combine all of the sections into one file assert(ios_pos(f) % JL_CACHE_BYTE_ALIGNMENT == 0); @@ -3206,7 +3190,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_write_value(&s, extext_methods); jl_write_value(&s, new_ext_cis); jl_write_value(&s, method_roots_list); - jl_write_value(&s, ext_targets); jl_write_value(&s, edges); } write_uint32(f, jl_array_len(s.link_ids_gctags)); @@ -3297,18 +3280,18 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_ext_cis = NULL; - jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL; + jl_array_t *method_roots_list = NULL, *edges = NULL; int64_t checksumpos = 0; int64_t checksumpos_ff = 0; int64_t datastartpos = 0; - JL_GC_PUSH6(&mod_array, &extext_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges); + JL_GC_PUSH5(&mod_array, &extext_methods, &new_ext_cis, &method_roots_list, &edges); if (worklist) { mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array) // Generate _native_data` if (_native_data != NULL) { jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_ext_cis, NULL, NULL, NULL); + &extext_methods, &new_ext_cis, NULL, NULL); jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1); *_native_data = jl_precompile_worklist(worklist, extext_methods, new_ext_cis); jl_precompile_toplevel_module = NULL; @@ -3341,7 +3324,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli 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); + &extext_methods, &new_ext_cis, &method_roots_list, &edges); if (!emit_split) { write_int32(f, 0); // No clone_targets write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f)); @@ -3353,7 +3336,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } if (_native_data != NULL) native_functions = *_native_data; - jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, ext_targets, edges); + jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, edges); if (_native_data != NULL) native_functions = NULL; if (worklist) @@ -3443,7 +3426,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl /* outputs */ jl_array_t **restored, jl_array_t **init_order, jl_array_t **extext_methods, jl_array_t **internal_methods, jl_array_t **new_ext_cis, jl_array_t **method_roots_list, - jl_array_t **ext_targets, jl_array_t **edges, + jl_array_t **edges, char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED { jl_task_t *ct = jl_current_task; @@ -3514,7 +3497,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl assert(!ios_eof(f)); s.s = f; uintptr_t offset_restored = 0, offset_init_order = 0, offset_extext_methods = 0, offset_new_ext_cis = 0, offset_method_roots_list = 0; - uintptr_t offset_ext_targets = 0, offset_edges = 0; + uintptr_t offset_edges = 0; if (!s.incremental) { size_t i; for (i = 0; tags[i] != NULL; i++) { @@ -3547,7 +3530,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl offset_extext_methods = jl_read_offset(&s); offset_new_ext_cis = jl_read_offset(&s); offset_method_roots_list = jl_read_offset(&s); - offset_ext_targets = jl_read_offset(&s); offset_edges = jl_read_offset(&s); } s.buildid_depmods_idxs = depmod_to_imageidx(depmods); @@ -3574,13 +3556,12 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl uint32_t external_fns_begin = read_uint32(f); jl_read_arraylist(s.s, ccallable_list ? ccallable_list : &s.ccallable_list); if (s.incremental) { - assert(restored && init_order && extext_methods && internal_methods && new_ext_cis && method_roots_list && ext_targets && edges); + assert(restored && init_order && extext_methods && internal_methods && new_ext_cis && method_roots_list && edges); *restored = (jl_array_t*)jl_delayed_reloc(&s, offset_restored); *init_order = (jl_array_t*)jl_delayed_reloc(&s, offset_init_order); *extext_methods = (jl_array_t*)jl_delayed_reloc(&s, offset_extext_methods); *new_ext_cis = (jl_array_t*)jl_delayed_reloc(&s, offset_new_ext_cis); *method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list); - *ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets); *edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges); *internal_methods = jl_alloc_vec_any(0); } @@ -4021,9 +4002,9 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i arraylist_t ccallable_list; jl_value_t *restored = NULL; - jl_array_t *init_order = NULL, *extext_methods = NULL, *internal_methods = NULL, *new_ext_cis = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL; + jl_array_t *init_order = NULL, *extext_methods = NULL, *internal_methods = NULL, *new_ext_cis = NULL, *method_roots_list = NULL, *edges = NULL; jl_svec_t *cachesizes_sv = NULL; - JL_GC_PUSH9(&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges, &cachesizes_sv); + JL_GC_PUSH8(&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, &edges, &cachesizes_sv); { // make a permanent in-memory copy of f (excluding the header) ios_bufmode(f, bm_none); @@ -4048,7 +4029,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i ios_static_buffer(f, sysimg, len); pkgcachesizes cachesizes; jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, - &ext_targets, &edges, &base, &ccallable_list, &cachesizes); + &edges, &base, &ccallable_list, &cachesizes); JL_SIGATOMIC_END(); // No special processing of `new_ext_cis` is required because recaching handled it @@ -4062,7 +4043,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i // allow users to start running in this updated world jl_atomic_store_release(&jl_world_counter, world); // but one of those immediate users is going to be our cache updates - jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_ext_cis, world); // restore external backedges (needs to be last) + jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)new_ext_cis, world); // restore external backedges (needs to be last) // now permit more methods to be added again JL_UNLOCK(&world_counter_lock); // reinit ccallables @@ -4078,9 +4059,9 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i jl_svecset(cachesizes_sv, 4, jl_box_long(cachesizes.reloclist)); jl_svecset(cachesizes_sv, 5, jl_box_long(cachesizes.gvarlist)); jl_svecset(cachesizes_sv, 6, jl_box_long(cachesizes.fptrlist)); - restored = (jl_value_t*)jl_svec(8, restored, init_order, extext_methods, + restored = (jl_value_t*)jl_svec(7, restored, init_order, extext_methods, new_ext_cis ? (jl_value_t*)new_ext_cis : jl_nothing, - method_roots_list, ext_targets, edges, cachesizes_sv); + method_roots_list, edges, cachesizes_sv); } else { restored = (jl_value_t*)jl_svec(2, restored, init_order); @@ -4095,7 +4076,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum) { JL_TIMING(LOAD_IMAGE, LOAD_Sysimg); - jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 9a7653972ea7c..b69c1edb5429b 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1,5 +1,5 @@ // inverse of backedges graph (caller=>callees hash) -jl_genericmemory_t *edges_map JL_GLOBALLY_ROOTED = NULL; // rooted for the duration of our uses of this +jl_array_t *internal_methods JL_GLOBALLY_ROOTED = NULL; // rooted for the duration of our uses of this static void write_float64(ios_t *s, double x) JL_NOTSAFEPOINT { @@ -185,9 +185,10 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, size_t i = 0, n = jl_array_nrows(mi->backedges); int cycle = depth; while (i < n) { - jl_method_instance_t *be; + jl_code_instance_t *be; i = get_next_edge(mi->backedges, i, NULL, &be); - int child_found = has_backedge_to_worklist(be, visited, stack); + JL_GC_PROMISE_ROOTED(be); // get_next_edge propagates the edge for us here + int child_found = has_backedge_to_worklist(be->def, visited, stack); if (child_found == 1 || child_found == 2) { // found what we were looking for, so terminate early found = 1; @@ -250,7 +251,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list) continue; jl_method_instance_t *mi = ci->def; jl_method_t *m = mi->def.method; - if (jl_atomic_load_relaxed(&ci->inferred) && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { + if (ci->owner == jl_nothing && jl_atomic_load_relaxed(&ci->inferred) && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { int found = has_backedge_to_worklist(mi, &visited, &stack); assert(found == 0 || found == 1 || found == 2); assert(stack.len == 0); @@ -318,82 +319,19 @@ static void jl_collect_new_roots(htable_t *relocatable_ext_cis, jl_array_t *root htable_free(&mset); } -// Create the forward-edge map (caller => callees) -// the intent of these functions is to invert the backedges tree -// for anything that points to a method not part of the worklist -// -// from MethodTables -static void jl_collect_missing_backedges(jl_methtable_t *mt) -{ - jl_array_t *backedges = mt->backedges; - if (backedges) { - size_t i, l = jl_array_nrows(backedges); - for (i = 1; i < l; i += 2) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(backedges, i); - jl_value_t *missing_callee = jl_array_ptr_ref(backedges, i - 1); // signature of abstract callee - jl_array_t *edges = (jl_array_t*)jl_eqtable_get(edges_map, (jl_value_t*)caller, NULL); - if (edges == NULL) { - edges = jl_alloc_vec_any(0); - JL_GC_PUSH1(&edges); - edges_map = jl_eqtable_put(edges_map, (jl_value_t*)caller, (jl_value_t*)edges, NULL); - JL_GC_POP(); - } - jl_array_ptr_1d_push(edges, NULL); - jl_array_ptr_1d_push(edges, missing_callee); - } - } -} - - -// from MethodInstances -static void collect_backedges(jl_method_instance_t *callee, int internal) -{ - jl_array_t *backedges = callee->backedges; - if (backedges) { - size_t i = 0, l = jl_array_nrows(backedges); - while (i < l) { - jl_value_t *invokeTypes; - jl_method_instance_t *caller; - i = get_next_edge(backedges, i, &invokeTypes, &caller); - jl_array_t *edges = (jl_array_t*)jl_eqtable_get(edges_map, (jl_value_t*)caller, NULL); - if (edges == NULL) { - edges = jl_alloc_vec_any(0); - JL_GC_PUSH1(&edges); - edges_map = jl_eqtable_put(edges_map, (jl_value_t*)caller, (jl_value_t*)edges, NULL); - JL_GC_POP(); - } - jl_array_ptr_1d_push(edges, invokeTypes); - jl_array_ptr_1d_push(edges, (jl_value_t*)callee); - } - } -} - -// For functions owned by modules not on the worklist, call this on each method. +// For every method: // - if the method is owned by a worklist module, add it to the list of things to be -// fully serialized -// - Collect all backedges (may be needed later when we invert this list). +// verified on reloading +// - if the method is extext, record that it needs to be reinserted later in the method table static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure) { jl_array_t *s = (jl_array_t*)closure; jl_method_t *m = ml->func.method; - if (s && !jl_object_in_image((jl_value_t*)m->module)) { - jl_array_ptr_1d_push(s, (jl_value_t*)m); - } - if (edges_map == NULL) - return 1; - jl_value_t *specializations = jl_atomic_load_relaxed(&m->specializations); - if (!jl_is_svec(specializations)) { - jl_method_instance_t *callee = (jl_method_instance_t*)specializations; - collect_backedges(callee, !s); - } - else { - size_t i, l = jl_svec_len(specializations); - for (i = 0; i < l; i++) { - jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(specializations, i); - if ((jl_value_t*)callee != jl_nothing) - collect_backedges(callee, !s); - } + if (!jl_object_in_image((jl_value_t*)m->module)) { + jl_array_ptr_1d_push(internal_methods, (jl_value_t*)m); + if (s) + jl_array_ptr_1d_push(s, (jl_value_t*)m); // extext } return 1; } @@ -401,10 +339,8 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure) static int jl_collect_methtable_from_mod(jl_methtable_t *mt, void *env) { if (!jl_object_in_image((jl_value_t*)mt)) - env = NULL; // do not collect any methods from here + env = NULL; // mark internal, not extext jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), jl_collect_methcache_from_mod, env); - if (env && edges_map) - jl_collect_missing_backedges(mt); return 1; } @@ -416,169 +352,38 @@ static void jl_collect_extext_methods_from_mod(jl_array_t *s, jl_module_t *m) foreach_mtable_in_module(m, jl_collect_methtable_from_mod, s); } -static void jl_record_edges(jl_method_instance_t *caller, arraylist_t *wq, jl_array_t *edges) +static void jl_record_edges(jl_method_instance_t *caller, jl_array_t *edges) { - jl_array_t *callees = NULL; - JL_GC_PUSH2(&caller, &callees); - callees = (jl_array_t*)jl_eqtable_pop(edges_map, (jl_value_t*)caller, NULL, NULL); - if (callees != NULL) { - jl_array_ptr_1d_push(edges, (jl_value_t*)caller); - jl_array_ptr_1d_push(edges, (jl_value_t*)callees); - size_t i, l = jl_array_nrows(callees); - for (i = 1; i < l; i += 2) { - jl_method_instance_t *c = (jl_method_instance_t*)jl_array_ptr_ref(callees, i); - if (c && jl_is_method_instance(c)) { - arraylist_push(wq, c); - } - } + jl_code_instance_t *ci = jl_atomic_load_relaxed(&caller->cache); + while (ci != NULL) { + if (jl_atomic_load_relaxed(&ci->edges) && + jl_atomic_load_relaxed(&ci->edges) != jl_emptysvec && + jl_atomic_load_relaxed(&ci->max_world) == ~(size_t)0) + jl_array_ptr_1d_push(edges, (jl_value_t*)ci); + ci = jl_atomic_load_relaxed(&ci->next); } - JL_GC_POP(); } - // Extract `edges` and `ext_targets` from `edges_map` -// `edges` = [caller1, targets_indexes1, ...], the list of methods and their edges -// `ext_targets` is [invokesig1, callee1, matches1, ...], the edges for each target -static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *external_cis, size_t world) +// `edges` = [caller1, ...], the list of codeinstances internal to methods +static void jl_collect_internal_cis(jl_array_t *edges, size_t world) { - htable_t external_mis; - htable_new(&external_mis, 0); - if (external_cis) { - for (size_t i = 0; i < jl_array_nrows(external_cis); i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(external_cis, i); - jl_method_instance_t *mi = ci->def; - ptrhash_put(&external_mis, (void*)mi, (void*)mi); - } - } - arraylist_t wq; - arraylist_new(&wq, 0); - void **table = (void**) edges_map->ptr; // edges_map is caller => callees - size_t table_size = edges_map->length; - for (size_t i = 0; i < table_size; i += 2) { - assert(table == edges_map->ptr && table_size == edges_map->length && - "edges_map changed during iteration"); - jl_method_instance_t *caller = (jl_method_instance_t*)table[i]; - jl_array_t *callees = (jl_array_t*)table[i + 1]; - if (callees == NULL) - continue; - assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); - if (!jl_object_in_image((jl_value_t*)caller->def.method->module) || - ptrhash_get(&external_mis, caller) != HT_NOTFOUND) { - jl_record_edges(caller, &wq, edges); + for (size_t i = 0; i < jl_array_nrows(internal_methods); i++) { + jl_method_t *m = (jl_method_t*)jl_array_ptr_ref(internal_methods, i); + jl_value_t *specializations = jl_atomic_load_relaxed(&m->specializations); + if (!jl_is_svec(specializations)) { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + jl_record_edges(mi, edges); } - } - htable_free(&external_mis); - while (wq.len) { - jl_method_instance_t *caller = (jl_method_instance_t*)arraylist_pop(&wq); - jl_record_edges(caller, &wq, edges); - } - arraylist_free(&wq); - edges_map = NULL; - htable_t edges_map2; - htable_new(&edges_map2, 0); - htable_t edges_ids; - size_t l = edges ? jl_array_nrows(edges) : 0; - htable_new(&edges_ids, l); - for (size_t i = 0; i < l / 2; i++) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, i * 2); - void *target = (void*)((char*)HT_NOTFOUND + i + 1); - ptrhash_put(&edges_ids, (void*)caller, target); - } - // process target list to turn it into a memoized validity table - // and compute the old methods list, ready for serialization - jl_value_t *matches = NULL; - jl_array_t *callee_ids = NULL; - jl_value_t *sig = NULL; - JL_GC_PUSH3(&matches, &callee_ids, &sig); - for (size_t i = 0; i < l; i += 2) { - jl_array_t *callees = (jl_array_t*)jl_array_ptr_ref(edges, i + 1); - size_t l = jl_array_nrows(callees); - callee_ids = jl_alloc_array_1d(jl_array_int32_type, l + 1); - int32_t *idxs = jl_array_data(callee_ids, int32_t); - idxs[0] = 0; - size_t nt = 0; - for (size_t j = 0; j < l; j += 2) { - jl_value_t *invokeTypes = jl_array_ptr_ref(callees, j); - jl_value_t *callee = jl_array_ptr_ref(callees, j + 1); - assert(callee && "unsupported edge"); - - if (jl_is_method_instance(callee)) { - jl_methtable_t *mt = jl_method_get_table(((jl_method_instance_t*)callee)->def.method); - if (!jl_object_in_image((jl_value_t*)mt)) - continue; - } - - // (nullptr, c) => call - // (invokeTypes, c) => invoke - // (nullptr, invokeTypes) => missing call - // (invokeTypes, nullptr) => missing invoke (unused--inferred as Any) - void *target = ptrhash_get(&edges_map2, invokeTypes ? (void*)invokeTypes : (void*)callee); - if (target == HT_NOTFOUND) { - size_t min_valid = 0; - size_t max_valid = ~(size_t)0; - if (invokeTypes) { - assert(jl_is_method_instance(callee)); - jl_method_t *m = ((jl_method_instance_t*)callee)->def.method; - matches = (jl_value_t*)m; // valid because there is no method replacement permitted -#ifndef NDEBUG - jl_methtable_t *mt = jl_method_get_table(m); - if ((jl_value_t*)mt != jl_nothing) { - jl_value_t *matches = jl_gf_invoke_lookup_worlds(invokeTypes, (jl_value_t*)mt, world, &min_valid, &max_valid); - if (matches != jl_nothing) { - assert(m == ((jl_method_match_t*)matches)->method); - } - } -#endif - } - else { - if (jl_is_method_instance(callee)) { - jl_method_instance_t *mi = (jl_method_instance_t*)callee; - sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); - } - else { - sig = callee; - } - int ambig = 0; - matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - INT32_MAX, 0, world, &min_valid, &max_valid, &ambig); - sig = NULL; - if (matches == jl_nothing) { - callee_ids = NULL; // invalid - break; - } - size_t k; - for (k = 0; k < jl_array_nrows(matches); k++) { - jl_method_match_t *match = (jl_method_match_t *)jl_array_ptr_ref(matches, k); - jl_array_ptr_set(matches, k, match->method); - } - } - jl_array_ptr_1d_push(ext_targets, invokeTypes); - jl_array_ptr_1d_push(ext_targets, callee); - jl_array_ptr_1d_push(ext_targets, matches); - target = (void*)((char*)HT_NOTFOUND + jl_array_nrows(ext_targets) / 3); - ptrhash_put(&edges_map2, (void*)callee, target); - } - idxs[++nt] = (char*)target - (char*)HT_NOTFOUND - 1; - } - jl_array_ptr_set(edges, i + 1, callee_ids); // swap callees for ids - if (!callee_ids) - continue; - idxs[0] = nt; - // record place of every method in edges - // add method edges to the callee_ids list - for (size_t j = 0; j < l; j += 2) { - jl_value_t *callee = jl_array_ptr_ref(callees, j + 1); - if (callee && jl_is_method_instance(callee)) { - void *target = ptrhash_get(&edges_ids, (void*)callee); - if (target != HT_NOTFOUND) { - idxs[++nt] = (char*)target - (char*)HT_NOTFOUND - 1; - } + else { + size_t j, l = jl_svec_len(specializations); + for (j = 0; j < l; j++) { + jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, j); + if ((jl_value_t*)mi != jl_nothing) + jl_record_edges(mi, edges); } } - jl_array_del_end(callee_ids, l - nt); } - JL_GC_POP(); - htable_free(&edges_map2); } // Headers @@ -946,374 +751,324 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) } } - -// verify that these edges intersect with the same methods as before -static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) +static size_t verify_invokesig(jl_value_t *invokesig, jl_method_t *expected, size_t minworld) { - JL_TIMING(VERIFY_IMAGE, VERIFY_Edges); - size_t i, l = jl_array_nrows(targets) / 3; - static jl_value_t *ulong_array JL_ALWAYS_LEAFTYPE = NULL; - if (ulong_array == NULL) - ulong_array = jl_apply_array_type((jl_value_t*)jl_ulong_type, 1); - jl_array_t *maxvalids = jl_alloc_array_1d(ulong_array, l); - memset(jl_array_data(maxvalids, size_t), 0, l * sizeof(size_t)); - jl_value_t *loctag = NULL; - jl_value_t *matches = NULL; - jl_value_t *sig = NULL; - JL_GC_PUSH4(&maxvalids, &matches, &sig, &loctag); - for (i = 0; i < l; i++) { - jl_value_t *invokesig = jl_array_ptr_ref(targets, i * 3); - jl_value_t *callee = jl_array_ptr_ref(targets, i * 3 + 1); - jl_value_t *expected = jl_array_ptr_ref(targets, i * 3 + 2); - size_t min_valid = 0; - size_t max_valid = ~(size_t)0; - if (invokesig) { - assert(callee && "unsupported edge"); - jl_method_t *m = ((jl_method_instance_t*)callee)->def.method; - if (jl_egal(invokesig, m->sig)) { - // the invoke match is `m` for `m->sig`, unless `m` is invalid - if (jl_atomic_load_relaxed(&m->deleted_world) < max_valid) - max_valid = 0; - } - else { - jl_methtable_t *mt = jl_method_get_table(m); - if ((jl_value_t*)mt == jl_nothing) { - max_valid = 0; - } - else { - matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, minworld, &min_valid, &max_valid); - if (matches == jl_nothing) { - max_valid = 0; - } - else { - matches = (jl_value_t*)((jl_method_match_t*)matches)->method; - if (matches != expected) { - max_valid = 0; - } - } - } - } + assert(jl_is_type(invokesig)); + assert(jl_is_method(expected)); + size_t min_valid = 0; + size_t max_valid = ~(size_t)0; + if (jl_egal(invokesig, expected->sig)) { + // the invoke match is `expected` for `expected->sig`, unless `expected` is invalid + if (jl_atomic_load_relaxed(&expected->deleted_world) < max_valid) + max_valid = 0; + } + else { + jl_methtable_t *mt = jl_method_get_table(expected); + if ((jl_value_t*)mt == jl_nothing) { + max_valid = 0; } else { - if (jl_is_method_instance(callee)) { - jl_method_instance_t *mi = (jl_method_instance_t*)callee; - sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); - } - else { - sig = callee; - } - assert(jl_is_array(expected)); - int ambig = 0; - // TODO: possibly need to included ambiguities too (for the optimizer correctness)? - // len + 1 is to allow us to log causes of invalidation (SnoopCompile's @snoopr) - matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - _jl_debug_method_invalidation ? INT32_MAX : jl_array_nrows(expected), - 0, minworld, &min_valid, &max_valid, &ambig); - sig = NULL; + jl_value_t *matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, minworld, &min_valid, &max_valid); if (matches == jl_nothing) { max_valid = 0; } else { - // setdiff!(matches, expected) - size_t j, k, ins = 0; - if (jl_array_nrows(matches) != jl_array_nrows(expected)) { + if (((jl_method_match_t*)matches)->method != expected) { max_valid = 0; } - for (k = 0; k < jl_array_nrows(matches); k++) { - jl_method_t *match = ((jl_method_match_t*)jl_array_ptr_ref(matches, k))->method; - size_t l = jl_array_nrows(expected); - for (j = 0; j < l; j++) - if (match == (jl_method_t*)jl_array_ptr_ref(expected, j)) - break; - if (j == l) { - // intersection has a new method or a method was - // deleted--this is now probably no good, just invalidate - // everything about it now - max_valid = 0; - if (!_jl_debug_method_invalidation) - break; - jl_array_ptr_set(matches, ins++, match); - } - } - if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) - jl_array_del_end((jl_array_t*)matches, jl_array_nrows(matches) - ins); } } - jl_array_data(maxvalids, size_t)[i] = max_valid; - if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) { - jl_array_ptr_1d_push(_jl_debug_method_invalidation, invokesig ? (jl_value_t*)invokesig : callee); - loctag = jl_cstr_to_string("insert_backedges_callee"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - loctag = jl_box_int32((int32_t)i); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, matches); - } - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)invokesig); - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)callee); - //ios_puts(max_valid == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); } - JL_GC_POP(); - return maxvalids; + return max_valid; } -// Combine all edges relevant to a method to initialize the maxvalids list -static jl_array_t *jl_verify_methods(jl_array_t *edges, jl_array_t *maxvalids) +static size_t verify_call(jl_value_t *sig, jl_svec_t *expecteds, size_t i, size_t n, size_t minworld, jl_value_t **matches JL_REQUIRE_ROOTED_SLOT) { - JL_TIMING(VERIFY_IMAGE, VERIFY_Methods); - jl_value_t *loctag = NULL; - jl_array_t *maxvalids2 = NULL; - JL_GC_PUSH2(&loctag, &maxvalids2); - size_t i, l = jl_array_nrows(edges) / 2; - maxvalids2 = jl_alloc_array_1d(jl_typeof(maxvalids), l); - size_t *maxvalids2_data = jl_array_data(maxvalids2, size_t); - memset(maxvalids2_data, 0, l * sizeof(size_t)); - for (i = 0; i < l; i++) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); - assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); - jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); - assert(jl_typetagis((jl_value_t*)callee_ids, jl_array_int32_type)); - if (callee_ids == NULL) { - // serializing the edges had failed - maxvalids2_data[i] = 0; + // verify that these edges intersect with the same methods as before + size_t min_valid = 0; + size_t max_valid = ~(size_t)0; + int ambig = 0; + // TODO: possibly need to included ambiguities too (for the optimizer correctness)? + jl_value_t *result = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, + _jl_debug_method_invalidation ? INT32_MAX : n, + 0, minworld, &min_valid, &max_valid, &ambig); + *matches = result; + if (result == jl_nothing) { + max_valid = 0; + } + else { + // setdiff!(result, expected) + size_t j, k, ins = 0; + if (jl_array_nrows(result) != n) { + max_valid = 0; } - else { - int32_t *idxs = jl_array_data(callee_ids, int32_t); - size_t j; - maxvalids2_data[i] = ~(size_t)0; - for (j = 0; j < idxs[0]; j++) { - int32_t idx = idxs[j + 1]; - size_t max_valid = jl_array_data(maxvalids, size_t)[idx]; - if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) { - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)caller); - loctag = jl_cstr_to_string("verify_methods"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - loctag = jl_box_int32((int32_t)idx); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + for (k = 0; k < jl_array_nrows(result); k++) { + jl_method_t *match = ((jl_method_match_t*)jl_array_ptr_ref(result, k))->method; + for (j = 0; j < n; j++) { + jl_value_t *t = jl_svecref(expecteds, j + i); + if (jl_is_code_instance(t)) + t = (jl_value_t*)((jl_code_instance_t*)t)->def; + jl_method_t *meth; + if (jl_is_method(t)) + meth = (jl_method_t*)t; + else { + assert(jl_is_method_instance(t)); + meth = ((jl_method_instance_t*)t)->def.method; } - if (max_valid < maxvalids2_data[i]) - maxvalids2_data[i] = max_valid; - if (max_valid == 0) + if (match == meth) + break; + } + if (j == n) { + // intersection has a new method or a method was + // deleted--this is now probably no good, just invalidate + // everything about it now + max_valid = 0; + if (!_jl_debug_method_invalidation) break; + jl_array_ptr_set(result, ins++, match); } } - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)caller); - //ios_puts(maxvalids2_data[i] == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); + if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) + jl_array_del_end((jl_array_t*)result, jl_array_nrows(result) - ins); } - JL_GC_POP(); - return maxvalids2; + return max_valid; } - -// Visit the entire call graph, starting from edges[idx] to determine if that method is valid -// Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable -// and slightly modified with an early termination option once the computation reaches its minimum -static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size_t idx, arraylist_t *visited, arraylist_t *stack) +// Test all edges relevant to a method: +//// Visit the entire call graph, starting from edges[idx] to determine if that method is valid +//// Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable +//// and slightly modified with an early termination option once the computation reaches its minimum +static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_t *maxworld, arraylist_t *stack, htable_t *visiting) { - if (maxvalids2_data[idx] == 0) { - visited->items[idx] = (void*)1; + size_t max_valid2 = jl_atomic_load_relaxed(&codeinst->max_world); + if (max_valid2 != WORLD_AGE_REVALIDATION_SENTINEL) { + *maxworld = max_valid2; return 0; } - size_t cycle = (size_t)visited->items[idx]; - if (cycle != 0) - return cycle - 1; // depth remaining - jl_value_t *cause = NULL; - arraylist_push(stack, (void*)idx); + assert(jl_is_method_instance(codeinst->def) && jl_is_method(codeinst->def->def.method)); + void **bp = ptrhash_bp(visiting, codeinst); + if (*bp != HT_NOTFOUND) + return (char*)*bp - (char*)HT_NOTFOUND; // cycle idx + arraylist_push(stack, (void*)codeinst); size_t depth = stack->len; - visited->items[idx] = (void*)(1 + depth); - jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, idx * 2 + 1); - assert(jl_typetagis((jl_value_t*)callee_ids, jl_array_int32_type)); - int32_t *idxs = jl_array_data(callee_ids, int32_t); - size_t i, n = jl_array_nrows(callee_ids); - cycle = depth; - for (i = idxs[0] + 1; i < n; i++) { - int32_t childidx = idxs[i]; - int child_cycle = jl_verify_graph_edge(maxvalids2_data, edges, childidx, visited, stack); - size_t child_max_valid = maxvalids2_data[childidx]; - if (child_max_valid < maxvalids2_data[idx]) { - maxvalids2_data[idx] = child_max_valid; - cause = jl_array_ptr_ref(edges, childidx * 2); + *bp = (char*)HT_NOTFOUND + depth; + JL_TIMING(VERIFY_IMAGE, VERIFY_Methods); + jl_value_t *loctag = NULL; + jl_value_t *sig = NULL; + jl_value_t *matches = NULL; + JL_GC_PUSH3(&loctag, &matches, &sig); + jl_svec_t *callees = jl_atomic_load_relaxed(&codeinst->edges); + assert(jl_is_svec((jl_value_t*)callees)); + // verify current edges + for (size_t j = 0; j < jl_svec_len(callees); ) { + jl_value_t *edge = jl_svecref(callees, j); + size_t max_valid2; + assert(!jl_is_method(edge)); // `Method`-edge isn't allowed for the optimized one-edge format + if (jl_is_code_instance(edge)) + edge = (jl_value_t*)((jl_code_instance_t*)edge)->def; + if (jl_is_method_instance(edge)) { + jl_method_instance_t *mi = (jl_method_instance_t*)edge; + sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); // TODO: ?? + max_valid2 = verify_call(sig, callees, j, 1, minworld, &matches); + sig = NULL; + j += 1; } - if (child_max_valid == 0) { - // found what we were looking for, so terminate early - break; + else if (jl_is_long(edge)) { + jl_value_t *sig = jl_svecref(callees, j + 1); + size_t nedges = jl_unbox_long(edge); + max_valid2 = verify_call(sig, callees, j + 2, nedges, minworld, &matches); + j += 2 + nedges; + edge = sig; } - else if (child_cycle && child_cycle < cycle) { - // record the cycle will resolve at depth "cycle" - cycle = child_cycle; + else if (jl_is_mtable(edge)) { + // skip the legacy edge (missing backedge) + j += 2; + continue; + } + else { + jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(callees, j + 1); + jl_method_t *meth; + if (jl_is_code_instance(callee)) + callee = ((jl_code_instance_t*)callee)->def; + if (jl_is_method_instance(callee)) + meth = callee->def.method; + else { + assert(jl_is_method(callee)); + meth = (jl_method_t*)callee; + } + max_valid2 = verify_invokesig(edge, meth, minworld); + j += 2; + } + if (*maxworld > max_valid2) + *maxworld = max_valid2; + if (max_valid2 != ~(size_t)0 && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, edge); + loctag = jl_cstr_to_string("insert_backedges_callee"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)codeinst); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, matches); + } + //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)edge); + //ios_puts(max_valid2 == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); + if (max_valid2 == 0 && !_jl_debug_method_invalidation) + break; + } + JL_GC_POP(); + // verify recursive edges (if valid, or debugging) + size_t cycle = depth; + jl_code_instance_t *cause = codeinst; + if (*maxworld == ~(size_t)0 || _jl_debug_method_invalidation) { + for (size_t j = 0; j < jl_svec_len(callees); j++) { + jl_value_t *edge = jl_svecref(callees, j); + if (!jl_is_code_instance(edge)) + continue; + jl_code_instance_t *callee = (jl_code_instance_t*)edge; + size_t max_valid2 = ~(size_t)0; + size_t child_cycle = jl_verify_method(callee, minworld, &max_valid2, stack, visiting); + if (*maxworld > max_valid2) { + cause = callee; + *maxworld = max_valid2; + } + if (max_valid2 == 0) { + // found what we were looking for, so terminate early + break; + } + else if (child_cycle && child_cycle < cycle) { + // record the cycle will resolve at depth "cycle" + cycle = child_cycle; + } } } - size_t max_valid = maxvalids2_data[idx]; - if (max_valid != 0 && cycle != depth) + if (*maxworld != 0 && cycle != depth) return cycle; // If we are the top of the current cycle, now mark all other parts of // our cycle with what we found. // Or if we found a failed edge, also mark all of the other parts of the - // cycle as also having an failed edge. + // cycle as also having a failed edge. while (stack->len >= depth) { - size_t childidx = (size_t)arraylist_pop(stack); - assert(visited->items[childidx] == (void*)(2 + stack->len)); - if (idx != childidx) { - if (max_valid < maxvalids2_data[childidx]) - maxvalids2_data[childidx] = max_valid; - } - visited->items[childidx] = (void*)1; - if (_jl_debug_method_invalidation && max_valid != ~(size_t)0) { - jl_method_instance_t *mi = (jl_method_instance_t*)jl_array_ptr_ref(edges, childidx * 2); - jl_value_t *loctag = NULL; - JL_GC_PUSH1(&loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + jl_code_instance_t *child = (jl_code_instance_t*)arraylist_pop(stack); + if (*maxworld != jl_atomic_load_relaxed(&child->max_world)) + jl_atomic_store_relaxed(&child->max_world, *maxworld); + void **bp = ptrhash_bp(visiting, codeinst); + assert(*bp == (char*)HT_NOTFOUND + stack->len + 1); + *bp = HT_NOTFOUND; + if (_jl_debug_method_invalidation && *maxworld != ~(size_t)0) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)child); loctag = jl_cstr_to_string("verify_methods"); + JL_GC_PUSH1(&loctag); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)cause); JL_GC_POP(); } } + //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)codeinst->def); + //ios_puts(max_valid == ~(size_t)0 ? "valid\n\n" : "INVALID\n\n", ios_stderr); return 0; } -// Visit all entries in edges, verify if they are valid -static void jl_verify_graph(jl_array_t *edges, jl_array_t *maxvalids2) +static size_t jl_verify_method_graph(jl_code_instance_t *codeinst, size_t minworld, arraylist_t *stack, htable_t *visiting) { - JL_TIMING(VERIFY_IMAGE, VERIFY_Graph); - arraylist_t stack, visited; - arraylist_new(&stack, 0); - size_t i, n = jl_array_nrows(edges) / 2; - arraylist_new(&visited, n); - memset(visited.items, 0, n * sizeof(size_t)); - size_t *maxvalids2_data = jl_array_data(maxvalids2, size_t); - for (i = 0; i < n; i++) { - assert(visited.items[i] == (void*)0 || visited.items[i] == (void*)1); - int child_cycle = jl_verify_graph_edge(maxvalids2_data, edges, i, &visited, &stack); - assert(child_cycle == 0); (void)child_cycle; - assert(stack.len == 0); - assert(visited.items[i] == (void*)1); + assert(stack->len == 0); + for (size_t i = 0, hsz = visiting->size; i < hsz; i++) + assert(visiting->table[i] == HT_NOTFOUND); + size_t maxworld = ~(size_t)0; + int child_cycle = jl_verify_method(codeinst, minworld, &maxworld, stack, visiting); + assert(child_cycle == 0); (void)child_cycle; + assert(stack->len == 0); + for (size_t i = 0, hsz = visiting->size / 2; i < hsz; i++) { + assert(visiting->table[2 * i + 1] == HT_NOTFOUND); + visiting->table[2 * i] = HT_NOTFOUND; } - arraylist_free(&stack); - arraylist_free(&visited); + return maxworld; } // Restore backedges to external targets -// `edges` = [caller1, targets_indexes1, ...], the list of worklist-owned methods calling external methods. -// `ext_targets` is [invokesig1, callee1, matches1, ...], the global set of non-worklist callees of worklist-owned methods. -static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *ext_ci_list, size_t minworld) +// `edges` = [caller1, ...], the list of worklist-owned code instances internally +// `ext_ci_list` = [caller1, ...], the list of worklist-owned code instances externally +static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_ci_list, size_t minworld) { // determine which CodeInstance objects are still valid in our image - jl_array_t *valids = jl_verify_edges(ext_targets, minworld); - JL_GC_PUSH1(&valids); - valids = jl_verify_methods(edges, valids); // consumes edges valids, initializes methods valids - jl_verify_graph(edges, valids); // propagates methods valids for each edge - - size_t n_ext_cis = ext_ci_list ? jl_array_nrows(ext_ci_list) : 0; - htable_t cis_pending_validation; - htable_new(&cis_pending_validation, n_ext_cis); - - // next build a map from external MethodInstances to their CodeInstance for insertion - for (size_t i = 0; i < n_ext_cis; i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(ext_ci_list, i); - if (jl_atomic_load_relaxed(&ci->max_world) == WORLD_AGE_REVALIDATION_SENTINEL) { - assert(jl_atomic_load_relaxed(&ci->min_world) == minworld); - void **bp = ptrhash_bp(&cis_pending_validation, (void*)ci->def); - assert(!jl_atomic_load_relaxed(&ci->next)); - if (*bp == HT_NOTFOUND) - *bp = (void*)ci; - else { - // Do ci->owner bifurcates the cache, we temporarily - // form a linked list of all the CI that need to be connected later - jl_code_instance_t *prev_ci = (jl_code_instance_t *)*bp; - jl_atomic_store_relaxed(&ci->next, prev_ci); - *bp = (void*)ci; - } - } - else { - assert(jl_atomic_load_relaxed(&ci->min_world) == 1); - assert(jl_atomic_load_relaxed(&ci->max_world) == ~(size_t)0); - jl_method_instance_t *caller = ci->def; - if (jl_atomic_load_relaxed(&ci->inferred) && jl_rettype_inferred(ci->owner, caller, minworld, ~(size_t)0) == jl_nothing) { - jl_mi_cache_insert(caller, ci); - } - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)caller); - //ios_puts("free\n", ios_stderr); - } - } - - // next enable any applicable new codes - size_t nedges = jl_array_nrows(edges) / 2; - for (size_t i = 0; i < nedges; i++) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); - size_t maxvalid = jl_array_data(valids, size_t)[i]; - if (maxvalid == ~(size_t)0) { - // if this callee is still valid, add all the backedges - jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); - int32_t *idxs = jl_array_data(callee_ids, int32_t); - for (size_t j = 0; j < idxs[0]; j++) { - int32_t idx = idxs[j + 1]; - jl_value_t *invokesig = jl_array_ptr_ref(ext_targets, idx * 3); - jl_value_t *callee = jl_array_ptr_ref(ext_targets, idx * 3 + 1); - if (callee && jl_is_method_instance(callee)) { - jl_method_instance_add_backedge((jl_method_instance_t*)callee, invokesig, caller); + // to enable any applicable new codes + arraylist_t stack; + arraylist_new(&stack, 0); + htable_t visiting; + htable_new(&visiting, 0); + for (size_t external = 0; external < (ext_ci_list ? 2 : 1); external++) { + if (external) + edges = ext_ci_list; + size_t nedges = jl_array_nrows(edges); + for (size_t i = 0; i < nedges; i++) { + jl_code_instance_t *codeinst = (jl_code_instance_t*)jl_array_ptr_ref(edges, i); + jl_svec_t *callees = jl_atomic_load_relaxed(&codeinst->edges); + jl_method_instance_t *caller = codeinst->def; + if (jl_atomic_load_relaxed(&codeinst->min_world) != minworld) { + if (external && jl_atomic_load_relaxed(&codeinst->max_world) != WORLD_AGE_REVALIDATION_SENTINEL) { + assert(jl_atomic_load_relaxed(&codeinst->min_world) == 1); + assert(jl_atomic_load_relaxed(&codeinst->max_world) == ~(size_t)0); } else { - jl_value_t *sig = callee == NULL ? invokesig : callee; - jl_methtable_t *mt = jl_method_table_for(sig); - // FIXME: rarely, `callee` has an unexpected `Union` signature, - // see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1030329344 - // Fix the issue and turn this back into an `assert((jl_value_t*)mt != jl_nothing)` - // This workaround exposes us to (rare) 265-violations. - if ((jl_value_t*)mt != jl_nothing) - jl_method_table_add_backedge(mt, sig, (jl_value_t*)caller); + continue; } } - } - // then enable any methods associated with it - void *ci = ptrhash_get(&cis_pending_validation, (void*)caller); - //assert(ci != HT_NOTFOUND); - if (ci != HT_NOTFOUND) { - // Update any external CIs and add them to the cache. - assert(jl_is_code_instance(ci)); - jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - while (codeinst) { - jl_code_instance_t *next_ci = jl_atomic_load_relaxed(&codeinst->next); - jl_atomic_store_relaxed(&codeinst->next, NULL); - - jl_value_t *owner = codeinst->owner; - JL_GC_PROMISE_ROOTED(owner); - - assert(jl_atomic_load_relaxed(&codeinst->min_world) == minworld); - // See #53586, #53109 - // assert(jl_atomic_load_relaxed(&codeinst->max_world) == WORLD_AGE_REVALIDATION_SENTINEL); - assert(jl_atomic_load_relaxed(&codeinst->inferred)); - jl_atomic_store_relaxed(&codeinst->max_world, maxvalid); - - if (jl_rettype_inferred(owner, caller, minworld, maxvalid) != jl_nothing) { - // We already got a code instance for this world age range from somewhere else - we don't need - // this one. - } else { - jl_mi_cache_insert(caller, codeinst); + size_t maxvalid = jl_verify_method_graph(codeinst, minworld, &stack, &visiting); + assert(jl_atomic_load_relaxed(&codeinst->max_world) == maxvalid); + if (maxvalid == ~(size_t)0) { + // if this callee is still valid, add all the backedges + for (size_t j = 0; j < jl_svec_len(callees); ) { + jl_value_t *edge = jl_svecref(callees, j); + if (jl_is_long(edge)) { + j += 2; // skip over signature and count but not methods + continue; + } + else if (jl_is_method(edge)) { + j += 1; + continue; + } + if (jl_is_code_instance(edge)) + edge = (jl_value_t*)((jl_code_instance_t*)edge)->def; + if (jl_is_method_instance(edge)) { + jl_method_instance_add_backedge((jl_method_instance_t*)edge, NULL, codeinst); + j += 1; + } + else if (jl_is_mtable(edge)) { + jl_methtable_t *mt = (jl_methtable_t*)edge; + jl_value_t *sig = jl_svecref(callees, j + 1); + jl_method_table_add_backedge(mt, sig, codeinst); + j += 2; + } + else { + jl_value_t *callee = jl_svecref(callees, j + 1); + if (jl_is_code_instance(callee)) + callee = (jl_value_t*)((jl_code_instance_t*)callee)->def; + else if (jl_is_method(callee)) { + j += 2; + continue; + } + jl_method_instance_add_backedge((jl_method_instance_t*)callee, edge, codeinst); + j += 2; + } } - codeinst = next_ci; - } - } - else { - // Likely internal. Find the CI already in the cache hierarchy. - for (jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&caller->cache); codeinst; codeinst = jl_atomic_load_relaxed(&codeinst->next)) { - if (jl_atomic_load_relaxed(&codeinst->min_world) == minworld && jl_atomic_load_relaxed(&codeinst->max_world) == WORLD_AGE_REVALIDATION_SENTINEL) { - jl_atomic_store_relaxed(&codeinst->max_world, maxvalid); + if (external) { + jl_value_t *owner = codeinst->owner; + JL_GC_PROMISE_ROOTED(owner); + + // See #53586, #53109 + assert(jl_atomic_load_relaxed(&codeinst->inferred)); + + if (jl_rettype_inferred(owner, caller, minworld, maxvalid) != jl_nothing) { + // We already got a code instance for this world age range from somewhere else - we don't need + // this one. + } + else { + jl_mi_cache_insert(caller, codeinst); + } } } } } - htable_free(&cis_pending_validation); - - JL_GC_POP(); -} -static void classify_callers(htable_t *callers_with_edges, jl_array_t *edges) -{ - size_t l = edges ? jl_array_nrows(edges) / 2 : 0; - for (size_t i = 0; i < l; i++) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); - ptrhash_put(callers_with_edges, (void*)caller, (void*)caller); - } + htable_free(&visiting); + arraylist_free(&stack); } static jl_value_t *read_verify_mod_list(ios_t *s, jl_array_t *depmods) diff --git a/src/toplevel.c b/src/toplevel.c index 6dcab3095e320..017d61bbc8ceb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -640,7 +640,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst_for_uninferred(jl_method_instan jl_code_instance_t *ci = jl_new_codeinst(mi, (jl_value_t*)jl_uninferred_sym, (jl_value_t*)jl_any_type, (jl_value_t*)jl_any_type, jl_nothing, (jl_value_t*)src, 0, src->min_world, src->max_world, - 0, NULL, 1, NULL); + 0, NULL, 1, NULL, NULL); return ci; } diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index d59a18e6d4f16..67191a024da73 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -588,7 +588,7 @@ CC.cache_owner(::REPLInterpreter) = REPLCacheToken() CC.may_optimize(::REPLInterpreter) = false # REPLInterpreter doesn't need any sources to be cached, so discard them aggressively -CC.transform_result_for_cache(::REPLInterpreter, ::Core.MethodInstance, ::CC.WorldRange, ::CC.InferenceResult) = nothing +CC.transform_result_for_cache(::REPLInterpreter, ::CC.InferenceResult) = nothing # REPLInterpreter analyzes a top-level frame, so better to not bail out from it CC.bail_out_toplevel_call(::REPLInterpreter, ::CC.InferenceLoopState, ::CC.InferenceState) = false @@ -673,8 +673,8 @@ function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f), sv::CC.InferenceState) if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv)) neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE) - result = CC.MethodCallResult(result.rt, result.exct, result.edgecycle, result.edgelimited, - result.edge, neweffects) + result = CC.MethodCallResult(result.rt, result.exct, neweffects, result.edge, + result.edgecycle, result.edgelimited, result.volatile_inf_result) end ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any, result::CC.MethodCallResult, arginfo::CC.ArgInfo, diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 009128b289ade..a49647ad4ea43 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -15,7 +15,7 @@ CC.may_optimize(::AbsIntOnlyInterp1) = false # it should work even if the interpreter discards inferred source entirely @newinterp AbsIntOnlyInterp2 CC.may_optimize(::AbsIntOnlyInterp2) = false -CC.transform_result_for_cache(::AbsIntOnlyInterp2, ::Core.MethodInstance, ::CC.WorldRange, ::CC.InferenceResult) = nothing +CC.transform_result_for_cache(::AbsIntOnlyInterp2, ::CC.InferenceResult) = nothing @test Base.infer_return_type(Base.init_stdio, (Ptr{Cvoid},); interp=AbsIntOnlyInterp2()) >: IO # OverlayMethodTable @@ -406,10 +406,10 @@ import .CC: CallInfo struct NoinlineCallInfo <: CallInfo info::CallInfo # wrapped call end +CC.add_edges_impl(edges::Vector{Any}, info::NoinlineCallInfo) = CC.add_edges!(edges, info.info) CC.nsplit_impl(info::NoinlineCallInfo) = CC.nsplit(info.info) CC.getsplit_impl(info::NoinlineCallInfo, idx::Int) = CC.getsplit(info.info, idx) CC.getresult_impl(info::NoinlineCallInfo, idx::Int) = CC.getresult(info.info, idx) -CC.add_uncovered_edges_impl(edges::Vector{Any}, info::NoinlineCallInfo, @nospecialize(atype)) = CC.add_uncovered_edges!(edges, info.info, atype) function CC.abstract_call(interp::NoinlineInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Int) @@ -497,10 +497,9 @@ struct CustomData inferred CustomData(@nospecialize inferred) = new(inferred) end -function CC.transform_result_for_cache(interp::CustomDataInterp, - mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult) - inferred_result = @invoke CC.transform_result_for_cache(interp::CC.AbstractInterpreter, - mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult) +function CC.transform_result_for_cache(interp::CustomDataInterp, result::CC.InferenceResult) + inferred_result = @invoke CC.transform_result_for_cache( + interp::CC.AbstractInterpreter, result::CC.InferenceResult) return CustomData(inferred_result) end function CC.src_inlining_policy(interp::CustomDataInterp, @nospecialize(src), diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index 1f0a84f1a8365..c71b821fd25f3 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -11,9 +11,8 @@ const EA = EscapeAnalysis # imports import .CC: - AbstractInterpreter, NativeInterpreter, WorldView, WorldRange, - InferenceParams, OptimizationParams, get_world_counter, get_inference_cache, - ipo_dataflow_analysis!, cache_result! + AbstractInterpreter, NativeInterpreter, WorldView, WorldRange, InferenceParams, + OptimizationParams, get_world_counter, get_inference_cache, ipo_dataflow_analysis! # usings using Core: CodeInstance, MethodInstance, CodeInfo diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index fc91a37c5bd9e..8d526fdefdc5b 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -88,8 +88,8 @@ module MiniCassette src = retrieve_code_info(mi, world) @assert isa(src, CodeInfo) src = copy(src) - @assert src.edges === nothing - src.edges = MethodInstance[mi] + @assert src.edges === Core.svec() + src.edges = Any[mi] transform!(mi, src, length(args), match.sparams) # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) diff --git a/test/compiler/invalidation.jl b/test/compiler/invalidation.jl index 76cf3cbdc0796..55faa4287da24 100644 --- a/test/compiler/invalidation.jl +++ b/test/compiler/invalidation.jl @@ -95,7 +95,8 @@ end const GLOBAL_BUFFER = IOBuffer() # test backedge optimization when the callee's type and effects information are maximized -begin take!(GLOBAL_BUFFER) +begin + take!(GLOBAL_BUFFER) pr48932_callee(x) = (print(GLOBAL_BUFFER, x); Base.inferencebarrier(x)) pr48932_caller(x) = pr48932_callee(Base.inferencebarrier(x)) @@ -150,11 +151,11 @@ begin take!(GLOBAL_BUFFER) ci = mi.cache @test isdefined(ci, :next) @test ci.owner === nothing - @test ci.max_world == typemax(UInt) + @test_broken ci.max_world == typemax(UInt) ci = ci.next @test !isdefined(ci, :next) @test ci.owner === InvalidationTesterToken() - @test ci.max_world == typemax(UInt) + @test_broken ci.max_world == typemax(UInt) end @test isnothing(pr48932_caller(42)) @@ -213,11 +214,11 @@ begin take!(GLOBAL_BUFFER) ci = mi.cache @test isdefined(ci, :next) @test ci.owner === nothing - @test ci.max_world == typemax(UInt) + @test_broken ci.max_world == typemax(UInt) ci = ci.next @test !isdefined(ci, :next) @test ci.owner === InvalidationTesterToken() - @test ci.max_world == typemax(UInt) + @test_broken ci.max_world == typemax(UInt) end @test isnothing(pr48932_caller_unuse(42)) @test "foo" == String(take!(GLOBAL_BUFFER)) diff --git a/test/core.jl b/test/core.jl index 5ba0e99e730d4..4b5a674ba44b3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -32,7 +32,7 @@ end # sanity tests that our built-in types are marked correctly for atomic fields for (T, c) in ( (Core.CodeInfo, []), - (Core.CodeInstance, [:next, :min_world, :max_world, :inferred, :debuginfo, :ipo_purity_bits, :invoke, :specptr, :specsigflags, :precompile]), + (Core.CodeInstance, [:next, :min_world, :max_world, :inferred, :edges, :debuginfo, :ipo_purity_bits, :invoke, :specptr, :specsigflags, :precompile]), (Core.Method, [:primary_world, :deleted_world]), (Core.MethodInstance, [:cache, :flags]), (Core.MethodTable, [:defs, :leafcache, :cache, :max_args]), diff --git a/test/precompile.jl b/test/precompile.jl index adf10363298ba..1607d4c6b502b 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -823,6 +823,7 @@ precompile_test_harness("code caching") do dir mispecs = minternal.specializations::Core.SimpleVector @test mispecs[1] === mi mi = mispecs[2]::Core.MethodInstance + mi.specTypes == Tuple{typeof(M.getelsize),Vector{M.X2}} ci = mi.cache @test ci.relocatability == 0 # PkgA loads PkgB, and both add roots to the same `push!` method (both before and after loading B) @@ -914,7 +915,7 @@ precompile_test_harness("code caching") do dir # external callers mods = Module[] for be in mi.backedges - push!(mods, be.def.module) + push!(mods, (be.def.def::Method).module) # XXX end @test MA ∈ mods @test MB ∈ mods @@ -923,7 +924,7 @@ precompile_test_harness("code caching") do dir # internal callers meths = Method[] for be in mi.backedges - push!(meths, be.def) + push!(meths, (be.def::Method).def) # XXX end @test which(M.g1, ()) ∈ meths @test which(M.g2, ()) ∈ meths @@ -1056,11 +1057,11 @@ precompile_test_harness("code caching") do dir idxs = findall(==("verify_methods"), invalidations) idxsbits = filter(idxs) do i mi = invalidations[i-1] - mi.def == m + mi.def.def === m end idx = only(idxsbits) tagbad = invalidations[idx+1] - @test isa(tagbad, Int32) + @test isa(tagbad, Core.CodeInstance) j = findfirst(==(tagbad), invalidations) @test invalidations[j-1] == "insert_backedges_callee" @test isa(invalidations[j-2], Type) @@ -1068,7 +1069,7 @@ precompile_test_harness("code caching") do dir m = only(methods(MB.useA2)) mi = only(Base.specializations(m)) @test !hasvalid(mi, world) - @test mi ∈ invalidations + @test any(x -> x isa Core.CodeInstance && x.def === mi, invalidations) m = only(methods(MB.map_nbits)) @test !hasvalid(m.specializations::Core.MethodInstance, world+1) # insert_backedges invalidations also trigger their backedges @@ -1178,12 +1179,7 @@ precompile_test_harness("invoke") do dir @eval using $CallerModule M = getfield(@__MODULE__, CallerModule) - function get_method_for_type(func, @nospecialize(T)) # return the method func(::T) - for m in methods(func) - m.sig.parameters[end] === T && return m - end - error("no ::Real method found for $func") - end + get_method_for_type(func, @nospecialize(T)) = which(func, (T,)) # return the method func(::T) function nvalid(mi::Core.MethodInstance) isdefined(mi, :cache) || return 0 ci = mi.cache @@ -1200,7 +1196,7 @@ precompile_test_harness("invoke") do dir mi = m.specializations::Core.MethodInstance @test length(mi.backedges) == 2 @test mi.backedges[1] === Tuple{typeof(func), Real} - @test isa(mi.backedges[2], Core.MethodInstance) + @test isa(mi.backedges[2], Core.CodeInstance) @test mi.cache.max_world == typemax(mi.cache.max_world) end for func in (M.q, M.qnc) @@ -1208,18 +1204,18 @@ precompile_test_harness("invoke") do dir mi = m.specializations::Core.MethodInstance @test length(mi.backedges) == 2 @test mi.backedges[1] === Tuple{typeof(func), Integer} - @test isa(mi.backedges[2], Core.MethodInstance) + @test isa(mi.backedges[2], Core.CodeInstance) @test mi.cache.max_world == typemax(mi.cache.max_world) end m = get_method_for_type(M.h, Real) - @test isempty(Base.specializations(m)) + @test nvalid(m.specializations::Core.MethodInstance) == 0 m = get_method_for_type(M.hnc, Real) - @test isempty(Base.specializations(m)) + @test nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callq)) - @test isempty(Base.specializations(m)) || nvalid(m.specializations::Core.MethodInstance) == 0 + @test nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqnc)) - @test isempty(Base.specializations(m)) || nvalid(m.specializations::Core.MethodInstance) == 0 + @test nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqi)) @test (m.specializations::Core.MethodInstance).specTypes == Tuple{typeof(M.callqi), Int} m = only(methods(M.callqnci)) @@ -1733,7 +1729,7 @@ precompile_test_harness("issue #46296") do load_path mi = first(Base.specializations(first(methods(identity)))) ci = Core.CodeInstance(mi, nothing, Any, Any, nothing, nothing, zero(Int32), typemin(UInt), typemax(UInt), zero(UInt32), nothing, 0x00, - Core.DebugInfo(mi)) + Core.DebugInfo(mi), Core.svec()) __init__() = @assert ci isa Core.CodeInstance diff --git a/test/precompile_absint1.jl b/test/precompile_absint1.jl index 7bc0382ffda85..ab36af163dc50 100644 --- a/test/precompile_absint1.jl +++ b/test/precompile_absint1.jl @@ -41,29 +41,33 @@ precompile_test_harness() do load_path let m = only(methods(TestAbsIntPrecompile1.basic_callee)) mi = only(Base.specializations(m)) ci = mi.cache - @test isdefined(ci, :next) + @test_broken isdefined(ci, :next) @test ci.owner === nothing @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) + @test_skip begin ci = ci.next @test !isdefined(ci, :next) @test ci.owner === cache_owner @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) + end end let m = only(methods(sum, (Vector{Float64},))) found = false for mi in Base.specializations(m) if mi isa Core.MethodInstance && mi.specTypes == Tuple{typeof(sum),Vector{Float64}} ci = mi.cache - @test isdefined(ci, :next) - @test ci.owner === cache_owner + @test_broken isdefined(ci, :next) + @test_broken ci.owner === cache_owner + @test_skip begin @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile1) == Base.object_build_id(ci) ci = ci.next + end @test !isdefined(ci, :next) @test ci.owner === nothing @test ci.max_world == typemax(UInt) diff --git a/test/precompile_absint2.jl b/test/precompile_absint2.jl index 066dcbaece4c4..75b84e26e06c6 100644 --- a/test/precompile_absint2.jl +++ b/test/precompile_absint2.jl @@ -22,10 +22,9 @@ precompile_test_harness() do load_path inferred CustomData(@nospecialize inferred) = new(inferred) end - function CC.transform_result_for_cache(interp::PrecompileInterpreter, - mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult) - inferred_result = @invoke CC.transform_result_for_cache(interp::CC.AbstractInterpreter, - mi::Core.MethodInstance, valid_worlds::CC.WorldRange, result::CC.InferenceResult) + function CC.transform_result_for_cache(interp::PrecompileInterpreter, result::CC.InferenceResult) + inferred_result = @invoke CC.transform_result_for_cache( + interp::CC.AbstractInterpreter, result::CC.InferenceResult) return CustomData(inferred_result) end function CC.src_inlining_policy(interp::PrecompileInterpreter, @nospecialize(src), @@ -64,29 +63,33 @@ precompile_test_harness() do load_path let m = only(methods(TestAbsIntPrecompile2.basic_callee)) mi = only(Base.specializations(m)) ci = mi.cache - @test isdefined(ci, :next) + @test_broken isdefined(ci, :next) @test ci.owner === nothing @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) + @test_skip begin ci = ci.next @test !isdefined(ci, :next) @test ci.owner === cache_owner @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) + end end let m = only(methods(sum, (Vector{Float64},))) found = false for mi = Base.specializations(m) if mi isa Core.MethodInstance && mi.specTypes == Tuple{typeof(sum),Vector{Float64}} ci = mi.cache - @test isdefined(ci, :next) - @test ci.owner === cache_owner + @test_broken isdefined(ci, :next) + @test_broken ci.owner === cache_owner + @test_skip begin @test ci.max_world == typemax(UInt) @test Base.module_build_id(TestAbsIntPrecompile2) == Base.object_build_id(ci) ci = ci.next + end @test !isdefined(ci, :next) @test ci.owner === nothing @test ci.max_world == typemax(UInt) diff --git a/test/stacktraces.jl b/test/stacktraces.jl index bc86479dbab4b..12da6d571013e 100644 --- a/test/stacktraces.jl +++ b/test/stacktraces.jl @@ -103,10 +103,7 @@ end end let src = Meta.lower(Main, quote let x = 1 end end).args[1]::Core.CodeInfo - li = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()) - @atomic li.cache = ccall(:jl_new_codeinst_for_uninferred, Ref{Core.CodeInstance}, (Any, Any), li, src) - li.specTypes = Tuple{} - li.def = @__MODULE__ + li = ccall(:jl_method_instance_for_thunk, Ref{Core.MethodInstance}, (Any, Any), src, @__MODULE__) sf = StackFrame(:a, :b, 3, li, false, false, 0) repr = string(sf) @test repr == "Toplevel MethodInstance thunk at b:3" From 85dc2c72110e44f68f483c2b7b2e4dc68c73a143 Mon Sep 17 00:00:00 2001 From: CY Han Date: Fri, 1 Nov 2024 22:20:10 +0800 Subject: [PATCH 538/548] docs: remove `dirname.c` from THIRDPARTY file (#56413) - `dirname.c` was removed by https://github.com/JuliaLang/julia/commit/c2cec7ad57102e4fbb733b8fb79d617a9524f0ae --- THIRDPARTY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 30f53727c50ab..3a74afec4a283 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -6,7 +6,6 @@ for exceptions. - [crc32c.c](https://stackoverflow.com/questions/17645167/implementing-sse-4-2s-crc32c-in-software) (CRC-32c checksum code by Mark Adler) [[ZLib](https://opensource.org/licenses/Zlib)]. - [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) (for ccall/cfunction ABI definitions) [BSD-3]. The portion of code that Julia uses from LDC is [BSD-3] licensed. - [LLVM](https://releases.llvm.org/3.9.0/LICENSE.TXT) (for parts of src/disasm.cpp) [UIUC] -- [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT] - [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3] - [Python](https://docs.python.org/3/license.html) (for strtod implementation on Windows) [PSF] - [FEMTOLISP](https://github.com/JeffBezanson/femtolisp) [BSD-3] From 7d81897aef774ce953ed67a9951ae1a051d38544 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:57:23 -0400 Subject: [PATCH 539/548] =?UTF-8?q?Allow=20ext=20=E2=86=92=20ext=20depende?= =?UTF-8?q?ncy=20if=20triggers=20are=20a=20strict=20superset=20(#56368)=20?= =?UTF-8?q?(#56402)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forward port of #56368 - this was a pretty clean port, so it should be good to go once tests pass. --- base/loading.jl | 75 ++++++++++--------- base/precompilation.jl | 31 +++++--- test/loading.jl | 68 +++++++++++++++++ .../Manifest.toml | 32 ++++++++ .../Project.toml | 12 +++ .../ext/ExtAB.jl | 12 +++ .../src/CrossPackageExtToExtDependency.jl | 7 ++ .../Extensions/CyclicExtensions/Manifest.toml | 7 +- .../Extensions/EnvWithDeps/Manifest.toml | 7 +- .../EnvWithHasExtensions/Manifest.toml | 7 +- .../EnvWithHasExtensionsv2/Manifest.toml | 7 +- .../project/Extensions/ExtDep.jl/Project.toml | 1 + .../Extensions/ExtDep.jl/src/ExtDep.jl | 1 + .../ExtToExtDependency/Manifest.toml | 21 ++++++ .../ExtToExtDependency/Project.toml | 14 ++++ .../Extensions/ExtToExtDependency/ext/ExtA.jl | 6 ++ .../ExtToExtDependency/ext/ExtAB.jl | 12 +++ .../src/ExtToExtDependency.jl | 7 ++ .../HasDepWithExtensions.jl/Manifest.toml | 7 +- .../Extensions/SomeOtherPackage/Project.toml | 4 + .../SomeOtherPackage/src/SomeOtherPackage.jl | 5 ++ 21 files changed, 293 insertions(+), 50 deletions(-) create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/Project.toml create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl create mode 100644 test/project/Extensions/ExtToExtDependency/Manifest.toml create mode 100644 test/project/Extensions/ExtToExtDependency/Project.toml create mode 100644 test/project/Extensions/ExtToExtDependency/ext/ExtA.jl create mode 100644 test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl create mode 100644 test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl create mode 100644 test/project/Extensions/SomeOtherPackage/Project.toml create mode 100644 test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl diff --git a/base/loading.jl b/base/loading.jl index 28875b8713b35..7b45348f47009 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -756,8 +756,9 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi proj = implicit_manifest_uuid_path(env, pkg) proj === nothing || return proj # if not found - parentid = get(EXT_PRIMED, pkg, nothing) - if parentid !== nothing + triggers = get(EXT_PRIMED, pkg, nothing) + if triggers !== nothing + parentid = triggers[1] _, parent_project_file = entry_point_and_project_file(env, parentid.name) if parent_project_file !== nothing parentproj = project_file_name_uuid(parent_project_file, parentid.name) @@ -1432,9 +1433,7 @@ function run_module_init(mod::Module, i::Int=1) end function run_package_callbacks(modkey::PkgId) - if !precompiling_extension - run_extension_callbacks(modkey) - end + run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) try @@ -1463,7 +1462,7 @@ mutable struct ExtensionId ntriggers::Int # how many more packages must be defined until this is loaded end -const EXT_PRIMED = Dict{PkgId, PkgId}() # Extension -> Parent +const EXT_PRIMED = Dict{PkgId,Vector{PkgId}}() # Extension -> Parent + Triggers (parent is always first) const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() # Trigger -> Extensions that can be triggered by it const EXT_DORMITORY_FAILED = ExtensionId[] @@ -1554,7 +1553,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} if haskey(EXT_PRIMED, id) || haskey(Base.loaded_modules, id) continue # extension is already primed or loaded, don't add it again end - EXT_PRIMED[id] = parent + EXT_PRIMED[id] = trigger_ids = PkgId[parent] gid = ExtensionId(id, parent, 1 + length(triggers), 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) @@ -1562,6 +1561,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} # TODO: Better error message if this lookup fails? uuid_trigger = UUID(totaldeps[trigger]::String) trigger_id = PkgId(uuid_trigger, trigger) + push!(trigger_ids, trigger_id) if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id) push!(trigger1, gid) @@ -1573,6 +1573,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} end loading_extension::Bool = false +loadable_extensions::Union{Nothing,Vector{PkgId}} = nothing precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) assert_havelock(require_lock) @@ -1603,7 +1604,7 @@ function run_extension_callbacks(pkgid::PkgId) for extid in extids @assert extid.ntriggers > 0 extid.ntriggers -= 1 - if extid.ntriggers == 0 + if extid.ntriggers == 0 && (loadable_extensions === nothing || extid.id in loadable_extensions) push!(extids_to_load, extid) end end @@ -2645,7 +2646,17 @@ function __require_prelocked(pkg::PkgId, env) # double-check the search now that we have lock m = _require_search_from_serialized(pkg, path, UInt128(0), true) m isa Module && return m - return compilecache(pkg, path; reasons) + triggers = get(EXT_PRIMED, pkg, nothing) + loadable_exts = nothing + if triggers !== nothing # extension + loadable_exts = PkgId[] + for (ext′, triggers′) in EXT_PRIMED + if triggers′ ⊊ triggers + push!(loadable_exts, ext′) + end + end + end + return compilecache(pkg, path; reasons, loadable_exts) end loaded isa Module && return loaded if isnothing(loaded) # maybe_cachefile_lock returns nothing if it had to wait for another process @@ -2996,10 +3007,16 @@ function check_package_module_loaded(pkg::PkgId) return nothing end +# protects against PkgId and UUID being imported and losing Base prefix +_pkg_str(_pkg::PkgId) = (_pkg.uuid === nothing) ? "Base.PkgId($(repr(_pkg.name)))" : "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))" +_pkg_str(_pkg::Vector) = sprint(show, eltype(_pkg); context = :module=>nothing) * "[" * join(map(_pkg_str, _pkg), ",") * "]" +_pkg_str(_pkg::Pair{PkgId}) = _pkg_str(_pkg.first) * " => " * repr(_pkg.second) +_pkg_str(_pkg::Nothing) = "nothing" + const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), - internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) + internal_stderr::IO = stderr, internal_stdout::IO = stdout, loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) @@ -3007,8 +3024,9 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: dl_load_path = String[abspath(x) for x in DL_LOAD_PATH] load_path = String[abspath(x) for x in Base.load_path()] # if pkg is a stdlib, append its parent Project.toml to the load path - parentid = get(EXT_PRIMED, pkg, nothing) - if parentid !== nothing + triggers = get(EXT_PRIMED, pkg, nothing) + if triggers !== nothing + parentid = triggers[1] for env in load_path project_file = env_project_file(env) if project_file === true @@ -3026,22 +3044,6 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: any(path -> path_sep in path, load_path) && error("LOAD_PATH entries cannot contain $(repr(path_sep))") - deps_strs = String[] - # protects against PkgId and UUID being imported and losing Base prefix - function pkg_str(_pkg::PkgId) - if _pkg.uuid === nothing - "Base.PkgId($(repr(_pkg.name)))" - else - "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))" - end - end - for (pkg, build_id) in concrete_deps - push!(deps_strs, "$(pkg_str(pkg)) => $(repr(build_id))") - end - deps_eltype = sprint(show, eltype(concrete_deps); context = :module=>nothing) - deps = deps_eltype * "[" * join(deps_strs, ",") * "]" - precomp_stack = "Base.PkgId[$(join(map(pkg_str, vcat(Base.precompilation_stack, pkg)), ", "))]" - if output_o === nothing # remove options that make no difference given the other cache options cacheflags = CacheFlags(cacheflags, opt_level=0) @@ -3072,10 +3074,11 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: # write data over stdin to avoid the (unlikely) case of exceeding max command line size write(io.in, """ empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated - Base.track_nested_precomp($precomp_stack) - Base.precompiling_extension = $(loading_extension | isext) - Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), - $(repr(load_path)), $deps, $(repr(source_path(nothing)))) + Base.track_nested_precomp($(_pkg_str(vcat(Base.precompilation_stack, pkg)))) + Base.loadable_extensions = $(_pkg_str(loadable_exts)) + Base.precompiling_extension = $(loading_extension) + Base.include_package_for_output($(_pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), + $(repr(load_path)), $(_pkg_str(concrete_deps)), $(repr(source_path(nothing)))) """) close(io.in) return io @@ -3130,18 +3133,18 @@ This can be used to reduce package load times. Cache files are stored in `DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) for important notes. """ -function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) +function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout path = locate_package(pkg) path === nothing && throw(ArgumentError("$(repr("text/plain", pkg)) not found during precompilation")) - return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, isext) + return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, loadable_exts) end const MAX_NUM_PRECOMPILE_FILES = Ref(10) function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout, keep_loaded_modules::Bool = true; flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), - reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) + reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout # decide where to put the resulting cache file @@ -3181,7 +3184,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in close(tmpio_o) close(tmpio_so) end - p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, isext) + p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, loadable_exts) if success(p) if cache_objects diff --git a/base/precompilation.jl b/base/precompilation.jl index 54f13d298a462..edd8824ff8d68 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -436,6 +436,7 @@ function _precompilepkgs(pkgs::Vector{String}, return name end + triggers = Dict{Base.PkgId,Vector{Base.PkgId}}() for (dep, deps) in env.deps pkg = Base.PkgId(dep, env.names[dep]) Base.in_sysimage(pkg) && continue @@ -444,25 +445,22 @@ function _precompilepkgs(pkgs::Vector{String}, # add any extensions pkg_exts = Dict{Base.PkgId, Vector{Base.PkgId}}() for (ext_name, extdep_uuids) in env.extensions[dep] - ext_deps = Base.PkgId[] - push!(ext_deps, pkg) # depends on parent package + ext_uuid = Base.uuid5(pkg.uuid, ext_name) + ext = Base.PkgId(ext_uuid, ext_name) + triggers[ext] = Base.PkgId[pkg] # depends on parent package all_extdeps_available = true for extdep_uuid in extdep_uuids extdep_name = env.names[extdep_uuid] if extdep_uuid in keys(env.deps) - push!(ext_deps, Base.PkgId(extdep_uuid, extdep_name)) + push!(triggers[ext], Base.PkgId(extdep_uuid, extdep_name)) else all_extdeps_available = false break end end all_extdeps_available || continue - ext_uuid = Base.uuid5(pkg.uuid, ext_name) - ext = Base.PkgId(ext_uuid, ext_name) - filter!(!Base.in_sysimage, ext_deps) - depsmap[ext] = ext_deps exts[ext] = pkg.name - pkg_exts[ext] = ext_deps + pkg_exts[ext] = depsmap[ext] = filter(!Base.in_sysimage, triggers[ext]) end if !isempty(pkg_exts) pkg_exts_map[pkg] = collect(keys(pkg_exts)) @@ -478,6 +476,16 @@ function _precompilepkgs(pkgs::Vector{String}, append!(direct_deps, keys(filter(d->last(d) in keys(env.project_deps), exts))) @debug "precompile: deps collected" + + # An extension effectively depends on another extension if it has a strict superset of its triggers + for ext_a in keys(exts) + for ext_b in keys(exts) + if triggers[ext_a] ⊋ triggers[ext_b] + push!(depsmap[ext_a], ext_b) + end + end + end + # this loop must be run after the full depsmap has been populated for (pkg, pkg_exts) in pkg_exts_map # find any packages that depend on the extension(s)'s deps and replace those deps in their deps list with the extension(s), @@ -839,7 +847,12 @@ function _precompilepkgs(pkgs::Vector{String}, t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do Base.with_logger(Base.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session - Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags, isext = haskey(exts, pkg)) + keep_loaded_modules = false + # for extensions, any extension in our direct dependencies is one we have a right to load + # for packages, we may load any extension (all possible triggers are accounted for above) + loadable_exts = haskey(exts, pkg) ? filter((dep)->haskey(exts, dep), depsmap[pkg]) : nothing + Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, keep_loaded_modules; + flags, cacheflags, loadable_exts) end end if ret isa Base.PrecompilableError diff --git a/test/loading.jl b/test/loading.jl index ecba64ca45a73..1cc20548d9bc8 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1152,6 +1152,74 @@ end cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) @test occursin("Hello Cycles!", String(read(cmd))) + # Extension-to-extension dependencies + + mktempdir() do depot # Parallel pre-compilation + code = """ + Base.disable_parallel_precompile = false + using ExtToExtDependency + Base.get_extension(ExtToExtDependency, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(ExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + ExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "ExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello ext-to-ext!", String(read(cmd))) + end + mktempdir() do depot # Serial pre-compilation + code = """ + Base.disable_parallel_precompile = true + using ExtToExtDependency + Base.get_extension(ExtToExtDependency, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(ExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + ExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "ExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello ext-to-ext!", String(read(cmd))) + end + + mktempdir() do depot # Parallel pre-compilation + code = """ + Base.disable_parallel_precompile = false + using CrossPackageExtToExtDependency + Base.get_extension(CrossPackageExtToExtDependency.CyclicExtensions, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(CrossPackageExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + CrossPackageExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "CrossPackageExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello x-package ext-to-ext!", String(read(cmd))) + end + mktempdir() do depot # Serial pre-compilation + code = """ + Base.disable_parallel_precompile = true + using CrossPackageExtToExtDependency + Base.get_extension(CrossPackageExtToExtDependency.CyclicExtensions, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(CrossPackageExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + CrossPackageExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "CrossPackageExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello x-package ext-to-ext!", String(read(cmd))) + end + finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml b/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml new file mode 100644 index 0000000000000..5497fdb7091bb --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml @@ -0,0 +1,32 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.11.1" +manifest_format = "2.0" +project_hash = "dc35c2cf8c6b82fb5b9624c9713c2df34ca30499" + +[[deps.CyclicExtensions]] +deps = ["ExtDep"] +path = "../CyclicExtensions" +uuid = "17d4f0df-b55c-4714-ac4b-55fa23f7355c" +version = "0.1.0" +weakdeps = ["SomePackage"] + + [deps.CyclicExtensions.extensions] + ExtA = ["SomePackage"] + ExtB = ["SomePackage"] + +[[deps.ExtDep]] +deps = ["SomeOtherPackage", "SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml b/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml new file mode 100644 index 0000000000000..76ffb7bd1c882 --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml @@ -0,0 +1,12 @@ +name = "CrossPackageExtToExtDependency" +uuid = "30f07f2e-c47e-40db-93a2-cbc4d1b301cc" +version = "0.1.0" + +[deps] +CyclicExtensions = "17d4f0df-b55c-4714-ac4b-55fa23f7355c" + +[weakdeps] +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" + +[extensions] +ExtAB = ["CyclicExtensions", "SomePackage"] diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl b/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl new file mode 100644 index 0000000000000..1ded9f2df5097 --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl @@ -0,0 +1,12 @@ +module ExtAB + +using CrossPackageExtToExtDependency +using SomePackage +using CyclicExtensions + +const ExtA = Base.get_extension(CyclicExtensions, :ExtA) +if !(ExtA isa Module) + error("expected extension to load") +end + +end diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl b/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl new file mode 100644 index 0000000000000..28b229e2d61bf --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl @@ -0,0 +1,7 @@ +module CrossPackageExtToExtDependency + +using CyclicExtensions + +greet() = print("Hello x-package ext-to-ext!") + +end # module CrossPackageExtToTextDependency diff --git a/test/project/Extensions/CyclicExtensions/Manifest.toml b/test/project/Extensions/CyclicExtensions/Manifest.toml index a506825cf7995..0f280293c07b6 100644 --- a/test/project/Extensions/CyclicExtensions/Manifest.toml +++ b/test/project/Extensions/CyclicExtensions/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -15,6 +15,11 @@ path = "../ExtDep2" uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" version = "0.1.0" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithDeps/Manifest.toml b/test/project/Extensions/EnvWithDeps/Manifest.toml index 85ff259f0a4d5..554a317b370eb 100644 --- a/test/project/Extensions/EnvWithDeps/Manifest.toml +++ b/test/project/Extensions/EnvWithDeps/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -15,6 +15,11 @@ path = "../ExtDep2" uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" version = "0.1.0" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithHasExtensions/Manifest.toml b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml index 004ef7892c173..ca2be57c61596 100644 --- a/test/project/Extensions/EnvWithHasExtensions/Manifest.toml +++ b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "a4c480cfa7da9610333d5c42623bf746bd286c5f" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -25,6 +25,11 @@ version = "0.1.0" ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml index 66781a5701363..9f8c717041b6e 100644 --- a/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml +++ b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "caa716752e6dff3d77c3de929ebbb5d2024d04ef" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -19,6 +19,11 @@ weakdeps = ["ExtDep"] [deps.HasExtensions.extensions] Extension2 = "ExtDep" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/ExtDep.jl/Project.toml b/test/project/Extensions/ExtDep.jl/Project.toml index d246934b7f958..1ece7bf11f95a 100644 --- a/test/project/Extensions/ExtDep.jl/Project.toml +++ b/test/project/Extensions/ExtDep.jl/Project.toml @@ -4,3 +4,4 @@ version = "0.1.0" [deps] SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" +SomeOtherPackage = "178f68a2-4498-45ee-a775-452b36359b63" diff --git a/test/project/Extensions/ExtDep.jl/src/ExtDep.jl b/test/project/Extensions/ExtDep.jl/src/ExtDep.jl index 1c0022d879f51..2d3c6b7f28827 100644 --- a/test/project/Extensions/ExtDep.jl/src/ExtDep.jl +++ b/test/project/Extensions/ExtDep.jl/src/ExtDep.jl @@ -2,6 +2,7 @@ module ExtDep # loading this package makes the check for loading extensions trigger # which tests #47921 +using SomeOtherPackage using SomePackage struct ExtDepStruct end diff --git a/test/project/Extensions/ExtToExtDependency/Manifest.toml b/test/project/Extensions/ExtToExtDependency/Manifest.toml new file mode 100644 index 0000000000000..41546213cdd41 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/Manifest.toml @@ -0,0 +1,21 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.11.1" +manifest_format = "2.0" +project_hash = "90b427e837c654fabb1434527ea698dabad46d29" + +[[deps.ExtDep]] +deps = ["SomeOtherPackage", "SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/ExtToExtDependency/Project.toml b/test/project/Extensions/ExtToExtDependency/Project.toml new file mode 100644 index 0000000000000..980db74c04dc4 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/Project.toml @@ -0,0 +1,14 @@ +name = "ExtToExtDependency" +uuid = "594ddb71-72fb-4cfe-9471-775d48a5b70b" +version = "0.1.0" + +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" + +[weakdeps] +SomeOtherPackage = "178f68a2-4498-45ee-a775-452b36359b63" +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" + +[extensions] +ExtA = ["SomePackage"] +ExtAB = ["SomePackage", "SomeOtherPackage"] diff --git a/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl b/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl new file mode 100644 index 0000000000000..71ed09795157c --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl @@ -0,0 +1,6 @@ +module ExtA + +using ExtToExtDependency +using SomePackage + +end diff --git a/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl b/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl new file mode 100644 index 0000000000000..a5b2c43cafd58 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl @@ -0,0 +1,12 @@ +module ExtAB + +using ExtToExtDependency +using SomePackage +using SomeOtherPackage + +const ExtA = Base.get_extension(ExtToExtDependency, :ExtA) +if !(ExtA isa Module) + error("expected extension to load") +end + +end diff --git a/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl b/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl new file mode 100644 index 0000000000000..ec2bf58f18641 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl @@ -0,0 +1,7 @@ +module ExtToExtDependency + +using ExtDep + +greet() = print("Hello ext-to-ext!") + +end # module ExtToExtDependency diff --git a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml index f659a59e0910b..98510dcb27733 100644 --- a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml +++ b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "4e196b07f2ee7adc48ac9d528d42b3cf3737c7a0" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -32,6 +32,11 @@ weakdeps = ["ExtDep", "ExtDep2"] ExtensionDep = "ExtDep3" ExtensionFolder = ["ExtDep", "ExtDep2"] +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/SomeOtherPackage/Project.toml b/test/project/Extensions/SomeOtherPackage/Project.toml new file mode 100644 index 0000000000000..6e7eee40c7be2 --- /dev/null +++ b/test/project/Extensions/SomeOtherPackage/Project.toml @@ -0,0 +1,4 @@ +name = "SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +authors = ["Cody Tapscott "] +version = "0.1.0" diff --git a/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl b/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl new file mode 100644 index 0000000000000..ba23eb3914561 --- /dev/null +++ b/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl @@ -0,0 +1,5 @@ +module SomeOtherPackage + +greet() = print("Hello World!") + +end # module SomeOtherPackage From 3de1b1dc04dd6a0f4e2a0d32db89beb6b009164a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:47:42 +0000 Subject: [PATCH 540/548] [docs] Fix rendering of warning admonition in llvm passes page (#56412) Follow up to #56392: also the warning in https://docs.julialang.org/en/v1.11.1/devdocs/llvm-passes/#Multiversioning is rendered incorrectly because of a missing space. --- doc/src/devdocs/llvm-passes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index da600f73fd696..7b847abaa2149 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -98,7 +98,7 @@ This pass performs modifications to a module to create functions that are optimi !!! warning - Use of `llvmcall` with multiversioning is dangerous. `llvmcall` enables access to features not typically exposed by the Julia APIs, and are therefore usually not available on all architectures. If multiversioning is enabled and code generation is requested for a target architecture that does not support the feature required by an `llvmcall` expression, LLVM will probably error out, likely with an abort and the message `LLVM ERROR: Do not know how to split the result of this operator!`. + Use of `llvmcall` with multiversioning is dangerous. `llvmcall` enables access to features not typically exposed by the Julia APIs, and are therefore usually not available on all architectures. If multiversioning is enabled and code generation is requested for a target architecture that does not support the feature required by an `llvmcall` expression, LLVM will probably error out, likely with an abort and the message `LLVM ERROR: Do not know how to split the result of this operator!`. ### GCInvariantVerifier From 4393f8ccf911617df158227a7fec06a9f8b000b8 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 1 Nov 2024 21:23:52 +0100 Subject: [PATCH 541/548] Fix dispatch for `rdiv!` with `LU` (#55764) --- stdlib/LinearAlgebra/src/lu.jl | 2 +- stdlib/LinearAlgebra/src/triangular.jl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl index d2e82af5d6409..0837ac08e74ea 100644 --- a/stdlib/LinearAlgebra/src/lu.jl +++ b/stdlib/LinearAlgebra/src/lu.jl @@ -786,7 +786,7 @@ function ldiv!(adjA::AdjointFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::Ab return B end -rdiv!(B::AbstractMatrix, A::LU) = transpose(ldiv!(transpose(A), transpose(B))) +rdiv!(B::AbstractMatrix, A::LU{T,Tridiagonal{T,V}}) where {T,V} = transpose(ldiv!(transpose(A), transpose(B))) # Conversions AbstractMatrix(F::LU) = (F.L * F.U)[invperm(F.p),:] diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index a032041a4116c..31447f1aff5ae 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -1048,16 +1048,16 @@ _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = function lmul!(A::AbstractTriangular, B::AbstractVecOrMat) if istriu(A) - _trimul!(B, UpperTriangular(A), B) + _trimul!(B, uppertriangular(A), B) else - _trimul!(B, LowerTriangular(A), B) + _trimul!(B, lowertriangular(A), B) end end function rmul!(A::AbstractMatrix, B::AbstractTriangular) if istriu(B) - _trimul!(A, A, UpperTriangular(B)) + _trimul!(A, A, uppertriangular(B)) else - _trimul!(A, A, LowerTriangular(B)) + _trimul!(A, A, lowertriangular(B)) end end @@ -1097,16 +1097,16 @@ _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) if istriu(A) - _ldiv!(B, UpperTriangular(A), B) + _ldiv!(B, uppertriangular(A), B) else - _ldiv!(B, LowerTriangular(A), B) + _ldiv!(B, lowertriangular(A), B) end end function rdiv!(A::AbstractMatrix, B::AbstractTriangular) if istriu(B) - _rdiv!(A, A, UpperTriangular(B)) + _rdiv!(A, A, uppertriangular(B)) else - _rdiv!(A, A, LowerTriangular(B)) + _rdiv!(A, A, lowertriangular(B)) end end From 770b1448be9ff532a56b0d3a58589b38fffa1b7b Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Sat, 2 Nov 2024 00:57:14 +0100 Subject: [PATCH 542/548] Remove overwritten method of OffsetArray (#56414) This is overwritten three definitions later in `Base.reshape(A::OffsetArray, inds::Colon)`. Should remove warnings I saw when testing a package that uses it. --- test/testhelpers/OffsetArrays.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/testhelpers/OffsetArrays.jl b/test/testhelpers/OffsetArrays.jl index f8da243da6b63..3463d5a94393d 100644 --- a/test/testhelpers/OffsetArrays.jl +++ b/test/testhelpers/OffsetArrays.jl @@ -560,7 +560,6 @@ Base.reshape(A::OffsetArray, inds::Tuple{Union{Integer,Base.OneTo},Vararg{Union{ Base.reshape(A::OffsetArray, inds::Dims) = _reshape_nov(A, inds) Base.reshape(A::OffsetVector, ::Colon) = A Base.reshape(A::OffsetVector, ::Tuple{Colon}) = A -Base.reshape(A::OffsetArray, ::Colon) = reshape(A, (Colon(),)) Base.reshape(A::OffsetArray, inds::Union{Int,Colon}...) = reshape(A, inds) Base.reshape(A::OffsetArray, inds::Tuple{Vararg{Union{Int,Colon}}}) = _reshape_nov(A, inds) # The following two additional methods for Colon are added to resolve method ambiguities to From c3c3cd1507208e2f9ea1bf6df2835bded1753497 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 1 Nov 2024 20:36:41 -0400 Subject: [PATCH 543/548] Add a missing GC root in constant declaration (#56408) As pointed out in https://github.com/JuliaLang/julia/pull/56224#discussion_r1816974147. --- src/toplevel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/toplevel.c b/src/toplevel.c index 017d61bbc8ceb..45143f99a178c 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -742,13 +742,16 @@ static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, con JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) { + JL_GC_PUSH1(&val); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); int did_warn = 0; while (1) { if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { - if (!val) + if (!val) { + JL_GC_POP(); return bpart; + } jl_value_t *old = decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(old); if (jl_egal(val, old)) @@ -778,6 +781,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, j break; } } + JL_GC_POP(); return bpart; } From 10a1d6f3f54f8eef0b65622d1a0cb867a98c1785 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 1 Nov 2024 20:37:18 -0400 Subject: [PATCH 544/548] Teach compiler about partitioned bindings (#56299) This commit teaches to compiler to update its world bounds whenever it looks at a binding partition, making the compiler sound in the presence of a partitioned binding. The key adjustment is that the compiler is no longer allowed to directly query the binding table without recording the world bounds, so all the various abstract evaluations that look at bindings need to be adjusted and are no longer pure tfuncs. We used to look at bindings a lot more, but thanks to earlier prep work to remove unnecessary binding-dependent code (#55288, #55289 and #55271), these changes become relatively straightforward. Note that as before, we do not create any binding partitions by default, so this commit is mostly preperatory. --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/abstractinterpretation.jl | 365 +++++++++++++++++++++--- base/compiler/cicache.jl | 2 + base/compiler/optimize.jl | 10 +- base/compiler/ssair/inlining.jl | 5 - base/compiler/ssair/ir.jl | 9 +- base/compiler/ssair/legacy.jl | 2 +- base/compiler/ssair/passes.jl | 6 +- base/compiler/ssair/slot2ssa.jl | 2 +- base/compiler/tfuncs.jl | 180 ++---------- base/runtime_internals.jl | 9 +- src/julia_internal.h | 1 - src/module.c | 12 - src/rtutils.c | 1 + stdlib/REPL/src/REPLCompletions.jl | 6 +- test/compiler/inference.jl | 3 - test/rebinding.jl | 12 + 16 files changed, 404 insertions(+), 221 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index e20b74454bb22..f7f7e80a0ebe1 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2290,6 +2290,191 @@ function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vecto return Future(CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo())) end +const generic_getglobal_effects = Effects(EFFECTS_THROWS, consistent=ALWAYS_FALSE, inaccessiblememonly=ALWAYS_FALSE) +function abstract_eval_getglobal(interp::AbstractInterpreter, sv::AbsIntState, @nospecialize(M), @nospecialize(s)) + ⊑ = partialorder(typeinf_lattice(interp)) + if M isa Const && s isa Const + M, s = M.val, s.val + if M isa Module && s isa Symbol + return CallMeta(abstract_eval_globalref(interp, GlobalRef(M, s), sv), NoCallInfo()) + end + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + elseif !hasintersect(widenconst(M), Module) || !hasintersect(widenconst(s), Symbol) + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + elseif M ⊑ Module && s ⊑ Symbol + return CallMeta(Any, UndefVarError, generic_getglobal_effects, NoCallInfo()) + end + return CallMeta(Any, Union{UndefVarError, TypeError}, generic_getglobal_effects, NoCallInfo()) +end + +function merge_exct(cm::CallMeta, @nospecialize(exct)) + if exct !== Bottom + cm = CallMeta(cm.rt, Union{cm.exct, exct}, Effects(cm.effects; nothrow=false), cm.info) + end + return cm +end + +function abstract_eval_getglobal(interp::AbstractInterpreter, sv::AbsIntState, @nospecialize(M), @nospecialize(s), @nospecialize(order)) + goe = global_order_exct(order, #=loading=#true, #=storing=#false) + cm = abstract_eval_getglobal(interp, sv, M, s) + return merge_exct(cm, goe) +end + +function abstract_eval_getglobal(interp::AbstractInterpreter, sv::AbsIntState, argtypes::Vector{Any}) + if length(argtypes) == 3 + return abstract_eval_getglobal(interp, sv, argtypes[2], argtypes[3]) + elseif length(argtypes) == 4 + return abstract_eval_getglobal(interp, sv, argtypes[2], argtypes[3], argtypes[4]) + elseif !isvarargtype(argtypes[end]) || length(argtypes) > 5 + return CallMeta(Union{}, ArgumentError, EFFECTS_THROWS, NoCallInfo()) + else + return CallMeta(Any, Union{ArgumentError, UndefVarError, TypeError, ConcurrencyViolationError}, + generic_getglobal_effects, NoCallInfo()) + end +end + +@nospecs function abstract_eval_get_binding_type(interp::AbstractInterpreter, sv::AbsIntState, M, s) + ⊑ = partialorder(typeinf_lattice(interp)) + if isa(M, Const) && isa(s, Const) + (M, s) = (M.val, s.val) + if !isa(M, Module) || !isa(s, Symbol) + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + end + partition = abstract_eval_binding_partition!(interp, GlobalRef(M, s), sv) + + if is_some_guard(binding_kind(partition)) + # We do not currently assume an invalidation for guard -> defined transitions + # rt = Const(nothing) + rt = Type + elseif is_some_const_binding(binding_kind(partition)) + rt = Const(Any) + else + rt = Const(partition_restriction(partition)) + end + return CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo()) + elseif !hasintersect(widenconst(M), Module) || !hasintersect(widenconst(s), Symbol) + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + elseif M ⊑ Module && s ⊑ Symbol + return CallMeta(Type, Union{}, EFFECTS_TOTAL, NoCallInfo()) + end + return CallMeta(Type, TypeError, EFFECTS_THROWS, NoCallInfo()) +end + +function abstract_eval_get_binding_type(interp::AbstractInterpreter, sv::AbsIntState, argtypes::Vector{Any}) + if length(argtypes) == 3 + return abstract_eval_get_binding_type(interp, sv, argtypes[2], argtypes[3]) + elseif !isvarargtype(argtypes[end]) || length(argtypes) > 4 + return CallMeta(Union{}, ArgumentError, EFFECTS_THROWS, NoCallInfo()) + end + return CallMeta(Type, Union{TypeError, ArgumentError}, EFFECTS_THROWS, NoCallInfo()) +end + +const setglobal!_effects = Effects(EFFECTS_TOTAL; effect_free=ALWAYS_FALSE, nothrow=false, inaccessiblememonly=ALWAYS_FALSE) + +function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState, @nospecialize(M), @nospecialize(s), @nospecialize(v)) + if isa(M, Const) && isa(s, Const) + M, s = M.val, s.val + if M isa Module && s isa Symbol + exct = global_assignment_exct(interp, sv, GlobalRef(M, s), v) + return CallMeta(v, exct, Effects(setglobal!_effects, nothrow=exct===Bottom), NoCallInfo()) + end + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + end + ⊑ = partialorder(typeinf_lattice(interp)) + if !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol)) + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + elseif M ⊑ Module && s ⊑ Symbol + return CallMeta(v, ErrorException, setglobal!_effects, NoCallInfo()) + end + return CallMeta(v, Union{TypeError, ErrorException}, setglobal!_effects, NoCallInfo()) +end + +function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState, @nospecialize(M), @nospecialize(s), @nospecialize(v), @nospecialize(order)) + goe = global_order_exct(order, #=loading=#false, #=storing=#true) + cm = abstract_eval_setglobal!(interp, sv, M, s, v) + return merge_exct(cm, goe) +end + +function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState, argtypes::Vector{Any}) + if length(argtypes) == 4 + return abstract_eval_setglobal!(interp, sv, argtypes[2], argtypes[3], argtypes[4]) + elseif length(argtypes) == 5 + return abstract_eval_setglobal!(interp, sv, argtypes[2], argtypes[3], argtypes[4], argtypes[5]) + elseif !isvarargtype(argtypes[end]) || length(argtypes) > 6 + return CallMeta(Union{}, ArgumentError, EFFECTS_THROWS, NoCallInfo()) + else + return CallMeta(Any, Union{ArgumentError, TypeError, ErrorException, ConcurrencyViolationError}, setglobal!_effects, NoCallInfo()) + end +end + +function abstract_eval_setglobalonce!(interp::AbstractInterpreter, sv::AbsIntState, argtypes::Vector{Any}) + if length(argtypes) in (4, 5, 6) + cm = abstract_eval_setglobal!(interp, sv, argtypes[2], argtypes[3], argtypes[4]) + if length(argtypes) >= 5 + goe = global_order_exct(argtypes[5], #=loading=#true, #=storing=#true) + cm = merge_exct(cm, goe) + end + if length(argtypes) == 6 + goe = global_order_exct(argtypes[6], #=loading=#true, #=storing=#false) + cm = merge_exct(cm, goe) + end + return CallMeta(Bool, cm.exct, cm.effects, cm.info) + elseif !isvarargtype(argtypes[end]) || length(argtypes) > 6 + return CallMeta(Union{}, ArgumentError, EFFECTS_THROWS, NoCallInfo()) + else + return CallMeta(Bool, Union{ArgumentError, TypeError, ErrorException, ConcurrencyViolationError}, setglobal!_effects, NoCallInfo()) + end +end + +function abstract_eval_replaceglobal!(interp::AbstractInterpreter, sv::AbsIntState, argtypes::Vector{Any}) + if length(argtypes) in (5, 6, 7) + (M, s, x, v) = argtypes[2], argtypes[3], argtypes[4], argtypes[5] + + T = nothing + if isa(M, Const) && isa(s, Const) + M, s = M.val, s.val + if !(M isa Module && s isa Symbol) + return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo()) + end + partition = abstract_eval_binding_partition!(interp, GlobalRef(M, s), sv) + rte = abstract_eval_partition_load(interp, partition) + if binding_kind(partition) == BINDING_KIND_GLOBAL + T = partition_restriction(partition) + end + exct = Union{rte.exct, global_assignment_binding_exct(partition, v)} + effects = merge_effects(rte.effects, Effects(setglobal!_effects, nothrow=exct===Bottom)) + sg = CallMeta(Any, exct, effects, NoCallInfo()) + else + sg = abstract_eval_setglobal!(interp, sv, M, s, v) + end + if length(argtypes) >= 6 + goe = global_order_exct(argtypes[6], #=loading=#true, #=storing=#true) + sg = merge_exct(sg, goe) + end + if length(argtypes) == 7 + goe = global_order_exct(argtypes[7], #=loading=#true, #=storing=#false) + sg = merge_exct(sg, goe) + end + rt = T === nothing ? + ccall(:jl_apply_cmpswap_type, Any, (Any,), S) where S : + ccall(:jl_apply_cmpswap_type, Any, (Any,), T) + return CallMeta(rt, sg.exct, sg.effects, sg.info) + elseif !isvarargtype(argtypes[end]) || length(argtypes) > 8 + return CallMeta(Union{}, ArgumentError, EFFECTS_THROWS, NoCallInfo()) + else + return CallMeta(Any, Union{ArgumentError, TypeError, ErrorException, ConcurrencyViolationError}, setglobal!_effects, NoCallInfo()) + end +end + +function args_are_actually_getglobal(argtypes) + length(argtypes) in (3, 4) || return false + M = argtypes[2] + s = argtypes[3] + isa(M, Const) || return false + isa(s, Const) || return false + return isa(M.val, Module) && isa(s.val, Symbol) +end + # call where the function is known exactly function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, @@ -2313,6 +2498,33 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_throw(interp, argtypes, sv) elseif f === Core.throw_methoderror return abstract_throw_methoderror(interp, argtypes, sv) + elseif f === Core.getglobal + return Future(abstract_eval_getglobal(interp, sv, argtypes)) + elseif f === Core.setglobal! + return Future(abstract_eval_setglobal!(interp, sv, argtypes)) + elseif f === Core.setglobalonce! + return Future(abstract_eval_setglobalonce!(interp, sv, argtypes)) + elseif f === Core.replaceglobal! + return Future(abstract_eval_replaceglobal!(interp, sv, argtypes)) + elseif f === Core.getfield && args_are_actually_getglobal(argtypes) + return Future(abstract_eval_getglobal(interp, sv, argtypes)) + elseif f === Core.isdefined && args_are_actually_getglobal(argtypes) + exct = Bottom + if length(argtypes) == 4 + order = argtypes[4] + exct = global_order_exct(order, true, false) + if !(isa(order, Const) && get_atomic_order(order.val, true, false).x >= MEMORY_ORDER_UNORDERED.x) + exct = Union{exct, ConcurrencyViolationError} + end + end + return Future(merge_exct(CallMeta(abstract_eval_isdefined( + interp, + GlobalRef((argtypes[2]::Const).val, + (argtypes[3]::Const).val), + sv), + NoCallInfo()), exct)) + elseif f === Core.get_binding_type + return Future(abstract_eval_get_binding_type(interp, sv, argtypes)) end rt = abstract_call_builtin(interp, f, arginfo, sv) ft = popfirst!(argtypes) @@ -2652,6 +2864,9 @@ struct RTEffects end end +CallMeta(rte::RTEffects, info::CallInfo) = + CallMeta(rte.rt, rte.exct, rte.effects, info, rte.refinements) + function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::InferenceState) unused = call_result_unused(sv, sv.currpc) if unused @@ -2832,13 +3047,9 @@ function abstract_eval_copyast(interp::AbstractInterpreter, e::Expr, vtypes::Uni return RTEffects(rt, Any, effects) end -function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, +function abstract_eval_isdefined_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) sym = e.args[1] - rt = Bool - effects = EFFECTS_TOTAL - exct = Union{} - isa(sym, Symbol) && (sym = GlobalRef(frame_module(sv), sym)) if isa(sym, SlotNumber) && vtypes !== nothing vtyp = vtypes[slot_id(sym)] if vtyp.typ === Bottom @@ -2848,11 +3059,22 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::U else # form `Conditional` to refine `vtyp.undef` in the then branch rt = Conditional(sym, vtyp.typ, vtyp.typ; isdefined=true) end - elseif isa(sym, GlobalRef) - if InferenceParams(interp).assume_bindings_static - rt = Const(isdefined_globalref(sym)) - elseif isdefinedconst_globalref(sym) + return RTEffects(rt, Union{}, EFFECTS_TOTAL) + end + return abstract_eval_isdefined(interp, sym, sv) +end + +function abstract_eval_isdefined(interp::AbstractInterpreter, @nospecialize(sym), sv::AbsIntState) + rt = Bool + effects = EFFECTS_TOTAL + exct = Union{} + isa(sym, Symbol) && (sym = GlobalRef(frame_module(sv), sym)) + if isa(sym, GlobalRef) + rte = abstract_eval_globalref(interp, sym, sv) + if rte.exct == Union{} rt = Const(true) + elseif rte.rt === Union{} && rte.exct === UndefVarError + rt = Const(false) else effects = Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE) end @@ -2936,7 +3158,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif ehead === :invoke || ehead === :invoke_modify error("type inference data-flow error: tried to double infer a function") elseif ehead === :isdefined - return abstract_eval_isdefined(interp, e, vtypes, sv) + return abstract_eval_isdefined_expr(interp, e, vtypes, sv) elseif ehead === :throw_undef_if_not return abstract_eval_throw_undef_if_not(interp, e, vtypes, sv) elseif ehead === :boundscheck @@ -3041,45 +3263,116 @@ function override_effects(effects::Effects, override::EffectsOverride) nortcall = override.nortcall ? true : effects.nortcall) end -isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) -isdefinedconst_globalref(g::GlobalRef) = isconst(g) && isdefined_globalref(g) +world_range(ir::IRCode) = ir.valid_worlds +world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world) +world_range(compact::IncrementalCompact) = world_range(compact.ir) -function abstract_eval_globalref_type(g::GlobalRef) - if isdefinedconst_globalref(g) - return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) +function force_binding_resolution!(g::GlobalRef) + # Force resolution of the binding + # TODO: This will go away once we switch over to fully partitioned semantics + ccall(:jl_globalref_boundp, Cint, (Any,), g) + return nothing +end + +function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact}, retry_after_resolve::Bool=true) + worlds = world_range(src) + partition = lookup_binding_partition(min_world(worlds), g) + partition.max_world < max_world(worlds) && return Any + while is_some_imported(binding_kind(partition)) + imported_binding = partition_restriction(partition)::Core.Binding + partition = lookup_binding_partition(min_world(worlds), imported_binding) + partition.max_world < max_world(worlds) && return Any + end + if is_some_guard(binding_kind(partition)) + if retry_after_resolve + # This method is surprisingly hot. For performance, don't ask the runtime to resolve + # the binding unless necessary - doing so triggers an additional lookup, which though + # not super expensive is hot enough to show up in benchmarks. + force_binding_resolution!(g) + return abstract_eval_globalref_type(g, src, false) + end + # return Union{} + return Any end - ty = ccall(:jl_get_binding_type, Any, (Any, Any), g.mod, g.name) - ty === nothing && return Any - return ty + if is_some_const_binding(binding_kind(partition)) + return Const(partition_restriction(partition)) + end + return partition_restriction(partition) end -abstract_eval_global(M::Module, s::Symbol) = abstract_eval_globalref_type(GlobalRef(M, s)) -function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - rt = abstract_eval_globalref_type(g) +function abstract_eval_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) + force_binding_resolution!(g) + partition = lookup_binding_partition(get_inference_world(interp), g) + update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world)) + + while is_some_imported(binding_kind(partition)) + imported_binding = partition_restriction(partition)::Core.Binding + partition = lookup_binding_partition(get_inference_world(interp), imported_binding) + update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world)) + end + + return partition +end + +function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Core.BindingPartition) consistent = inaccessiblememonly = ALWAYS_FALSE nothrow = false - if isa(rt, Const) - consistent = ALWAYS_TRUE - nothrow = true - if is_mutation_free_argtype(rt) - inaccessiblememonly = ALWAYS_TRUE - end - elseif InferenceParams(interp).assume_bindings_static - consistent = inaccessiblememonly = ALWAYS_TRUE - if isdefined_globalref(g) - nothrow = true + generic_effects = Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) + if is_some_guard(binding_kind(partition)) + if InferenceParams(interp).assume_bindings_static + return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) else - rt = Union{} + # We do not currently assume an invalidation for guard -> defined transitions + # return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) + return RTEffects(Any, UndefVarError, generic_effects) + end + end + + if is_some_const_binding(binding_kind(partition)) + rt = Const(partition_restriction(partition)) + return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE)) + end + + rt = partition_restriction(partition) + + if InferenceParams(interp).assume_bindings_static + if isdefined(g, :binding) && isdefined(g.binding, :value) + return RTEffects(rt, Union{}, Effecst(generic_effects, nothrow=true)) end - elseif isdefinedconst_globalref(g) - nothrow = true + # We do not assume in general that assigned global bindings remain assigned. + # The existence of pkgimages allows them to revert in practice. end - return RTEffects(rt, nothrow ? Union{} : UndefVarError, Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly)) + + return RTEffects(rt, UndefVarError, generic_effects) +end + +function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) + partition = abstract_eval_binding_partition!(interp, g, sv) + return abstract_eval_partition_load(interp, partition) +end + +function global_assignment_exct(interp::AbstractInterpreter, sv::AbsIntState, g::GlobalRef, @nospecialize(newty)) + partition = abstract_eval_binding_partition!(interp, g, sv) + return global_assignment_binding_exct(partition, newty) +end + +function global_assignment_binding_exct(partition::Core.BindingPartition, @nospecialize(newty)) + kind = binding_kind(partition) + if is_some_guard(kind) || is_some_const_binding(kind) + return ErrorException + end + + ty = partition_restriction(partition) + if !(widenconst(newty) <: ty) + return TypeError + end + + return Union{} end function handle_global_assignment!(interp::AbstractInterpreter, frame::InferenceState, lhs::GlobalRef, @nospecialize(newty)) effect_free = ALWAYS_FALSE - nothrow = global_assignment_nothrow(lhs.mod, lhs.name, ignorelimited(newty)) + nothrow = global_assignment_exct(interp, frame, lhs, ignorelimited(newty)) === Union{} inaccessiblememonly = ALWAYS_FALSE if !nothrow sub_curr_ssaflag!(frame, IR_FLAG_NOTHROW) diff --git a/base/compiler/cicache.jl b/base/compiler/cicache.jl index bf32e8f12f085..a66d7f9f09650 100644 --- a/base/compiler/cicache.jl +++ b/base/compiler/cicache.jl @@ -31,6 +31,8 @@ WorldRange(r::UnitRange) = WorldRange(first(r), last(r)) first(wr::WorldRange) = wr.min_world last(wr::WorldRange) = wr.max_world in(world::UInt, wr::WorldRange) = wr.min_world <= world <= wr.max_world +min_world(wr::WorldRange) = first(wr) +max_world(wr::WorldRange) = last(wr) function intersect(a::WorldRange, b::WorldRange) ret = WorldRange(max(a.min_world, b.min_world), min(a.max_world, b.max_world)) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index e8508ade88b6c..aeb3e6849773b 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -307,8 +307,10 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe isa(stmt, GotoNode) && return (true, false, true) isa(stmt, GotoIfNot) && return (true, false, ⊑(𝕃ₒ, argextype(stmt.cond, src), Bool)) if isa(stmt, GlobalRef) - nothrow = consistent = isdefinedconst_globalref(stmt) - return (consistent, nothrow, nothrow) + # Modeled more precisely in abstract_eval_globalref. In general, if a + # GlobalRef was moved to statement position, it is probably not `const`, + # so we can't say much about it anyway. + return (false, false, false) elseif isa(stmt, Expr) (; head, args) = stmt if head === :static_parameter @@ -444,7 +446,7 @@ function argextype( elseif isa(x, QuoteNode) return Const(x.value) elseif isa(x, GlobalRef) - return abstract_eval_globalref_type(x) + return abstract_eval_globalref_type(x, src) elseif isa(x, PhiNode) || isa(x, PhiCNode) || isa(x, UpsilonNode) return Any elseif isa(x, PiNode) @@ -1277,7 +1279,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) # types of call arguments only once `slot2reg` converts this `IRCode` to the SSA form # and eliminates slots (see below) argtypes = sv.slottypes - return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes) + return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, WorldRange(ci.min_world, ci.max_world)) end function process_meta!(meta::Vector{Expr}, @nospecialize stmt) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index ae4c04241fa13..8d5ba8353b2c0 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1694,11 +1694,6 @@ function early_inline_special_case(ir::IRCode, stmt::Expr, flag::UInt32, if has_flag(flag, IR_FLAG_NOTHROW) return SomeCase(quoted(val)) end - elseif f === Core.get_binding_type - length(argtypes) == 3 || return nothing - if get_binding_type_effect_free(argtypes[2], argtypes[3]) - return SomeCase(quoted(val)) - end end end if f === compilerbarrier diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 90eab43a3f25b..41423a03cc276 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -430,14 +430,17 @@ struct IRCode cfg::CFG new_nodes::NewNodeStream meta::Vector{Expr} + valid_worlds::WorldRange - function IRCode(stmts::InstructionStream, cfg::CFG, debuginfo::DebugInfoStream, argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState}) + function IRCode(stmts::InstructionStream, cfg::CFG, debuginfo::DebugInfoStream, + argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState}, + valid_worlds=WorldRange(typemin(UInt), typemax(UInt))) return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta) end function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream) di = ir.debuginfo @assert di.codelocs === stmts.line - return new(stmts, ir.argtypes, ir.sptypes, di, cfg, new_nodes, ir.meta) + return new(stmts, ir.argtypes, ir.sptypes, di, cfg, new_nodes, ir.meta, ir.valid_worlds) end global function copy(ir::IRCode) di = ir.debuginfo @@ -445,7 +448,7 @@ struct IRCode di = copy(di) di.edges = copy(di.edges) di.codelocs = stmts.line - return new(stmts, copy(ir.argtypes), copy(ir.sptypes), di, copy(ir.cfg), copy(ir.new_nodes), copy(ir.meta)) + return new(stmts, copy(ir.argtypes), copy(ir.sptypes), di, copy(ir.cfg), copy(ir.new_nodes), copy(ir.meta), ir.valid_worlds) end end diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 2b0721b8d2408..675ca2dea9b32 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -44,7 +44,7 @@ function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{A di = DebugInfoStream(nothing, ci.debuginfo, nstmts) stmts = InstructionStream(code, ssavaluetypes, info, di.codelocs, ci.ssaflags) meta = Expr[] - return IRCode(stmts, cfg, di, argtypes, meta, sptypes) + return IRCode(stmts, cfg, di, argtypes, meta, sptypes, WorldRange(ci.min_world, ci.max_world)) end """ diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index bfe33c23871fe..b483c307a2f5e 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -474,9 +474,9 @@ function lift_leaves(compact::IncrementalCompact, field::Int, elseif isa(leaf, QuoteNode) leaf = leaf.value elseif isa(leaf, GlobalRef) - mod, name = leaf.mod, leaf.name - if isdefined(mod, name) && isconst(mod, name) - leaf = getglobal(mod, name) + typ = argextype(leaf, compact) + if isa(typ, Const) + leaf = typ.val else return nothing end diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 2eacdf0f56cfe..6fc87934d3bc5 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -176,7 +176,7 @@ function typ_for_val(@nospecialize(x), ci::CodeInfo, ir::IRCode, idx::Int, slott end return (ci.ssavaluetypes::Vector{Any})[idx] end - isa(x, GlobalRef) && return abstract_eval_globalref_type(x) + isa(x, GlobalRef) && return abstract_eval_globalref_type(x, ci) isa(x, SSAValue) && return (ci.ssavaluetypes::Vector{Any})[x.id] isa(x, Argument) && return slottypes[x.n] isa(x, NewSSAValue) && return types(ir)[new_to_regular(x, length(ir.stmts))] diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 80e252dde3a02..aaa1354fd5e54 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -407,10 +407,7 @@ end if isa(a1, DataType) && !isabstracttype(a1) if a1 === Module hasintersect(widenconst(sym), Symbol) || return Bottom - if isa(sym, Const) && isa(sym.val, Symbol) && isa(arg1, Const) && - isdefinedconst_globalref(GlobalRef(arg1.val::Module, sym.val::Symbol)) - return Const(true) - end + # isa(sym, Const) case intercepted in abstract interpretation elseif isa(sym, Const) val = sym.val if isa(val, Symbol) @@ -1160,7 +1157,9 @@ end if isa(sv, Module) setfield && return Bottom if isa(nv, Symbol) - return abstract_eval_global(sv, nv) + # In ordinary inference, this case is intercepted early and + # re-routed to `getglobal`. + return Any end return Bottom end @@ -1407,8 +1406,9 @@ end elseif ff === Core.modifyglobal! o = unwrapva(argtypes[2]) f = unwrapva(argtypes[3]) - RT = modifyglobal!_tfunc(𝕃ᵢ, o, f, Any, Any, Symbol) - TF = getglobal_tfunc(𝕃ᵢ, o, f, Symbol) + GT = abstract_eval_get_binding_type(interp, sv, o, f).rt + RT = isa(GT, Const) ? Pair{GT.val, GT.val} : Pair + TF = isa(GT, Const) ? GT.val : Any elseif ff === Core.memoryrefmodify! o = unwrapva(argtypes[2]) RT = memoryrefmodify!_tfunc(𝕃ᵢ, o, Any, Any, Symbol, Bool) @@ -2277,20 +2277,6 @@ function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argt elseif f === typeassert na == 2 || return false return typeassert_nothrow(𝕃, argtypes[1], argtypes[2]) - elseif f === getglobal - if na == 2 - return getglobal_nothrow(argtypes[1], argtypes[2]) - elseif na == 3 - return getglobal_nothrow(argtypes[1], argtypes[2], argtypes[3]) - end - return false - elseif f === setglobal! - if na == 3 - return setglobal!_nothrow(argtypes[1], argtypes[2], argtypes[3]) - elseif na == 4 - return setglobal!_nothrow(argtypes[1], argtypes[2], argtypes[3], argtypes[4]) - end - return false elseif f === Core.get_binding_type na == 2 || return false return get_binding_type_nothrow(𝕃, argtypes[1], argtypes[2]) @@ -2473,7 +2459,8 @@ function getfield_effects(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospeci end end if hasintersect(widenconst(obj), Module) - inaccessiblememonly = getglobal_effects(argtypes, rt).inaccessiblememonly + # Modeled more precisely in abstract_eval_getglobal + inaccessiblememonly = ALWAYS_FALSE elseif is_mutation_free_argtype(obj) inaccessiblememonly = ALWAYS_TRUE else @@ -2482,24 +2469,7 @@ function getfield_effects(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospeci return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly, noub) end -function getglobal_effects(argtypes::Vector{Any}, @nospecialize(rt)) - 2 ≤ length(argtypes) ≤ 3 || return EFFECTS_THROWS - consistent = inaccessiblememonly = ALWAYS_FALSE - nothrow = false - M, s = argtypes[1], argtypes[2] - if (length(argtypes) == 3 ? getglobal_nothrow(M, s, argtypes[3]) : getglobal_nothrow(M, s)) - nothrow = true - # typeasserts below are already checked in `getglobal_nothrow` - Mval, sval = (M::Const).val::Module, (s::Const).val::Symbol - if isconst(Mval, sval) - consistent = ALWAYS_TRUE - if is_mutation_free_argtype(rt) - inaccessiblememonly = ALWAYS_TRUE - end - end - end - return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) -end + """ builtin_effects(𝕃::AbstractLattice, f::Builtin, argtypes::Vector{Any}, rt) -> Effects @@ -2525,11 +2495,13 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty if f === isdefined return isdefined_effects(𝕃, argtypes) elseif f === getglobal - return getglobal_effects(argtypes, rt) + 2 ≤ length(argtypes) ≤ 3 || return EFFECTS_THROWS + # Modeled more precisely in abstract_eval_getglobal + return Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE, nothrow=false, inaccessiblememonly=ALWAYS_FALSE) elseif f === Core.get_binding_type length(argtypes) == 2 || return EFFECTS_THROWS - effect_free = get_binding_type_effect_free(argtypes[1], argtypes[2]) ? ALWAYS_TRUE : ALWAYS_FALSE - return Effects(EFFECTS_TOTAL; effect_free) + # Modeled more precisely in abstract_eval_get_binding_type + return Effects(EFFECTS_TOTAL; effect_free=ALWAYS_FALSE) elseif f === compilerbarrier length(argtypes) == 2 || return Effects(EFFECTS_THROWS; consistent=ALWAYS_FALSE) setting = argtypes[1] @@ -3065,118 +3037,28 @@ function typename_static(@nospecialize(t)) return isType(t) ? _typename(t.parameters[1]) : Core.TypeName end -function global_order_nothrow(@nospecialize(o), loading::Bool, storing::Bool) - o isa Const || return false +function global_order_exct(@nospecialize(o), loading::Bool, storing::Bool) + if !(o isa Const) + if o === Symbol + return ConcurrencyViolationError + elseif !hasintersect(o, Symbol) + return TypeError + else + return Union{ConcurrencyViolationError, TypeError} + end + end sym = o.val if sym isa Symbol order = get_atomic_order(sym, loading, storing) - return order !== MEMORY_ORDER_INVALID && order !== MEMORY_ORDER_NOTATOMIC - end - return false -end -@nospecs function getglobal_nothrow(M, s, o) - global_order_nothrow(o, #=loading=#true, #=storing=#false) || return false - return getglobal_nothrow(M, s) -end -@nospecs function getglobal_nothrow(M, s) - if M isa Const && s isa Const - M, s = M.val, s.val - if M isa Module && s isa Symbol - return isdefinedconst_globalref(GlobalRef(M, s)) - end - end - return false -end -@nospecs function getglobal_tfunc(𝕃::AbstractLattice, M, s, order=Symbol) - if M isa Const && s isa Const - M, s = M.val, s.val - if M isa Module && s isa Symbol - return abstract_eval_global(M, s) - end - return Bottom - elseif !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol)) - return Bottom - end - T = get_binding_type_tfunc(𝕃, M, s) - T isa Const && return T.val - return Any -end -@nospecs function setglobal!_tfunc(𝕃::AbstractLattice, M, s, v, order=Symbol) - if !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol)) - return Bottom - end - return v -end -@nospecs function swapglobal!_tfunc(𝕃::AbstractLattice, M, s, v, order=Symbol) - setglobal!_tfunc(𝕃, M, s, v) === Bottom && return Bottom - return getglobal_tfunc(𝕃, M, s) -end -@nospecs function modifyglobal!_tfunc(𝕃::AbstractLattice, M, s, op, v, order=Symbol) - T = get_binding_type_tfunc(𝕃, M, s) - T === Bottom && return Bottom - T isa Const || return Pair - T = T.val - return Pair{T, T} -end -@nospecs function replaceglobal!_tfunc(𝕃::AbstractLattice, M, s, x, v, success_order=Symbol, failure_order=Symbol) - v = setglobal!_tfunc(𝕃, M, s, v) - v === Bottom && return Bottom - T = get_binding_type_tfunc(𝕃, M, s) - T === Bottom && return Bottom - T isa Const || return ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T - T = T.val - return ccall(:jl_apply_cmpswap_type, Any, (Any,), T) -end -@nospecs function setglobalonce!_tfunc(𝕃::AbstractLattice, M, s, v, success_order=Symbol, failure_order=Symbol) - setglobal!_tfunc(𝕃, M, s, v) === Bottom && return Bottom - return Bool -end - -add_tfunc(Core.getglobal, 2, 3, getglobal_tfunc, 1) -add_tfunc(Core.setglobal!, 3, 4, setglobal!_tfunc, 3) -add_tfunc(Core.swapglobal!, 3, 4, swapglobal!_tfunc, 3) -add_tfunc(Core.modifyglobal!, 4, 5, modifyglobal!_tfunc, 3) -add_tfunc(Core.replaceglobal!, 4, 6, replaceglobal!_tfunc, 3) -add_tfunc(Core.setglobalonce!, 3, 5, setglobalonce!_tfunc, 3) - -@nospecs function setglobal!_nothrow(M, s, newty, o) - global_order_nothrow(o, #=loading=#false, #=storing=#true) || return false - return setglobal!_nothrow(M, s, newty) -end -@nospecs function setglobal!_nothrow(M, s, newty) - if M isa Const && s isa Const - M, s = M.val, s.val - if isa(M, Module) && isa(s, Symbol) - return global_assignment_nothrow(M, s, newty) - end - end - return false -end - -function global_assignment_nothrow(M::Module, s::Symbol, @nospecialize(newty)) - if !isconst(M, s) - ty = ccall(:jl_get_binding_type, Any, (Any, Any), M, s) - return ty isa Type && widenconst(newty) <: ty - end - return false -end - -@nospecs function get_binding_type_effect_free(M, s) - if M isa Const && s isa Const - M, s = M.val, s.val - if M isa Module && s isa Symbol - return ccall(:jl_get_binding_type, Any, (Any, Any), M, s) !== nothing + if order !== MEMORY_ORDER_INVALID && order !== MEMORY_ORDER_NOTATOMIC + return Union{} + else + return ConcurrencyViolationError end + else + return TypeError end - return false -end -@nospecs function get_binding_type_tfunc(𝕃::AbstractLattice, M, s) - if get_binding_type_effect_free(M, s) - return Const(Core.get_binding_type((M::Const).val::Module, (s::Const).val::Symbol)) - end - return Type end -add_tfunc(Core.get_binding_type, 2, 2, get_binding_type_tfunc, 0) @nospecs function get_binding_type_nothrow(𝕃::AbstractLattice, M, s) ⊑ = partialorder(𝕃) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index dd526a24d6494..1da58af38d545 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -230,13 +230,20 @@ const BINDING_KIND_DECLARED = 0x7 const BINDING_KIND_GUARD = 0x8 is_some_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT) +is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED) +is_some_guard(kind::UInt8) = (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED) function lookup_binding_partition(world::UInt, b::Core.Binding) ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) end function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) - ccall(:jl_get_globalref_partition, Ref{Core.BindingPartition}, (Any, UInt), gr, world) + if isdefined(gr, :binding) + b = gr.binding + else + b = ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), gr.mod, gr.name, true) + end + return lookup_binding_partition(world, b) end partition_restriction(bpart::Core.BindingPartition) = ccall(:jl_bpart_get_restriction_value, Any, (Any,), bpart) diff --git a/src/julia_internal.h b/src/julia_internal.h index 9a8750bbc2500..f3959490855c8 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -956,7 +956,6 @@ STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFE } JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world); -JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr JL_PROPAGATES_ROOT, size_t world); EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); diff --git a/src/module.c b/src/module.c index bdacd487e978d..1655c781111b0 100644 --- a/src/module.c +++ b/src/module.c @@ -60,18 +60,6 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) } } -JL_DLLEXPORT jl_binding_partition_t *jl_get_globalref_partition(jl_globalref_t *gr, size_t world) -{ - if (!gr) - return NULL; - jl_binding_t *b = NULL; - if (gr) - b = gr->binding; - if (!b) - b = jl_get_module_binding(gr->mod, gr->name, 0); - return jl_get_binding_partition(b, world); -} - JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_names) { jl_task_t *ct = jl_current_task; diff --git a/src/rtutils.c b/src/rtutils.c index faa087dcb077d..7b04fbca5d032 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -288,6 +288,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh) if (!old_gc_state || !eh->gc_state) // it was or is unsafe now jl_gc_safepoint_(ptls); jl_value_t *exception = ptls->sig_exception; + JL_GC_PROMISE_ROOTED(exception); if (exception) { int8_t oldstate = jl_gc_unsafe_enter(ptls); /* The temporary ptls->bt_data is rooted by special purpose code in the diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 67191a024da73..23f3337ab5e8e 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -624,11 +624,13 @@ function is_call_graph_uncached(sv::CC.InferenceState) return is_call_graph_uncached(parent::CC.InferenceState) end +isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) + # aggressive global binding resolution within `repl_frame` function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, sv::CC.InferenceState) if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv)) - if CC.isdefined_globalref(g) + if isdefined_globalref(g) return CC.RTEffects(Const(ccall(:jl_get_globalref_value, Any, (Any,), g)), Union{}, CC.EFFECTS_TOTAL) end return CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS) @@ -655,7 +657,7 @@ function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f), a1val, a2val = a1.val, a2.val if isa(a1val, Module) && isa(a2val, Symbol) g = GlobalRef(a1val, a2val) - if CC.isdefined_globalref(g) + if isdefined_globalref(g) return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) end return Union{} diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 2fc7e917186f4..9fafc9bdca6ad 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1184,9 +1184,6 @@ let isdefined_tfunc(@nospecialize xs...) = @test isdefined_tfunc(ComplexF32, Const(0)) === Const(false) @test isdefined_tfunc(SometimesDefined, Const(:x)) == Bool @test isdefined_tfunc(SometimesDefined, Const(:y)) === Const(false) - @test isdefined_tfunc(Const(Base), Const(:length)) === Const(true) - @test isdefined_tfunc(Const(Base), Symbol) == Bool - @test isdefined_tfunc(Const(Base), Const(:NotCurrentlyDefinedButWhoKnows)) == Bool @test isdefined_tfunc(Core.SimpleVector, Const(1)) === Const(false) @test Const(false) ⊑ isdefined_tfunc(Const(:x), Symbol) @test Const(false) ⊑ isdefined_tfunc(Const(:x), Const(:y)) diff --git a/test/rebinding.jl b/test/rebinding.jl index 564be70e44913..c93c34be7a75c 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -3,6 +3,8 @@ module Rebinding using Test + make_foo() = Foo(1) + @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD struct Foo x::Int @@ -17,6 +19,16 @@ module Rebinding @test Base.binding_kind(@__MODULE__, :Foo) == Base.BINDING_KIND_GUARD @test contains(repr(x), "@world") + struct Foo + x::Int + end + @test Foo != typeof(x) + + # This tests that the compiler uses the correct world, but does not test + # invalidation. + @test typeof(Base.invoke_in_world(defined_world_age, make_foo)) == typeof(x) + @test typeof(make_foo()) == Foo + # Tests for @world syntax @test Base.@world(Foo, defined_world_age) == typeof(x) @test Base.@world(Rebinding.Foo, defined_world_age) == typeof(x) From 9d1cedb2379111a01eab53b811fbf76a1af53bd2 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 1 Nov 2024 22:49:37 -0400 Subject: [PATCH 545/548] Restore JL_NOTSAFEPOINT in jl_stderr_obj (#56407) This is not a function we're really using, but it's used in the embedding examples, so I'm sure somebody would complain if I deleted it or made it a safepoint, so let's just give the same best-effort result as before. --- src/julia.h | 1 + src/module.c | 22 ++++++++++++++++++++++ src/rtutils.c | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/julia.h b/src/julia.h index bfb641d38374b..5b9986a5e68ee 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1854,6 +1854,7 @@ JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, size_t len); JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void); JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b JL_PROPAGATES_ROOT); JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b JL_PROPAGATES_ROOT); +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t *b JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); diff --git a/src/module.c b/src/module.c index 1655c781111b0..08ad0d64dbf55 100644 --- a/src/module.c +++ b/src/module.c @@ -360,6 +360,28 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t return decode_restriction_value(pku); } +JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved(jl_binding_t *b) +{ + // Unlike jl_get_binding_value this doesn't try to allocate new binding partitions if they + // don't already exist, making this JL_NOTSAFEPOINT. + if (!b) + return NULL; + jl_binding_partition_t *bpart = jl_atomic_load_relaxed(&b->partitions); + if (!bpart) + return NULL; + size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); + if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) + return NULL; + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + return NULL; + if (jl_bkind_is_some_import(decode_restriction_kind(pku))) + return NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return decode_restriction_value(pku); + return jl_atomic_load_relaxed(&b->value); +} + JL_DLLEXPORT jl_value_t *jl_bpart_get_restriction_value(jl_binding_partition_t *bpart) { jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); diff --git a/src/rtutils.c b/src/rtutils.c index 7b04fbca5d032..7e1fb576008f6 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -567,7 +567,7 @@ JL_DLLEXPORT jl_value_t *jl_stderr_obj(void) JL_NOTSAFEPOINT if (jl_base_module == NULL) return NULL; jl_binding_t *stderr_obj = jl_get_module_binding(jl_base_module, jl_symbol("stderr"), 0); - return stderr_obj ? jl_get_binding_value(stderr_obj) : NULL; + return stderr_obj ? jl_get_binding_value_if_resolved(stderr_obj) : NULL; } // toys for debugging --------------------------------------------------------- From 715eb1dd51ee60cec30425affe5d14941a60d0ac Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:27:10 +0900 Subject: [PATCH 546/548] reland "Inlining: Remove outdated code path for GlobalRef movement (#46880)" (#56382) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the description of the original PR: > We used to not allow `GlobalRef` in `PhiNode` at all (because they > could have side effects). However, we then change the IR to make > side-effecting `GlobalRef`s illegal in statement position in general, > so now `PhiNode`s values are just regular value position, so there's > no reason any more to try to move `GlobalRef`s out to statement > position in inlining. Moreover, doing so introduces a bunch of > unnecessary `GlobalRef`s that weren't being moved back. We could fix > that separately by setting appropriate flags, but it's simpler to just > get rid of this special case entirely. This change itself does not sound to have any issues, and in fact, it is very useful for keeping the IR slim, especially in code generated by Cassette-like systems, so I would like to reland it. However, the original PR was reverted in JuliaLang/julia#46951 due to bugs like JuliaLang/julia#46940 and JuliaLang/julia#46943. I could not reproduce these bugs on my end (maybe they have been fixed on some GC-side fixes?), so I believe relanding the original PR’s changes would not cause any issues, but it is necessary to confirm that similar problems do not arise before merging this PR. --- base/compiler/ssair/inlining.jl | 12 ------------ test/compiler/EscapeAnalysis/EscapeAnalysis.jl | 4 ++-- test/compiler/inline.jl | 7 +++++++ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 8d5ba8353b2c0..98be475520f01 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -665,18 +665,6 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun compact.active_result_bb -= 1 refinish = true end - # It is possible for GlobalRefs and Exprs to be in argument position - # at this point in the IR, though in that case they are required - # to be effect-free. However, we must still move them out of argument - # position, since `Argument` is allowed in PhiNodes, but `GlobalRef` - # and `Expr` are not, so a substitution could anger the verifier. - for aidx in 1:length(argexprs) - aexpr = argexprs[aidx] - if isa(aexpr, Expr) || isa(aexpr, GlobalRef) - ninst = removable_if_unused(NewInstruction(aexpr, argextype(aexpr, compact), compact.result[idx][:line])) - argexprs[aidx] = insert_node_here!(compact, ninst) - end - end if isa(item, InliningTodo) compact.ssa_rename[old_idx] = ir_inline_item!(compact, idx, argexprs, item, boundscheck, state.todo_bbs) elseif isa(item, UnionSplit) diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index 9afe49c01562d..4799fe4cee5ca 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -671,8 +671,8 @@ end @test has_all_escape(result.state[Argument(3)]) # b end let result = @eval EATModule() begin - const Rx = SafeRef{String}("Rx") - $code_escapes((String,)) do s + const Rx = SafeRef(Ref("")) + $code_escapes((Base.RefValue{String},)) do s Rx[] = s Core.sizeof(Rx[]) end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 53f7adc2a2a77..416f3873c5422 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -2282,3 +2282,10 @@ let;Base.Experimental.@force_compile f_EA_finalizer(42000) @test foreign_buffer_checker.finalized end + +# Test that inlining doesn't unnecessarily move things to statement position +@noinline f_noinline_invoke(x::Union{Symbol,Nothing}=nothing) = Core.donotdelete(x) +g_noinline_invoke(x) = f_noinline_invoke(x) +let src = code_typed1(g_noinline_invoke, (Union{Symbol,Nothing},)) + @test !any(@nospecialize(x)->isa(x,GlobalRef), src.code) +end From 671fc4244a800561e870bf3aa4933063287d801b Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Sat, 2 Nov 2024 04:58:08 -0700 Subject: [PATCH 547/548] copy effects key to `Base.infer_effects` (#56363) Copied from the docstring of `Core.Compiler.Effects`, this makes it easier to figure out what the output of `Base.infer_effects` is actually telling you. --- base/compiler/effects.jl | 75 +++++++++++++++++++++------------------- base/reflection.jl | 2 ++ 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 3d9b69360b317..a2e7e3dde603d 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -1,3 +1,42 @@ +const effects_key_string = """ +## Key for `show` output of Effects: + +The output represents the state of different effect properties in the following order: + +1. `consistent` (`c`): + - `+c` (green): `ALWAYS_TRUE` + - `-c` (red): `ALWAYS_FALSE` + - `?c` (yellow): `CONSISTENT_IF_NOTRETURNED` and/or `CONSISTENT_IF_INACCESSIBLEMEMONLY` +2. `effect_free` (`e`): + - `+e` (green): `ALWAYS_TRUE` + - `-e` (red): `ALWAYS_FALSE` + - `?e` (yellow): `EFFECT_FREE_IF_INACCESSIBLEMEMONLY` +3. `nothrow` (`n`): + - `+n` (green): `true` + - `-n` (red): `false` +4. `terminates` (`t`): + - `+t` (green): `true` + - `-t` (red): `false` +5. `notaskstate` (`s`): + - `+s` (green): `true` + - `-s` (red): `false` +6. `inaccessiblememonly` (`m`): + - `+m` (green): `ALWAYS_TRUE` + - `-m` (red): `ALWAYS_FALSE` + - `?m` (yellow): `INACCESSIBLEMEM_OR_ARGMEMONLY` +7. `noub` (`u`): + - `+u` (green): `true` + - `-u` (red): `false` + - `?u` (yellow): `NOUB_IF_NOINBOUNDS` +8. `:nonoverlayed` (`o`): + - `+o` (green): `ALWAYS_TRUE` + - `-o` (red): `ALWAYS_FALSE` + - `?o` (yellow): `CONSISTENT_OVERLAY` +9. `:nortcall` (`r`): + - `+r` (green): `true` + - `-r` (red): `false` +""" + """ effects::Effects @@ -74,42 +113,8 @@ initialized with `ALWAYS_TRUE`/`true` and then transitioned towards `ALWAYS_FALS Note that within the current flow-insensitive analysis design, effects detected by local analysis on each statement usually taint the global conclusion conservatively. -## Key for `show` output of Effects: -The output represents the state of different effect properties in the following order: - -1. `consistent` (`c`): - - `+c` (green): `ALWAYS_TRUE` - - `-c` (red): `ALWAYS_FALSE` - - `?c` (yellow): `CONSISTENT_IF_NOTRETURNED` and/or `CONSISTENT_IF_INACCESSIBLEMEMONLY` -2. `effect_free` (`e`): - - `+e` (green): `ALWAYS_TRUE` - - `-e` (red): `ALWAYS_FALSE` - - `?e` (yellow): `EFFECT_FREE_IF_INACCESSIBLEMEMONLY` -3. `nothrow` (`n`): - - `+n` (green): `true` - - `-n` (red): `false` -4. `terminates` (`t`): - - `+t` (green): `true` - - `-t` (red): `false` -5. `notaskstate` (`s`): - - `+s` (green): `true` - - `-s` (red): `false` -6. `inaccessiblememonly` (`m`): - - `+m` (green): `ALWAYS_TRUE` - - `-m` (red): `ALWAYS_FALSE` - - `?m` (yellow): `INACCESSIBLEMEM_OR_ARGMEMONLY` -7. `noub` (`u`): - - `+u` (green): `true` - - `-u` (red): `false` - - `?u` (yellow): `NOUB_IF_NOINBOUNDS` -8. `:nonoverlayed` (`o`): - - `+o` (green): `ALWAYS_TRUE` - - `-o` (red): `ALWAYS_FALSE` - - `?o` (yellow): `CONSISTENT_OVERLAY` -9. `:nortcall` (`r`): - - `+r` (green): `true` - - `-r` (red): `false` +$(effects_key_string) """ struct Effects consistent::UInt8 diff --git a/base/reflection.jl b/base/reflection.jl index 8fe8d324eb792..f2a554e0f27c5 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -877,6 +877,8 @@ signature, the `:nothrow` bit gets tainted. The `Base.infer_effects` function should not be used from generated functions; doing so will result in an error. +$(Core.Compiler.effects_key_string) + # See Also - [`Core.Compiler.Effects`](@ref): A type representing the computational effects of a method call. - [`Base.@assume_effects`](@ref): A macro for making assumptions about the effects of a method. From fe67097b0d277bce02186443b80c53f852b3e878 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Sat, 2 Nov 2024 14:48:02 +0000 Subject: [PATCH 548/548] Fix `make install` for asan build (#56347) Now the makescript finds libclang_rt.asan-x86_64.so for example. The change from `-0` to `-1` is as with `-1`, `libclang_rt.asan-*` is searched for in `usr/lib/julia` instead of `usr/lib`. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d1e5b31f85b1c..2cac2f8818324 100644 --- a/Makefile +++ b/Makefile @@ -255,9 +255,9 @@ JL_PRIVATE_LIBS-$(USE_SYSTEM_CSL) += libpthread endif ifeq ($(SANITIZE),1) ifeq ($(USECLANG),1) -JL_PRIVATE_LIBS-1 += libclang_rt.asan +JL_PRIVATE_LIBS-0 += libclang_rt.asan-* else -JL_PRIVATE_LIBS-1 += libasan +JL_PRIVATE_LIBS-0 += libasan endif endif