From 4250be80f52531fda3c72542c3e351c5ad67b4a5 Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Thu, 9 Jan 2025 11:37:29 +0100 Subject: [PATCH 1/4] add detection for zen 5 (#56967) ref https://github.com/llvm/llvm-project/commit/149a150b50c112e26fc5acbdd58250c44ccd777f --------- Co-authored-by: gbaraldi --- src/features_x86.h | 28 +++++++++++++++++++++++++++- src/processor_x86.cpp | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/features_x86.h b/src/features_x86.h index 2ecc8fee32a38d..b6e2b23985b4ff 100644 --- a/src/features_x86.h +++ b/src/features_x86.h @@ -5,6 +5,13 @@ #else #define JL_X86_64ONLY_VER(x) x #endif +// The code is similar to what is here so the bits can be used as reference +// https://github.com/llvm/llvm-project/blob/3f7905733820851bc4f65cb4af693c3101cbf20d/llvm/lib/TargetParser/Host.cpp#L1257 + +// The way the bits here work is an index into the features array. This is a bit array +// The index works as follows: +// 32*i + j where i is the index into the array and j is the bit in the array. +// There is a reference to what each index corresponds to in _get_host_cpu // X86 features definition // EAX=1: ECX @@ -79,6 +86,7 @@ JL_FEATURE_DEF(avx512vp2intersect, 32 * 4 + 8, 0) JL_FEATURE_DEF(serialize, 32 * 4 + 14, 110000) JL_FEATURE_DEF(tsxldtrk, 32 * 4 + 16, 110000) JL_FEATURE_DEF(pconfig, 32 * 4 + 18, 0) +// JL_FEATURE_DEF(ibt, 32 * 4 + 20, 0) JL_FEATURE_DEF_NAME(amx_bf16, 32 * 4 + 22, 110000, "amx-bf16") JL_FEATURE_DEF(avx512fp16, 32 * 4 + 23, 140000) JL_FEATURE_DEF_NAME(amx_tile, 32 * 4 + 24, 110000, "amx-tile") @@ -110,10 +118,28 @@ JL_FEATURE_DEF(clzero, 32 * 8 + 0, 0) JL_FEATURE_DEF(wbnoinvd, 32 * 8 + 9, 0) // EAX=7,ECX=1: EAX +JL_FEATURE_DEF(sha512, 32 * 9 + 0, 170000) +JL_FEATURE_DEF(sm3, 32 * 9 + 1, 170000) +JL_FEATURE_DEF(sm4, 32 * 9 + 2, 170000) +JL_FEATURE_DEF(raoint, 32 * 9 + 3, 170000) JL_FEATURE_DEF(avxvnni, 32 * 9 + 4, 120000) JL_FEATURE_DEF(avx512bf16, 32 * 9 + 5, 0) +JL_FEATURE_DEF(cmpccxadd, 32 * 9 + 7, 160000) +JL_FEATURE_DEF_NAME(amx_fp16, 32 * 9 + 21, 160000, "amx-fp16") +JL_FEATURE_DEF(hreset, 32 * 9 + 22, 160000) +JL_FEATURE_DEF(avxifma, 32 * 9 + 23, 160000) + +// EAX=7,ECX=1: EBX +JL_FEATURE_DEF(avxvnniint8, 32 * 10 + 4, 160000) +JL_FEATURE_DEF(avxneconvert, 32 * 10 + 5, 160000) +JL_FEATURE_DEF_NAME(amx_complex, 32 * 10 + 8, 170000, "amx-complex") +JL_FEATURE_DEF(avxvnniint16, 32 * 10 + 10, 170000) +JL_FEATURE_DEF(prefetchi, 32 * 10 + 14, 160000) +JL_FEATURE_DEF(usermsr, 32 * 10 + 15, 170000) +// JL_FEATURE_DEF(avx10, 32 * 10 + 19, 170000) // TODO: What to do about avx10 and it's mess? +// JL_FEATURE_DEF(apxf, 32 * 10 + 21, 190000) // EAX=0x14,ECX=0: EBX -JL_FEATURE_DEF(ptwrite, 32 * 10 + 4, 0) +JL_FEATURE_DEF(ptwrite, 32 * 11 + 4, 0) #undef JL_X86_64ONLY_VER diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index f1dff063de1d96..bf765be160ed2a 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -96,9 +96,10 @@ enum class CPU : uint32_t { amd_znver2, amd_znver3, amd_znver4, + amd_znver5, }; -static constexpr size_t feature_sz = 11; +static constexpr size_t feature_sz = 12; static constexpr FeatureName feature_names[] = { #define JL_FEATURE_DEF(name, bit, llvmver) {#name, bit, llvmver}, #define JL_FEATURE_DEF_NAME(name, bit, llvmver, str) {str, bit, llvmver}, @@ -141,6 +142,10 @@ static constexpr FeatureDep deps[] = { {vpclmulqdq, avx}, {vpclmulqdq, pclmul}, {avxvnni, avx2}, + {avxvnniint8, avx2}, + {avxvnniint16, avx2}, + {avxifma, avx2}, + {avxneconvert, avx2}, {avx512f, avx2}, {avx512dq, avx512f}, {avx512ifma, avx512f}, @@ -159,6 +164,8 @@ static constexpr FeatureDep deps[] = { {avx512fp16, avx512vl}, {amx_int8, amx_tile}, {amx_bf16, amx_tile}, + {amx_fp16, amx_tile}, + {amx_complex, amx_tile}, {sse4a, sse3}, {xop, fma4}, {fma4, avx}, @@ -166,6 +173,9 @@ static constexpr FeatureDep deps[] = { {xsaveopt, xsave}, {xsavec, xsave}, {xsaves, xsave}, + {sha512, avx2}, + {sm3, avx}, + {sm4, avx2}, }; // We require cx16 on 64bit by default. This can be overwritten with `-cx16` @@ -236,6 +246,7 @@ constexpr auto znver2 = znver1 | get_feature_masks(clwb, rdpid, wbnoinvd); constexpr auto znver3 = znver2 | get_feature_masks(shstk, pku, vaes, vpclmulqdq); constexpr auto znver4 = znver3 | get_feature_masks(avx512f, avx512cd, avx512dq, avx512bw, avx512vl, avx512ifma, avx512vbmi, avx512vbmi2, avx512vnni, avx512bitalg, avx512vpopcntdq, avx512bf16, gfni, shstk, xsaves); +constexpr auto znver5 = znver4 | get_feature_masks(avxvnni, movdiri, movdir64b, avx512vp2intersect, prefetchi, avxvnni); } @@ -298,6 +309,7 @@ static constexpr CPUSpec cpus[] = { {"znver2", CPU::amd_znver2, CPU::generic, 0, Feature::znver2}, {"znver3", CPU::amd_znver3, CPU::amd_znver2, 120000, Feature::znver3}, {"znver4", CPU::amd_znver4, CPU::amd_znver3, 160000, Feature::znver4}, + {"znver5", CPU::amd_znver5, CPU::amd_znver4, 190000, Feature::znver5}, }; static constexpr size_t ncpu_names = sizeof(cpus) / sizeof(cpus[0]); @@ -575,6 +587,9 @@ static CPU get_amd_processor_name(uint32_t family, uint32_t model, const uint32_ return CPU::amd_znver4; } return CPU::amd_znver3; // fallback + case 26: + // if (model <= 0x77) + return CPU::amd_znver5; } } @@ -660,11 +675,12 @@ static NOINLINE std::pair> _get_host_cpu(void) int32_t info7[4]; jl_cpuidex(info7, 7, 1); features[9] = info7[0]; + features[10] = info7[1]; } if (maxleaf >= 0x14) { int32_t info14[4]; jl_cpuidex(info14, 0x14, 0); - features[10] = info14[1]; + features[11] = info14[1]; } // Fix up AVX bits to account for OS support and match LLVM model @@ -705,7 +721,20 @@ static NOINLINE std::pair> _get_host_cpu(void) else { cpu = uint32_t(CPU::generic); } - + /* Feature bits to register map + feature[0] = ecx + feature[1] = edx + feature[2] = leaf 7 ebx + feature[3] = leaf 7 ecx + feature[4] = leaf 7 edx + feature[5] = leaf 0x80000001 ecx + feature[6] = leaf 0x80000001 edx + feature[7] = leaf 0xd subleaf 1 eax + feature[8] = leaf 0x80000008 ebx + feature[9] = leaf 7 ebx subleaf 1 eax + feature[10] = leaf 7 ebx subleaf 1 ebx + feature[11] = leaf 0x14 ebx + */ return std::make_pair(cpu, features); } From 1ebacacc5a82e16930bd5ae70b641a3cbd6ee883 Mon Sep 17 00:00:00 2001 From: Will Tebbutt Date: Thu, 9 Jan 2025 11:30:55 +0000 Subject: [PATCH 2/4] fix deepcopy for non-trivial circular references (#56990) Resolves #56775 . Credit for this fix rests entirely with bbrehm . --------- Co-authored-by: Lilith Orion Hafner Co-authored-by: Neven Sajko <4944410+nsajko@users.noreply.github.com> --- base/deepcopy.jl | 7 +++++-- test/copy.jl | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/base/deepcopy.jl b/base/deepcopy.jl index c4f9ae1a6cb105..f60ce2043dd5ac 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -120,11 +120,14 @@ function _deepcopy_memory_t(@nospecialize(x::Memory), T, stackdict::IdDict) end return dest end -@eval function deepcopy_internal(x::Array{T, N}, stackdict::IdDict) where {T, N} +function deepcopy_internal(x::Array{T, N}, stackdict::IdDict) where {T, N} if haskey(stackdict, x) return stackdict[x]::typeof(x) end - stackdict[x] = $(Expr(:new, :(Array{T, N}), :(deepcopy_internal(x.ref, stackdict)), :(x.size))) + y = stackdict[x] = Array{T, N}(undef, ntuple(Returns(0), Val{N}())) + setfield!(y, :ref, deepcopy_internal(x.ref, stackdict)) + setfield!(y, :size, x.size) + y end function deepcopy_internal(x::GenericMemoryRef, stackdict::IdDict) if haskey(stackdict, x) diff --git a/test/copy.jl b/test/copy.jl index 559bf5d3e757af..f5cc57c86feaae 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -253,6 +253,15 @@ end @test copyto!(s, String[]) == [1, 2] # No error end +@testset "circular reference arrays" begin + # issue 56775 + p = Any[nothing] + p[1] = p + p2 = deepcopy(p) + @test p2 === p2[1] + @test p2 !== p +end + @testset "deepcopy_internal arrays" begin @test (@inferred Base.deepcopy_internal(zeros(), IdDict())) == zeros() end From cfd3922cabfdfca0b417651ee4cea5a649cbebe5 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 9 Jan 2025 17:28:11 +0100 Subject: [PATCH 3/4] Add basic code for binding partition revalidation (#56649) This adds the binding partition revalidation code from #54654. This is the last piece of that PR that hasn't been merged yet - however the TODO in that PR still stands for future work. This PR itself adds a callback that gets triggered by deleting a binding. It will then walk all code in the system and invalidate code instances of Methods whose lowered source referenced the given global. This walk is quite slow. Future work will add backedges and optimizations to make this faster, but the basic functionality should be in place with this PR. --- base/Base_compiler.jl | 1 + base/invalidation.jl | 111 ++++++++++++++++++++++++++++++++++++++++++ src/gf.c | 5 ++ src/module.c | 26 +++++++++- test/rebinding.jl | 7 +++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 base/invalidation.jl diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index 91f327980389d2..db3ebb0232e38d 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -255,6 +255,7 @@ include("ordering.jl") using .Order include("coreir.jl") +include("invalidation.jl") # For OS specific stuff # We need to strcat things here, before strings are really defined diff --git a/base/invalidation.jl b/base/invalidation.jl new file mode 100644 index 00000000000000..40a010ab7361c2 --- /dev/null +++ b/base/invalidation.jl @@ -0,0 +1,111 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct GlobalRefIterator + mod::Module +end +IteratorSize(::Type{GlobalRefIterator}) = SizeUnknown() +globalrefs(mod::Module) = GlobalRefIterator(mod) + +function iterate(gri::GlobalRefIterator, i = 1) + m = gri.mod + table = ccall(:jl_module_get_bindings, Ref{SimpleVector}, (Any,), m) + i == length(table) && return nothing + b = table[i] + b === nothing && return iterate(gri, i+1) + return ((b::Core.Binding).globalref, i+1) +end + +const TYPE_TYPE_MT = Type.body.name.mt +const NONFUNCTION_MT = Core.MethodTable.name.mt +function foreach_module_mtable(visit, m::Module, world::UInt) + for gb in globalrefs(m) + binding = gb.binding + bpart = lookup_binding_partition(world, binding) + if is_defined_const_binding(binding_kind(bpart)) + v = partition_restriction(bpart) + uw = unwrap_unionall(v) + name = gb.name + if isa(uw, DataType) + tn = uw.name + if tn.module === m && tn.name === name && tn.wrapper === v && isdefined(tn, :mt) + # this is the original/primary binding for the type (name/wrapper) + mt = tn.mt + if mt !== nothing && mt !== TYPE_TYPE_MT && mt !== NONFUNCTION_MT + @assert mt.module === m + visit(mt) || return false + end + end + elseif isa(v, Module) && v !== m && parentmodule(v) === m && _nameof(v) === name + # this is the original/primary binding for the submodule + foreach_module_mtable(visit, v, world) || return false + elseif isa(v, Core.MethodTable) && v.module === m && v.name === name + # this is probably an external method table here, so let's + # assume so as there is no way to precisely distinguish them + visit(v) || return false + end + end + end + return true +end + +function foreach_reachable_mtable(visit, world::UInt) + visit(TYPE_TYPE_MT) || return + visit(NONFUNCTION_MT) || return + for mod in loaded_modules_array() + foreach_module_mtable(visit, mod, world) + end +end + +function should_invalidate_code_for_globalref(gr::GlobalRef, src::CodeInfo) + found_any = false + labelchangemap = nothing + stmts = src.code + isgr(g::GlobalRef) = gr.mod == g.mod && gr.name === g.name + isgr(g) = false + for i = 1:length(stmts) + stmt = stmts[i] + if isgr(stmt) + found_any = true + continue + end + for ur in Compiler.userefs(stmt) + arg = ur[] + # If any of the GlobalRefs in this stmt match the one that + # we are about, we need to move out all GlobalRefs to preserve + # effect order, in case we later invalidate a different GR + if isa(arg, GlobalRef) + if isgr(arg) + @assert !isa(stmt, PhiNode) + found_any = true + break + end + end + end + end + return found_any +end + +function invalidate_code_for_globalref!(gr::GlobalRef, new_max_world::UInt) + valid_in_valuepos = false + foreach_reachable_mtable(new_max_world) do mt::Core.MethodTable + for method in MethodList(mt) + if isdefined(method, :source) + src = _uncompressed_ir(method) + old_stmts = src.code + if should_invalidate_code_for_globalref(gr, src) + for mi in specializations(method) + ci = mi.cache + while true + if ci.max_world > new_max_world + ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), ci, new_max_world) + end + isdefined(ci, :next) || break + ci = ci.next + end + end + end + end + end + return true + end +end diff --git a/src/gf.c b/src/gf.c index 080d1ebd52ba80..ba28edfbeeff70 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1867,6 +1867,11 @@ static void invalidate_code_instance(jl_code_instance_t *replaced, size_t max_wo JL_UNLOCK(&replaced_mi->def.method->writelock); } +JL_DLLEXPORT void jl_invalidate_code_instance(jl_code_instance_t *replaced, size_t max_world) +{ + invalidate_code_instance(replaced, max_world, 1); +} + static void _invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_world, int depth) { jl_array_t *backedges = replaced_mi->backedges; if (backedges) { diff --git a/src/module.c b/src/module.c index 1b4c5bd78f667f..004371b9144b24 100644 --- a/src/module.c +++ b/src/module.c @@ -1032,6 +1032,21 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var jl_gc_wb(bpart, val); } +void jl_invalidate_binding_refs(jl_globalref_t *ref, size_t new_world) +{ + static jl_value_t *invalidate_code_for_globalref = NULL; + if (invalidate_code_for_globalref == NULL && jl_base_module != NULL) + invalidate_code_for_globalref = jl_get_global(jl_base_module, jl_symbol("invalidate_code_for_globalref!")); + if (!invalidate_code_for_globalref) + jl_error("Binding invalidation is not permitted during bootstrap."); + if (jl_generating_output()) + jl_error("Binding invalidation is not permitted during image generation."); + jl_value_t *boxed_world = jl_box_ulong(new_world); + JL_GC_PUSH1(&boxed_world); + jl_call2((jl_function_t*)invalidate_code_for_globalref, (jl_value_t*)ref, boxed_world); + JL_GC_POP(); +} + extern jl_mutex_t world_counter_lock; JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) { @@ -1046,9 +1061,11 @@ JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) JL_LOCK(&world_counter_lock); jl_task_t *ct = jl_current_task; + size_t last_world = ct->world_age; size_t new_max_world = jl_atomic_load_acquire(&jl_world_counter); - // TODO: Trigger invalidation here - (void)ct; + ct->world_age = jl_typeinf_world; + jl_invalidate_binding_refs(gr, new_max_world); + ct->world_age = last_world; 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); @@ -1334,6 +1351,11 @@ JL_DLLEXPORT void jl_add_to_module_init_list(jl_value_t *mod) jl_array_ptr_1d_push(jl_module_init_order, mod); } +JL_DLLEXPORT jl_svec_t *jl_module_get_bindings(jl_module_t *m) +{ + return jl_atomic_load_relaxed(&m->bindings); +} + JL_DLLEXPORT void jl_init_restored_module(jl_value_t *mod) { if (!jl_generating_output() || jl_options.incremental) { diff --git a/test/rebinding.jl b/test/rebinding.jl index c93c34be7a75c6..ad0ad1fc1643d2 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -33,4 +33,11 @@ module Rebinding @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) + + # Test invalidation (const -> undefined) + const delete_me = 1 + f_return_delete_me() = delete_me + @test f_return_delete_me() == 1 + Base.delete_binding(@__MODULE__, :delete_me) + @test_throws UndefVarError f_return_delete_me() end From 3d85309e800fcc8368a8291cc76bc28a10067b10 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 9 Jan 2025 17:29:39 +0100 Subject: [PATCH 4/4] Consolidate various isdefined functionality into a new builtin (#56985) In #54999 I extended `:isdefined` with the ability to specify whether or not to consider imported bindings defined. As a result, we now have two mechanisms for querying `isdefined` on globals (the other being `Core.isdefined`) with incompatible feature sets (`Core.isdefined` supports an atomic ordering argument, but not `allow_import`). Additionally, only one of them had proper codegen support. I also don't like to have IR forms for things that could be perfectly well handled by builtin calls (along the lines of #56713). So this tries to clean that all up by: 1. Adding a new builtin `isdefinedglobal` that has the full feature set 2. Dropping `:isdefined` on globals as an IR form (the frontend form gets lowered to the intrinsic if called on globals) 3. Wiring up codegen and correcting inference for that new builtin An additional motivation is that `isdefined` on globals needs support for partition edges (like other builtins), and having to have a special case for :isdefined was marginally annoying. Just using an intrinsic for this is much cleaner. Lastly, the reason for a new intrinsic over extending the existing `isdefined`, is that over time we've moved away from conflating fields and globals for Module (e.g. introducing `getglobal`/`setglobal!`), so this is a natural extension of that. Of course, the existing behavior is retained for ordinary `isdefined`. --- Compiler/src/Compiler.jl | 4 +- Compiler/src/abstractinterpretation.jl | 105 +++++++++++++++++++------ Compiler/src/ssair/verify.jl | 4 +- base/boot.jl | 2 +- base/docs/basedocs.jl | 34 ++++++++ doc/src/base/base.md | 1 + doc/src/devdocs/ast.md | 7 +- src/builtin_proto.h | 1 + src/builtins.c | 28 +++++++ src/codegen.cpp | 98 +++++++++++++++-------- src/interpreter.c | 13 +-- src/julia-syntax.scm | 10 ++- src/staticdata.c | 5 +- test/meta.jl | 2 +- test/syntax.jl | 34 ++++---- 15 files changed, 245 insertions(+), 103 deletions(-) diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index a9ab27a682ad7d..ea939f86422c55 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -49,7 +49,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc using Base using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, - BINDING_KIND_GLOBAL, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, + BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES, OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME, _array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any, @@ -58,7 +58,7 @@ using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospeciali datatype_pointerfree, decode_effects_override, diff_names, fieldindex, generating_output, get_nospecializeinfer_sig, get_world_counter, has_free_typevars, hasgenerator, hasintersect, indexed_iterate, isType, is_file_tracked, is_function_def, - is_meta_expr, is_meta_expr_head, is_nospecialized, is_nospecializeinfer, + is_meta_expr, is_meta_expr_head, is_nospecialized, is_nospecializeinfer, is_defined_const_binding, is_some_const_binding, is_some_guard, is_some_imported, is_valid_intrinsic_elptr, isbitsunion, isconcretedispatch, isdispatchelem, isexpr, isfieldatomic, isidentityfree, iskindtype, ismutabletypename, ismutationfree, issingletontype, isvarargtype, isvatuple, diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index f2d4461a9874a4..0c340a89d9878e 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2637,21 +2637,14 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), elseif f === Core.getfield && argtypes_are_actually_getglobal(argtypes) return Future(abstract_eval_getglobal(interp, sv, si.saw_latestworld, argtypes)) elseif f === Core.isdefined && argtypes_are_actually_getglobal(argtypes) - exct = Bottom - if length(argtypes) == 4 - order = argtypes[4] - exct = global_order_exct(order, #=loading=#true, #=storing=#false) - if !(isa(order, Const) && get_atomic_order(order.val, #=loading=#true, #=storing=#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::Module, - (argtypes[3]::Const).val::Symbol), - si.saw_latestworld, - sv), - NoCallInfo()), exct)) + return Future(abstract_eval_isdefinedglobal(interp, argtypes[2], argtypes[3], Const(true), + length(argtypes) == 4 ? argtypes[4] : Const(:unordered), + si.saw_latestworld, sv)) + elseif f === Core.isdefinedglobal && 3 <= length(argtypes) <= 5 + return Future(abstract_eval_isdefinedglobal(interp, argtypes[2], argtypes[3], + length(argtypes) >= 4 ? argtypes[4] : Const(true), + length(argtypes) >= 5 ? argtypes[5] : Const(:unordered), + si.saw_latestworld, sv)) elseif f === Core.get_binding_type return Future(abstract_eval_get_binding_type(interp, sv, argtypes)) end @@ -3203,21 +3196,73 @@ function abstract_eval_isdefined_expr(interp::AbstractInterpreter, e::Expr, ssta return abstract_eval_isdefined(interp, sym, sstate.saw_latestworld, sv) end -function abstract_eval_isdefined(interp::AbstractInterpreter, @nospecialize(sym), saw_latestworld::Bool, sv::AbsIntState) +const generic_isdefinedglobal_effects = Effects(EFFECTS_TOTAL, consistent=ALWAYS_FALSE, nothrow=false) +function abstract_eval_isdefinedglobal(interp::AbstractInterpreter, mod::Module, sym::Symbol, allow_import::Union{Bool, Nothing}, saw_latestworld::Bool, sv::AbsIntState) rt = Bool + if saw_latestworld + return RTEffects(rt, Union{}, Effects(generic_isdefinedglobal_effects, nothrow=true)) + end + effects = EFFECTS_TOTAL - exct = Union{} - isa(sym, Symbol) && (sym = GlobalRef(frame_module(sv), sym)) - if isa(sym, GlobalRef) - rte = abstract_eval_globalref(interp, sym, saw_latestworld, sv) + partition = lookup_binding_partition!(interp, GlobalRef(mod, sym), sv) + if allow_import !== true && is_some_imported(binding_kind(partition)) + if allow_import === false + rt = Const(false) + else + effects = Effects(generic_isdefinedglobal_effects, nothrow=true) + end + else + partition = walk_binding_partition!(interp, partition, sv) + rte = abstract_eval_partition_load(interp, partition) 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) + effects = Effects(generic_isdefinedglobal_effects, nothrow=true) + end + end + return RTEffects(rt, Union{}, effects) +end + +function abstract_eval_isdefinedglobal(interp::AbstractInterpreter, @nospecialize(M), @nospecialize(s), @nospecialize(allow_import_arg), @nospecialize(order_arg), saw_latestworld::Bool, sv::AbsIntState) + exct = Bottom + allow_import = true + if allow_import_arg !== nothing + if !isa(allow_import_arg, Const) + allow_import = nothing + if widenconst(allow_import_arg) != Bool + exct = Union{exct, TypeError} + end + else + allow_import = allow_import_arg.val + end + end + if order_arg !== nothing + exct = global_order_exct(order_arg, #=loading=#true, #=storing=#false) + if !(isa(order_arg, Const) && get_atomic_order(order_arg.val, #=loading=#true, #=storing=#false).x >= MEMORY_ORDER_UNORDERED.x) + exct = Union{exct, ConcurrencyViolationError} + end + end + if M isa Const && s isa Const + M, s = M.val, s.val + if M isa Module && s isa Symbol + return merge_exct(CallMeta(abstract_eval_isdefinedglobal(interp, M, s, allow_import, saw_latestworld, sv), NoCallInfo()), exct) end - elseif isexpr(sym, :static_parameter) + 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(Bool, Union{exct, UndefVarError}, generic_isdefinedglobal_effects, NoCallInfo()) + end + return CallMeta(Bool, Union{exct, TypeError, UndefVarError}, generic_isdefinedglobal_effects, NoCallInfo()) +end + +function abstract_eval_isdefined(interp::AbstractInterpreter, @nospecialize(sym), saw_latestworld::Bool, sv::AbsIntState) + rt = Bool + effects = EFFECTS_TOTAL + exct = Union{} + if isexpr(sym, :static_parameter) n = sym.args[1]::Int if 1 <= n <= length(sv.sptypes) sp = sv.sptypes[n] @@ -3443,22 +3488,31 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, return partition_restriction(partition) end -function abstract_eval_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) +function lookup_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)) + partition +end +function walk_binding_partition!(interp::AbstractInterpreter, partition::Core.BindingPartition, sv::AbsIntState) 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_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) + partition = lookup_binding_partition!(interp, g, sv) + partition = walk_binding_partition!(interp, partition, sv) return partition end function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Core.BindingPartition) - if is_some_guard(binding_kind(partition)) + kind = binding_kind(partition) + if is_some_guard(kind) || kind == BINDING_KIND_UNDEF_CONST if InferenceParams(interp).assume_bindings_static return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) else @@ -3468,13 +3522,12 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co end end - if is_some_const_binding(binding_kind(partition)) + if is_defined_const_binding(kind) 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) - return RTEffects(rt, UndefVarError, generic_getglobal_effects) end diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 072a564a31f784..12eb09be693f37 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -366,7 +366,7 @@ function verify_ir(ir::IRCode, print::Bool=true, @verify_error "Assignment should have been removed during SSA conversion" raise_error() elseif stmt.head === :isdefined - if length(stmt.args) > 2 || (length(stmt.args) == 2 && !isa(stmt.args[2], Bool)) + if length(stmt.args) > 2 @verify_error "malformed isdefined" raise_error() end @@ -382,7 +382,7 @@ function verify_ir(ir::IRCode, print::Bool=true, elseif stmt.head === :foreigncall isforeigncall = true elseif stmt.head === :isdefined && length(stmt.args) == 1 && - (stmt.args[1] isa GlobalRef || isexpr(stmt.args[1], :static_parameter)) + isexpr(stmt.args[1], :static_parameter) # a GlobalRef or static_parameter isdefined check does not evaluate its argument continue elseif stmt.head === :call diff --git a/base/boot.jl b/base/boot.jl index 05788844fde664..53e439d83ebe22 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -231,7 +231,7 @@ export fieldtype, getfield, setfield!, swapfield!, modifyfield!, replacefield!, setfieldonce!, nfields, throw, tuple, ===, isdefined, eval, # access to globals - getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!, + getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!, isdefinedglobal, # ifelse, sizeof # not exported, to avoid conflicting with Base # type reflection <:, typeof, isa, typeassert, diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 16175010a20fa8..88ed34de02b64b 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2754,6 +2754,9 @@ compatible with the stores to that location. Otherwise, if not declared as To test whether an array element is defined, use [`isassigned`](@ref) instead. +The global variable variant is supported for compatibility with older julia +releases. For new code, prefer [`isdefinedglobal`](@ref). + See also [`@isdefined`](@ref). # Examples @@ -2781,6 +2784,37 @@ false """ isdefined + +""" + isdefinedglobal(m::Module, s::Symbol, [allow_import::Bool=true, [order::Symbol=:unordered]]) + +Tests whether a global variable `s` is defined in module `m` (in the current world age). +A variable is considered defined if and only if a value may be read from this global variable +and an access will not throw. This includes both constants and global variables that have +a value set. + +If `allow_import` is `false`, the global variable must be defined inside `m` +and may not be imported from another module. + +See also [`@isdefined`](@ref). + +# Examples +```jldoctest +julia> isdefinedglobal(Base, :sum) +true + +julia> isdefinedglobal(Base, :NonExistentMethod) +false + +julia> isdefinedglobal(Base, :sum, false) +true + +julia> isdefinedglobal(Main, :sum, false) +false +``` +""" +isdefinedglobal + """ Memory{T}(undef, n) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 7181965d9aa816..e6c8ff554d4940 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -166,6 +166,7 @@ Core.replacefield! Core.swapfield! Core.setfieldonce! Core.isdefined +Core.isdefinedglobal Base.@isdefined Base.convert Base.promote diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index d8db3ca677082d..fe63dfe35edacb 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -426,11 +426,8 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. * `isdefined` - `Expr(:isdefined, :x [, allow_import])` returns a Bool indicating whether `x` has - already been defined in the current scope. The optional second argument is a boolean - that specifies whether `x` should be considered defined by an import or if only constants - or globals in the current module count as being defined. If `x` is not a global, the argument - is ignored. + `Expr(:isdefined, :x)` returns a Bool indicating whether `x` has + already been defined in the current scope. * `the_exception` diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 70ad67040991da..77463ae4884cbc 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -45,6 +45,7 @@ DECLARE_BUILTIN(invoke); DECLARE_BUILTIN(is); DECLARE_BUILTIN(isa); DECLARE_BUILTIN(isdefined); +DECLARE_BUILTIN(isdefinedglobal); DECLARE_BUILTIN(issubtype); DECLARE_BUILTIN(memorynew); DECLARE_BUILTIN(memoryref); diff --git a/src/builtins.c b/src/builtins.c index c326e53ea968d2..90d8a0d453e20b 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1352,6 +1352,33 @@ JL_CALLABLE(jl_f_getglobal) return v; } +JL_CALLABLE(jl_f_isdefinedglobal) +{ + jl_module_t *m = NULL; + jl_sym_t *s = NULL; + JL_NARGS(isdefined, 2, 3); + int allow_import = 1; + enum jl_memory_order order = jl_memory_order_unspecified; + JL_TYPECHK(isdefined, module, args[0]); + JL_TYPECHK(isdefined, symbol, args[1]); + if (nargs == 3) { + JL_TYPECHK(isdefined, bool, args[2]); + allow_import = jl_unbox_bool(args[2]); + } + if (nargs == 4) { + JL_TYPECHK(isdefined, symbol, args[3]); + order = jl_get_atomic_order_checked((jl_sym_t*)args[2], 1, 0); + } + m = (jl_module_t*)args[0]; + s = (jl_sym_t*)args[1]; + if (order == jl_memory_order_unspecified) + order = jl_memory_order_unordered; + if (order < jl_memory_order_unordered) + jl_atomic_error("isdefined: module binding cannot be accessed non-atomically"); + int bound = jl_boundp(m, s, allow_import); // seq_cst always + return bound ? jl_true : jl_false; +} + JL_CALLABLE(jl_f_setglobal) { enum jl_memory_order order = jl_memory_order_release; @@ -2451,6 +2478,7 @@ void jl_init_primitives(void) JL_GC_DISABLED // module bindings jl_builtin_getglobal = add_builtin_func("getglobal", jl_f_getglobal); jl_builtin_setglobal = add_builtin_func("setglobal!", jl_f_setglobal); + jl_builtin_isdefinedglobal = add_builtin_func("isdefinedglobal", jl_f_isdefinedglobal); add_builtin_func("get_binding_type", jl_f_get_binding_type); jl_builtin_swapglobal = add_builtin_func("swapglobal!", jl_f_swapglobal); jl_builtin_replaceglobal = add_builtin_func("replaceglobal!", jl_f_replaceglobal); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4b0f4ebd765faa..7bc14d2d0347f6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4173,6 +4173,34 @@ static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return false; } +static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_sym_t *name, int allow_import, enum jl_memory_order order) +{ + Value *isnull = NULL; + jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_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); + LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*))); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_binding); + ai.decorateInst(v); + v->setOrdering(get_llvm_atomic_order(order)); + isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue)); + } + else { + Value *v = ctx.builder.CreateCall(prepare_call(jlboundp_func), { + literal_pointer_val(ctx, (jl_value_t*)modu), + literal_pointer_val(ctx, (jl_value_t*)name), + ConstantInt::get(getInt32Ty(ctx.builder.getContext()), allow_import) + }); + isnull = ctx.builder.CreateICmpNE(v, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)); + } + return mark_julia_type(ctx, isnull, false, jl_bool_type); +} + static bool emit_f_opmemory(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ArrayRef argv, size_t nargs, const jl_cgval_t *modifyop) { @@ -5074,6 +5102,42 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } + else if (f == jl_builtin_isdefinedglobal && (nargs == 2 || nargs == 3 || nargs == 4)) { + const jl_cgval_t &mod = argv[1]; + const jl_cgval_t &sym = argv[2]; + bool allow_import = true; + enum jl_memory_order order = jl_memory_order_unspecified; + + if (nargs >= 3) { + const jl_cgval_t &arg3 = argv[3]; + if (arg3.constant && jl_is_bool(arg3.constant)) + allow_import = jl_unbox_bool(arg3.constant); + else + return false; + } + + if (nargs == 4) { + const jl_cgval_t &arg4 = argv[4]; + if (arg4.constant && jl_is_symbol(arg4.constant)) + order = jl_get_atomic_order((jl_sym_t*)arg4.constant, true, false); + else + return false; + } + else + order = jl_memory_order_unordered; + + if (order < jl_memory_order_unordered) { + return false; + } + + if (!mod.constant || !sym.constant || !jl_is_symbol(sym.constant) || !jl_is_module(mod.constant)) { + return false; + } + + *ret = emit_isdefinedglobal(ctx, (jl_module_t*)mod.constant, (jl_sym_t*)sym.constant, allow_import, order); + return true; + } + else if (f == jl_builtin_isdefined && (nargs == 2 || nargs == 3)) { const jl_cgval_t &obj = argv[1]; const jl_cgval_t &fld = argv[2]; @@ -6074,39 +6138,7 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym, int allow_i isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false, true), emit_tagfrom(ctx, jl_tvar_type)); } else { - jl_module_t *modu; - jl_sym_t *name; - if (jl_is_globalref(sym)) { - modu = jl_globalref_mod(sym); - name = jl_globalref_name(sym); - } - else { - assert(jl_is_symbol(sym) && "malformed isdefined expression"); - modu = ctx.module; - name = (jl_sym_t*)sym; - } - jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0); - jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_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); - LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*))); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_binding); - ai.decorateInst(v); - v->setOrdering(AtomicOrdering::Unordered); - isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue)); - } - else { - Value *v = ctx.builder.CreateCall(prepare_call(jlboundp_func), { - literal_pointer_val(ctx, (jl_value_t*)modu), - literal_pointer_val(ctx, (jl_value_t*)name), - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), allow_import) - }); - isnull = ctx.builder.CreateICmpNE(v, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)); - } + assert(false && "malformed expression"); } return mark_julia_type(ctx, isnull, false, jl_bool_type); } diff --git a/src/interpreter.c b/src/interpreter.c index b9bd1997caa17d..338853b56f6921 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -251,22 +251,15 @@ static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else if (head == jl_isdefined_sym) { jl_value_t *sym = args[0]; int defined = 0; - int allow_import = 1; - if (nargs == 2) { - assert(jl_is_bool(args[1]) && "malformed IR"); - allow_import = args[1] == jl_true; - } + assert(nargs == 1 && "malformed IR"); if (jl_is_slotnumber(sym) || jl_is_argument(sym)) { ssize_t n = jl_slot_number(sym); if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL) jl_error("access to invalid slot number"); defined = s->locals[n - 1] != NULL; } - else if (jl_is_globalref(sym)) { - defined = jl_boundp(jl_globalref_mod(sym), jl_globalref_name(sym), allow_import); - } - else if (jl_is_symbol(sym)) { - defined = jl_boundp(s->module, (jl_sym_t*)sym, allow_import); + else if (jl_is_globalref(sym) || jl_is_symbol(sym)) { + jl_error("[Internal Error]: :isdefined on globalref should use `isdefinedglobal`"); } else if (jl_is_expr(sym) && ((jl_expr_t*)sym)->head == jl_static_parameter_sym) { ssize_t n = jl_unbox_long(jl_exprarg(sym, 0)); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c522a565f462a9..007c3ce8208203 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1022,7 +1022,7 @@ (call (core svec) ,@attrs) ,mut ,min-initialized)) (call (core _setsuper!) ,name ,super) - (if (isdefined (globalref (thismodule) ,name) (false)) + (if (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (block (= ,prev (globalref (thismodule) ,name)) (if (call (core _equiv_typedef) ,prev ,name) @@ -1084,7 +1084,7 @@ (= ,name (call (core _abstracttype) (thismodule) (inert ,name) (call (core svec) ,@params))) (call (core _setsuper!) ,name ,super) (call (core _typebody!) ,name) - (if (&& (isdefined (globalref (thismodule) ,name) (false)) + (if (&& (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) (const (globalref (thismodule) ,name) ,name)) @@ -1105,7 +1105,7 @@ (= ,name (call (core _primitivetype) (thismodule) (inert ,name) (call (core svec) ,@params) ,n)) (call (core _setsuper!) ,name ,super) (call (core _typebody!) ,name) - (if (&& (isdefined (globalref (thismodule) ,name) (false)) + (if (&& (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) (const (globalref (thismodule) ,name) ,name)) @@ -4064,7 +4064,9 @@ f(x) = yt(x) (if (and (vinfo:asgn vi) (vinfo:capt vi)) `(call (core isdefined) ,sym (inert contents)) e)) - (else e)))) + (else (if (globalref? sym) + `(call (core isdefinedglobal) ,(cadr sym) (inert ,(caddr sym))) + e))))) ((_opaque_closure) (let* ((isva (car (cddddr e))) (nargs (cadr (cddddr e))) diff --git a/src/staticdata.c b/src/staticdata.c index bdeecb911e3eb9..4124d3b481bb37 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -101,7 +101,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 195 +#define NUM_TAGS 196 // 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. @@ -311,6 +311,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_builtin_compilerbarrier); INSERT_TAG(jl_builtin_getglobal); INSERT_TAG(jl_builtin_setglobal); + INSERT_TAG(jl_builtin_isdefinedglobal); INSERT_TAG(jl_builtin_swapglobal); INSERT_TAG(jl_builtin_modifyglobal); INSERT_TAG(jl_builtin_replaceglobal); @@ -506,7 +507,7 @@ static htable_t bits_replace; static const jl_fptr_args_t id_to_fptrs[] = { &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__call_latest, &jl_f__call_in_world, &jl_f__call_in_world_total, &jl_f_isdefined, &jl_f_isdefinedglobal, &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_getfield, &jl_f_setfield, &jl_f_swapfield, &jl_f_modifyfield, &jl_f_setfieldonce, &jl_f_replacefield, &jl_f_fieldtype, &jl_f_nfields, &jl_f_apply_type, &jl_f_memorynew, diff --git a/test/meta.jl b/test/meta.jl index 2b235d3cc1b0d1..e9e344bba2e220 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -274,7 +274,7 @@ ci = code_lowered(g, Tuple{Val{true}})[1] @eval isdefined_globalref(x) = $(Expr(:isdefined, GlobalRef(Base, :foo))) ci = code_lowered(isdefined_globalref, Tuple{Int})[1] @test Meta.partially_inline!(copy(ci.code), Any[isdefined_globalref, 1], Tuple{typeof(isdefined_globalref), Int}, - [], 0, 0, :propagate)[1] == Expr(:isdefined, GlobalRef(Base, :foo)) + [], 0, 0, :propagate)[1] == Expr(:call, GlobalRef(Core, :isdefinedglobal), Base, QuoteNode(:foo)) withunreachable(s::String) = sin(s) ci = code_lowered(withunreachable, Tuple{String})[1] diff --git a/test/syntax.jl b/test/syntax.jl index fca8b9e8ccdf52..9fd0204821eab6 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3919,28 +3919,28 @@ module ExtendedIsDefined import .Import.x4 @test x2 == 2 # Resolve the binding @eval begin - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x1))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x2))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x3))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x4))) + @test Core.isdefinedglobal(@__MODULE__, :x1) + @test Core.isdefinedglobal(@__MODULE__, :x2) + @test Core.isdefinedglobal(@__MODULE__, :x3) + @test Core.isdefinedglobal(@__MODULE__, :x4) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x1), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x2), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x3), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x4), false)) + @test Core.isdefinedglobal(@__MODULE__, :x1, false) + @test !Core.isdefinedglobal(@__MODULE__, :x2, false) + @test !Core.isdefinedglobal(@__MODULE__, :x3, false) + @test !Core.isdefinedglobal(@__MODULE__, :x4, false) end @eval begin @Base.Experimental.force_compile - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x1))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x2))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x3))) - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x4))) - - @test $(Expr(:isdefined, GlobalRef(@__MODULE__, :x1), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x2), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x3), false)) - @test !$(Expr(:isdefined, GlobalRef(@__MODULE__, :x4), false)) + @test Core.isdefinedglobal(@__MODULE__, :x1) + @test Core.isdefinedglobal(@__MODULE__, :x2) + @test Core.isdefinedglobal(@__MODULE__, :x3) + @test Core.isdefinedglobal(@__MODULE__, :x4) + + @test Core.isdefinedglobal(@__MODULE__, :x1, false) + @test !Core.isdefinedglobal(@__MODULE__, :x2, false) + @test !Core.isdefinedglobal(@__MODULE__, :x3, false) + @test !Core.isdefinedglobal(@__MODULE__, :x4, false) end end