Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpart: Start enforcing min_world for global variable definitions #57150

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Compiler/test/abioverride.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ let world = Base.tls_world_age()
global new_ci = Core.CodeInstance(Core.ABIOverride(Tuple{typeof(myplus), Int}, mi),
#=owner=#SecondArgConstOverride(1), new_source.rettype, Any#=new_source.exctype is missing=#,
#=inferred_const=#nothing, #=code=#nothing, #=const_flags=#Int32(0),
new_source.min_world, new_source.max_world, #=new_source.ipo_purity_bits is missing=#UInt32(0),
new_source.min_world, typemax(UInt), #=new_source.ipo_purity_bits is missing=#UInt32(0),
#=analysis_results=#nothing, new_source.debuginfo, new_source.edges)

# Poke the CI into the global cache
Expand Down
17 changes: 11 additions & 6 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,23 @@ const META = gensym(:meta)
const METAType = IdDict{Any,Any}

function meta(m::Module; autoinit::Bool=true)
if !isdefined(m, META) || getfield(m, META) === nothing
autoinit ? initmeta(m) : return nothing
if !isdefinedglobal(m, META)
return autoinit ? invokelatest(initmeta, m) : nothing
end
return getfield(m, META)::METAType
# TODO: This `invokelatest` is not technically required, but because
# of the automatic constant backdating is currently required to avoid
# a warning.
return invokelatest(getglobal, m, META)::METAType
end

function initmeta(m::Module)
if !isdefined(m, META) || getfield(m, META) === nothing
Core.eval(m, :($META = $(METAType())))
if !isdefinedglobal(m, META)
val = METAType()
Core.eval(m, :(const $META = $val))
push!(modules, m)
return val
end
nothing
return getglobal(m, META)
end

function signature!(tv::Vector{Any}, expr::Expr)
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ export
@invoke,
invokelatest,
@invokelatest,
@world,

# loading source files
__precompile__,
Expand Down
2 changes: 1 addition & 1 deletion base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1389,7 +1389,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
restored = sv[1]::Vector{Any}
for M in restored
M = M::Module
if isdefined(M, Base.Docs.META) && getfield(M, Base.Docs.META) !== nothing
if isdefinedglobal(M, Base.Docs.META)
push!(Base.Docs.modules, M)
end
if is_root_module(M)
Expand Down
10 changes: 5 additions & 5 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,7 @@ JL_CALLABLE(jl_f_setglobal)
jl_atomic_error("setglobal!: module binding cannot be written non-atomically");
else if (order >= jl_memory_order_seq_cst)
jl_fence();
jl_binding_t *b = jl_get_binding_wr(mod, var, 0);
jl_binding_t *b = jl_get_binding_wr(mod, var);
jl_checked_assignment(b, mod, var, args[2]); // release store
if (order >= jl_memory_order_seq_cst)
jl_fence();
Expand Down Expand Up @@ -1430,7 +1430,7 @@ JL_CALLABLE(jl_f_swapglobal)
if (order == jl_memory_order_notatomic)
jl_atomic_error("swapglobal!: module binding cannot be written non-atomically");
// is seq_cst already, no fence needed
jl_binding_t *b = jl_get_binding_wr(mod, var, 0);
jl_binding_t *b = jl_get_binding_wr(mod, var);
return jl_checked_swap(b, mod, var, args[2]);
}

Expand All @@ -1448,7 +1448,7 @@ JL_CALLABLE(jl_f_modifyglobal)
JL_TYPECHK(modifyglobal!, symbol, (jl_value_t*)var);
if (order == jl_memory_order_notatomic)
jl_atomic_error("modifyglobal!: module binding cannot be written non-atomically");
jl_binding_t *b = jl_get_binding_wr(mod, var, 0);
jl_binding_t *b = jl_get_binding_wr(mod, var);
// is seq_cst already, no fence needed
return jl_checked_modify(b, mod, var, args[2], args[3]);
}
Expand Down Expand Up @@ -1477,7 +1477,7 @@ JL_CALLABLE(jl_f_replaceglobal)
jl_atomic_error("replaceglobal!: module binding cannot be written non-atomically");
if (failure_order == jl_memory_order_notatomic)
jl_atomic_error("replaceglobal!: module binding cannot be accessed non-atomically");
jl_binding_t *b = jl_get_binding_wr(mod, var, 0);
jl_binding_t *b = jl_get_binding_wr(mod, var);
// is seq_cst already, no fence needed
return jl_checked_replace(b, mod, var, args[2], args[3]);
}
Expand Down Expand Up @@ -1506,7 +1506,7 @@ JL_CALLABLE(jl_f_setglobalonce)
jl_atomic_error("setglobalonce!: module binding cannot be written non-atomically");
if (failure_order == jl_memory_order_notatomic)
jl_atomic_error("setglobalonce!: module binding cannot be accessed non-atomically");
jl_binding_t *b = jl_get_binding_wr(mod, var, 0);
jl_binding_t *b = jl_get_binding_wr(mod, var);
// is seq_cst already, no fence needed
jl_value_t *old = jl_checked_assignonce(b, mod, var, args[2]);
return old == NULL ? jl_true : jl_false;
Expand Down
103 changes: 11 additions & 92 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,12 +930,12 @@ static const auto jlgetbindingorerror_func = new JuliaFunction<>{
},
nullptr,
};
static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{
XSTR(jl_get_binding_wr),
static const auto jlcheckbpwritable_func = new JuliaFunction<>{
XSTR(jl_check_binding_currently_writable),
[](LLVMContext &C) {
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
return FunctionType::get(T_pjlvalue,
{T_pjlvalue, T_pjlvalue, getInt32Ty(C)}, false);
return FunctionType::get(getVoidTy(C),
{T_pjlvalue, T_pjlvalue, T_pjlvalue}, false);
},
nullptr,
};
Expand Down Expand Up @@ -2098,8 +2098,6 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed
static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg,
ArrayRef<const char*> 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);
static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa);
static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i);
static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg);
Expand Down Expand Up @@ -3498,19 +3496,17 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
bool issetglobal, bool isreplaceglobal, bool isswapglobal, bool ismodifyglobal, bool issetglobalonce,
const jl_cgval_t *modifyop, bool alloc)
{
jl_binding_t *bnd = NULL;
Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc);
jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1);
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
if (bp == NULL)
return jl_cgval_t();
Value *bp = julia_binding_gv(ctx, bnd);
if (bpart) {
jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction);
if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) {
if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL) {
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
// TODO: use typeassert in jl_check_binding_assign_value too
emit_typecheck(ctx, rval, ty, "typeassert");
rval = update_julia_type(ctx, rval, ty);
if (rval.typ == jl_bottom_type)
Expand Down Expand Up @@ -3545,6 +3541,8 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
}
Value *m = literal_pointer_val(ctx, (jl_value_t*)mod);
Value *s = literal_pointer_val(ctx, (jl_value_t*)sym);
ctx.builder.CreateCall(prepare_call(jlcheckbpwritable_func),
{ bp, m, s });
if (issetglobal) {
ctx.builder.CreateCall(prepare_call(jlcheckassign_func),
{ bp, m, s, mark_callee_rooted(ctx, boxed(ctx, rval)) });
Expand Down Expand Up @@ -5991,85 +5989,6 @@ static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatyp
ctx.builder.SetInsertPoint(ifok);
}

// returns a jl_ppvalue_t location for the global variable m.s
// if the reference currently bound or assign == true,
// pbnd will also be assigned with the binding address
static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s,
jl_binding_t **pbnd, bool assign, bool alloc)
{
jl_binding_t *b = jl_get_module_binding(m, s, 1);
jl_binding_partition_t *bpart = jl_get_binding_partition_all(b, ctx.min_world, ctx.max_world);
jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction);
if (assign) {
if (jl_bkind_is_some_guard(decode_restriction_kind(pku)))
// not yet declared
b = NULL;
}
else {
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_all(b, ctx.min_world, ctx.max_world);
}
pku = jl_walk_binding_inplace_all(&b, &bpart, ctx.min_world, ctx.max_world);
}
if (!b || !bpart) {
// var not found. switch to delayed lookup.
Constant *initnul = Constant::getNullValue(ctx.types().T_pjlvalue);
GlobalVariable *bindinggv = new GlobalVariable(*ctx.f->getParent(), ctx.types().T_pjlvalue,
false, GlobalVariable::PrivateLinkage, initnul, "jl_binding_ptr"); // LLVM has bugs with nameless globals
LoadInst *cachedval = ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, bindinggv, Align(sizeof(void*)));
setName(ctx.emission_context, cachedval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".cached");
cachedval->setOrdering(AtomicOrdering::Unordered);
BasicBlock *have_val = BasicBlock::Create(ctx.builder.getContext(), "found");
BasicBlock *not_found = BasicBlock::Create(ctx.builder.getContext(), "notfound");
BasicBlock *currentbb = ctx.builder.GetInsertBlock();
auto iscached = ctx.builder.CreateICmpNE(cachedval, initnul);
setName(ctx.emission_context, iscached, "iscached");
ctx.builder.CreateCondBr(iscached, have_val, not_found);
not_found->insertInto(ctx.f);
ctx.builder.SetInsertPoint(not_found);
Value *bval = nullptr;
if (assign) {
bval = ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func),
{ literal_pointer_val(ctx, (jl_value_t*)m),
literal_pointer_val(ctx, (jl_value_t*)s),
ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc)});
} else {
bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func),
{ literal_pointer_val(ctx, (jl_value_t*)m),
literal_pointer_val(ctx, (jl_value_t*)s)});
}
setName(ctx.emission_context, bval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".found");
ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release);
ctx.builder.CreateBr(have_val);
have_val->insertInto(ctx.f);
ctx.builder.SetInsertPoint(have_val);
PHINode *p = ctx.builder.CreatePHI(ctx.types().T_pjlvalue, 2);
p->addIncoming(cachedval, currentbb);
p->addIncoming(bval, not_found);
setName(ctx.emission_context, p, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s));
return p;
}
if (assign) {
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),
literal_pointer_val(ctx, (jl_value_t*)s),
ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc) });
CreateTrap(ctx.builder);
return NULL;
}
}
else {
if (b->deprecated)
cg_bdw(ctx, s, b);
}
*pbnd = b;
return julia_binding_gv(ctx, b);
}

static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa)
{
LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)));
Expand Down Expand Up @@ -10184,7 +10103,7 @@ static void init_jit_functions(void)
add_named_global(jltypeerror_func, &jl_type_error);
add_named_global(jlcheckassign_func, &jl_checked_assignment);
add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error);
add_named_global(jlgetbindingwrorerror_func, &jl_get_binding_wr);
add_named_global(jlcheckbpwritable_func, &jl_check_binding_currently_writable);
add_named_global(jlboundp_func, &jl_boundp);
for (auto it : builtin_func_map())
add_named_global(it.second, it.first);
Expand Down
1 change: 1 addition & 0 deletions src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
XX(jl_get_binding_for_method_def) \
XX(jl_get_binding_or_error) \
XX(jl_get_binding_wr) \
XX(jl_check_binding_currently_writable) \
XX(jl_get_cpu_name) \
XX(jl_get_cpu_features) \
XX(jl_cpu_has_fma) \
Expand Down
11 changes: 8 additions & 3 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -4626,6 +4626,8 @@ f(x) = yt(x)
(if (globalref? lhs)
(begin
(emit `(global ,lhs))
(if (null? (cadr lam))
(emit `(latestworld)))
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)))
(emit `(= ,lhs ,rhs))))
(define (emit-assignment lhs rhs)
Expand Down Expand Up @@ -4944,13 +4946,16 @@ f(x) = yt(x)
#f))
((global) ; keep global declarations as statements
(if value (error "misplaced \"global\" declaration"))
(emit e))
(emit e)
(if (null? (cadr lam))
(emit `(latestworld))))
((globaldecl)
(if value (error "misplaced \"global\" declaration"))
(if (atom? (caddr e)) (emit e)
(if (atom? (caddr e)) (begin (emit e) (emit `(latestworld)))
(let ((rr (make-ssavalue)))
(emit `(= ,rr ,(caddr e)))
(emit `(globaldecl ,(cadr e) ,rr)))))
(emit `(globaldecl ,(cadr e) ,rr))
(emit `(latestworld)))))
((local-def) #f)
((local) #f)
((moved-local)
Expand Down
3 changes: 2 additions & 1 deletion src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -2001,7 +2001,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var
JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var);
JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var);
// get binding for assignment
JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc);
JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s);
JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import);
JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var);
Expand Down
Loading