diff --git a/base/client.jl b/base/client.jl index 842389224637f..d335f378f24c1 100644 --- a/base/client.jl +++ b/base/client.jl @@ -124,14 +124,14 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool) end if lasterr !== nothing lasterr = scrub_repl_backtrace(lasterr) - istrivialerror(lasterr) || ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, lasterr) + istrivialerror(lasterr) || setglobal!(Main, :err, lasterr) invokelatest(display_error, errio, lasterr) errcount = 0 lasterr = nothing else ast = Meta.lower(Main, ast) value = Core.eval(Main, ast) - ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value) + setglobal!(Main, :ans, value) if !(value === nothing) && show_value if have_color print(answer_color()) @@ -151,7 +151,7 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool) end errcount += 1 lasterr = scrub_repl_backtrace(current_exceptions()) - ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, lasterr) + setglobal!(Main, :err, lasterr) if errcount > 2 @error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount break diff --git a/doc/src/manual/embedding.md b/doc/src/manual/embedding.md index 22c2f66f9b8b0..a87bf5fa0aaa7 100644 --- a/doc/src/manual/embedding.md +++ b/doc/src/manual/embedding.md @@ -371,7 +371,8 @@ As an alternative for very simple cases, it is possible to just create a global per pointer using ```c -jl_set_global(jl_main_module, jl_symbol("var"), var); +jl_binding_t *bp = jl_get_binding_wr(jl_main_module, jl_symbol("var"), 1); +jl_checked_assignment(bp, val); ``` ### Updating fields of GC-managed objects diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 46adef8444aa9..c820751ab56e2 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -56,6 +56,7 @@ DECLARE_BUILTIN(typeof); DECLARE_BUILTIN(_typevar); DECLARE_BUILTIN(donotdelete); DECLARE_BUILTIN(getglobal); +DECLARE_BUILTIN(setglobal); JL_CALLABLE(jl_f_invoke_kwsorter); #ifdef DEFINE_BUILTIN_GLOBALS diff --git a/src/builtins.c b/src/builtins.c index f8a78485551ad..30840f4edaf5c 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1203,7 +1203,7 @@ JL_CALLABLE(jl_f_setglobal) if (order == jl_memory_order_notatomic) jl_atomic_error("setglobal!: module binding cannot be written non-atomically"); // is seq_cst already, no fence needed - jl_binding_t *b = jl_get_binding_wr((jl_module_t*)args[0], (jl_sym_t*)args[1], 1); + jl_binding_t *b = jl_get_binding_wr_or_error((jl_module_t*)args[0], (jl_sym_t*)args[1]); jl_checked_assignment(b, args[2]); return args[2]; } @@ -1218,7 +1218,7 @@ JL_CALLABLE(jl_f_get_binding_type) jl_value_t *ty = jl_binding_type(mod, sym); if (ty == (jl_value_t*)jl_nothing) { jl_binding_t *b = jl_get_binding_wr(mod, sym, 0); - if (b) { + if (b && b->owner == mod) { 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); @@ -1920,7 +1920,7 @@ void jl_init_primitives(void) JL_GC_DISABLED // module bindings jl_builtin_getglobal = add_builtin_func("getglobal", jl_f_getglobal); - add_builtin_func("setglobal!", jl_f_setglobal); + jl_builtin_setglobal = add_builtin_func("setglobal!", jl_f_setglobal); add_builtin_func("get_binding_type", jl_f_get_binding_type); add_builtin_func("set_binding_type!", jl_f_set_binding_type); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index cc095459113c0..b48f6df40a01a 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -474,25 +474,22 @@ static Value *maybe_bitcast(jl_codectx_t &ctx, Value *V, Type *to) { return V; } -static Value *julia_binding_gv(jl_codectx_t &ctx, Value *bv) +static Value *julia_binding_pvalue(jl_codectx_t &ctx, Value *bv) { + bv = emit_bitcast(ctx, bv, ctx.types().T_pprjlvalue); Value *offset = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_binding_t, value) / sizeof(size_t)); return ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, bv, offset); } static Value *julia_binding_gv(jl_codectx_t &ctx, jl_binding_t *b) { - // emit a literal_pointer_val to the value field of a jl_binding_t + // emit a literal_pointer_val to a jl_binding_t // binding->value are prefixed with * - Value *bv; if (imaging_mode) - bv = emit_bitcast(ctx, - tbaa_decorate(ctx.tbaa().tbaa_const, - ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, julia_pgv(ctx, "*", b->name, b->owner, b), Align(sizeof(void*)))), - ctx.types().T_pprjlvalue); + return tbaa_decorate(ctx.tbaa().tbaa_const, ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, + julia_pgv(ctx, "*", b->name, b->owner, b), Align(sizeof(void*)))); else - bv = ConstantExpr::getBitCast(literal_static_pointer_val(b, ctx.types().T_pjlvalue), ctx.types().T_pprjlvalue); - return julia_binding_gv(ctx, bv); + return literal_static_pointer_val(b, ctx.types().T_pjlvalue); } // --- mapping between julia and llvm types --- diff --git a/src/codegen.cpp b/src/codegen.cpp index ca51b8dcaf5d2..e783a9df3f460 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -651,6 +651,15 @@ static const auto jlgetbindingorerror_func = new JuliaFunction{ }, nullptr, }; +static const auto jlgetbindingwrorerror_func = new JuliaFunction{ + XSTR(jl_get_binding_wr_or_error), + [](LLVMContext &C) { + auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); + return FunctionType::get(T_pjlvalue, + {T_pjlvalue, T_pjlvalue}, false); + }, + nullptr, +}; static const auto jlboundp_func = new JuliaFunction{ XSTR(jl_boundp), [](LLVMContext &C) { @@ -2387,16 +2396,19 @@ static void jl_add_method_root(jl_codectx_t &ctx, jl_value_t *val) // --- generating function calls --- -static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *name) +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); + if (bp == NULL) + return jl_cgval_t(ctx.builder.getContext()); + bp = julia_binding_pvalue(ctx, bp); if (bnd && bnd->value != NULL) { if (bnd->constp) { return mark_julia_const(ctx, bnd->value); } LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*))); - v->setOrdering(AtomicOrdering::Unordered); + v->setOrdering(order); tbaa_decorate(ctx.tbaa().tbaa_binding, v); return mark_julia_type(ctx, v, true, bnd->ty); } @@ -2404,6 +2416,20 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * return emit_checked_var(ctx, bp, name, false, ctx.tbaa().tbaa_binding); } +static void emit_globalset(jl_codectx_t &ctx, jl_binding_t *bnd, Value *bp, const jl_cgval_t &rval_info, AtomicOrdering Order) +{ + Value *rval = boxed(ctx, rval_info); + if (bnd && !bnd->constp && bnd->ty && jl_subtype(rval_info.typ, bnd->ty)) { + StoreInst *v = ctx.builder.CreateAlignedStore(rval, julia_binding_pvalue(ctx, bp), Align(sizeof(void*))); + v->setOrdering(Order); + tbaa_decorate(ctx.tbaa().tbaa_binding, v); + emit_write_barrier_binding(ctx, bp, rval); + } + else { + ctx.builder.CreateCall(prepare_call(jlcheckassign_func), { bp, mark_callee_rooted(ctx, rval) }); + } +} + static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2, Value *nullcheck1, Value *nullcheck2) { @@ -2668,6 +2694,49 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva return emit_box_compare(ctx, arg1, arg2, nullcheck1, nullcheck2); } +static bool emit_f_opglobal(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, + const jl_cgval_t *argv, size_t nargs, const jl_cgval_t *modifyop) +{ + const jl_cgval_t &mod = argv[1]; + const jl_cgval_t &sym = argv[2]; + const jl_cgval_t &val = argv[3]; + enum jl_memory_order order = jl_memory_order_unspecified; + + 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, false, true); + else + return false; + } + else + order = jl_memory_order_monotonic; + + if (order == jl_memory_order_invalid || order == jl_memory_order_notatomic) { + emit_atomic_error(ctx, order == jl_memory_order_invalid ? "invalid atomic ordering" : "setglobal!: module binding cannot be written non-atomically"); + *ret = jl_cgval_t(ctx.builder.getContext()); // unreachable + return true; + } + + if (sym.constant && jl_is_symbol(sym.constant)) { + jl_sym_t *name = (jl_sym_t*)sym.constant; + if (mod.constant && jl_is_module(mod.constant)) { + jl_binding_t *bnd = NULL; + Value *bp = global_binding_pointer(ctx, (jl_module_t*)mod.constant, name, &bnd, true); + if (bp) { + emit_globalset(ctx, bnd, bp, val, get_llvm_atomic_order(order)); + *ret = val; + } + else { + *ret = jl_cgval_t(ctx.builder.getContext()); // unreachable + } + return true; + } + } + + return false; +} + static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, const jl_cgval_t *argv, size_t nargs, const jl_cgval_t *modifyop) { @@ -2706,7 +2775,7 @@ static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, jl_datatype_t *uty = (jl_datatype_t*)jl_unwrap_unionall(obj.typ); if (jl_is_datatype(uty) && jl_struct_try_layout(uty)) { ssize_t idx = -1; - if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { + if (fld.constant && jl_is_symbol(fld.constant)) { idx = jl_field_index(uty, (jl_sym_t*)fld.constant, 0); } else if (fld.constant && fld.typ == (jl_value_t*)jl_long_type) { @@ -3138,7 +3207,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (nargs == 3) { const jl_cgval_t &arg3 = argv[3]; - if (arg3.typ == (jl_value_t*)jl_symbol_type && arg3.constant) + if (arg3.constant && jl_is_symbol(arg3.constant)) order = jl_get_atomic_order((jl_sym_t*)arg3.constant, true, false); else if (arg3.constant == jl_false) boundscheck = jl_false; @@ -3155,10 +3224,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_type_type((jl_value_t*)utt) && jl_is_concrete_type(jl_tparam0(utt))) utt = (jl_datatype_t*)jl_typeof(jl_tparam0(utt)); - if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { + if (fld.constant && jl_is_symbol(fld.constant)) { jl_sym_t *name = (jl_sym_t*)fld.constant; if (obj.constant && jl_is_module(obj.constant)) { - *ret = emit_globalref(ctx, (jl_module_t*)obj.constant, name); + *ret = emit_globalref(ctx, (jl_module_t*)obj.constant, name, order == jl_memory_order_unspecified ? AtomicOrdering::Unordered : get_llvm_atomic_order(order)); return true; } @@ -3257,7 +3326,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (nargs == 3) { const jl_cgval_t &arg3 = argv[3]; - if (arg3.typ == (jl_value_t*)jl_symbol_type && arg3.constant) + if (arg3.constant && jl_is_symbol(arg3.constant)) order = jl_get_atomic_order((jl_sym_t*)arg3.constant, true, false); else return false; @@ -3265,16 +3334,16 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else order = jl_memory_order_monotonic; - if (order == jl_memory_order_invalid) { - emit_atomic_error(ctx, "invalid atomic ordering"); + if (order == jl_memory_order_invalid || order == jl_memory_order_notatomic) { + emit_atomic_error(ctx, order == jl_memory_order_invalid ? "invalid atomic ordering" : "getglobal: module binding cannot be read non-atomically"); *ret = jl_cgval_t(ctx.builder.getContext()); // unreachable return true; } - if (sym.constant && sym.typ == (jl_value_t*)jl_symbol_type) { + if (sym.constant && jl_is_symbol(sym.constant)) { jl_sym_t *name = (jl_sym_t*)sym.constant; if (mod.constant && jl_is_module(mod.constant)) { - *ret = emit_globalref(ctx, (jl_module_t*)mod.constant, name); + *ret = emit_globalref(ctx, (jl_module_t*)mod.constant, name, get_llvm_atomic_order(order)); return true; } } @@ -3282,6 +3351,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return false; } + else if (f == jl_builtin_setglobal && (nargs == 3 || nargs == 4)) { + return emit_f_opglobal(ctx, ret, f, argv, nargs, nullptr); + } + else if ((f == jl_builtin_setfield && (nargs == 3 || nargs == 4)) || (f == jl_builtin_swapfield && (nargs == 3 || nargs == 4)) || (f == jl_builtin_replacefield && (nargs == 4 || nargs == 5 || nargs == 6)) || @@ -3425,7 +3498,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, assert(jl_is_datatype(stt)); ssize_t fieldidx = -1; - if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { + if (fld.constant && jl_is_symbol(fld.constant)) { jl_sym_t *sym = (jl_sym_t*)fld.constant; fieldidx = jl_field_index(stt, sym, 0); } @@ -3901,49 +3974,50 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t jl_binding_t **pbnd, bool assign) { jl_binding_t *b = NULL; - if (assign) { + if (assign) b = jl_get_binding_wr(m, s, 0); - assert(b != NULL); + else + b = jl_get_binding(m, s); + if (b == NULL) { + // 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); + LoadInst *cachedval = ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, bindinggv, Align(sizeof(void*))); + 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(); + ctx.builder.CreateCondBr(ctx.builder.CreateICmpNE(cachedval, initnul), have_val, not_found); + ctx.f->getBasicBlockList().push_back(not_found); + ctx.builder.SetInsertPoint(not_found); + Value *bval = ctx.builder.CreateCall(prepare_call(assign ? jlgetbindingwrorerror_func : jlgetbindingorerror_func), + { literal_pointer_val(ctx, (jl_value_t*)m), + literal_pointer_val(ctx, (jl_value_t*)s) }); + ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); + ctx.builder.CreateBr(have_val); + ctx.f->getBasicBlockList().push_back(have_val); + ctx.builder.SetInsertPoint(have_val); + PHINode *p = ctx.builder.CreatePHI(ctx.types().T_pjlvalue, 2); + p->addIncoming(cachedval, currentbb); + p->addIncoming(bval, not_found); + return p; + } + if (assign) { if (b->owner != m) { char *msg; (void)asprintf(&msg, "cannot assign a value to imported variable %s.%s from module %s", jl_symbol_name(b->owner->name), jl_symbol_name(s), jl_symbol_name(m->name)); emit_error(ctx, msg); free(msg); + return NULL; } } else { - b = jl_get_binding(m, s); - if (b == NULL) { - // 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); - LoadInst *cachedval = ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, bindinggv, Align(sizeof(void*))); - 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(); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpNE(cachedval, initnul), have_val, not_found); - ctx.f->getBasicBlockList().push_back(not_found); - ctx.builder.SetInsertPoint(not_found); - Value *bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s) }); - ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); - ctx.builder.CreateBr(have_val); - ctx.f->getBasicBlockList().push_back(have_val); - ctx.builder.SetInsertPoint(have_val); - PHINode *p = ctx.builder.CreatePHI(ctx.types().T_pjlvalue, 2); - p->addIncoming(cachedval, currentbb); - p->addIncoming(bval, not_found); - return julia_binding_gv(ctx, emit_bitcast(ctx, p, ctx.types().T_pprjlvalue)); - } if (b->deprecated) cg_bdw(ctx, b); } - if (pbnd) - *pbnd = b; + *pbnd = b; return julia_binding_gv(ctx, b); } @@ -3988,7 +4062,8 @@ static jl_cgval_t emit_global(jl_codectx_t &ctx, jl_sym_t *sym) { jl_binding_t *jbp = NULL; Value *bp = global_binding_pointer(ctx, ctx.module, sym, &jbp, false); - assert(bp != NULL); + if (bp == NULL) + return jl_cgval_t(ctx.builder.getContext()); if (jbp && jbp->value != NULL) { if (jbp->constp) return mark_julia_const(ctx, jbp->value); @@ -4067,6 +4142,7 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) if (bnd->value != NULL) 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*))); tbaa_decorate(ctx.tbaa().tbaa_binding, v); v->setOrdering(AtomicOrdering::Unordered); @@ -4409,50 +4485,31 @@ static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t } } -static void emit_binding_store(jl_codectx_t &ctx, jl_binding_t *bnd, Value *bp, jl_value_t *r, ssize_t ssaval, AtomicOrdering Order) -{ - assert(bnd); - jl_cgval_t rval_info = emit_expr(ctx, r, ssaval); - Value *rval = boxed(ctx, rval_info); - if (!bnd->constp && bnd->ty && jl_subtype(rval_info.typ, bnd->ty)) { - StoreInst *v = ctx.builder.CreateAlignedStore(rval, bp, Align(sizeof(void*))); - v->setOrdering(Order); - tbaa_decorate(ctx.tbaa().tbaa_binding, v); - emit_write_barrier_binding(ctx, literal_pointer_val(ctx, bnd), rval); - } - else { - ctx.builder.CreateCall(prepare_call(jlcheckassign_func), - { literal_pointer_val(ctx, bnd), - mark_callee_rooted(ctx, rval) }); - } -} - static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssize_t ssaval) { assert(!jl_is_ssavalue(l)); + jl_cgval_t rval_info = emit_expr(ctx, r, ssaval); - jl_sym_t *s = NULL; jl_binding_t *bnd = NULL; Value *bp = NULL; if (jl_is_symbol(l)) - s = (jl_sym_t*)l; + bp = global_binding_pointer(ctx, ctx.module, (jl_sym_t*)l, &bnd, true); // now bp != NULL or bnd != NULL else if (jl_is_globalref(l)) - bp = global_binding_pointer(ctx, jl_globalref_mod(l), jl_globalref_name(l), &bnd, true); // now bp != NULL + bp = global_binding_pointer(ctx, jl_globalref_mod(l), jl_globalref_name(l), &bnd, true); // now bp != NULL or bnd != NULL else assert(jl_is_slot(l)); - if (bp == NULL && s != NULL) - bp = global_binding_pointer(ctx, ctx.module, s, &bnd, true); - if (bp != NULL) { // it's a global - emit_binding_store(ctx, bnd, bp, r, ssaval, AtomicOrdering::Unordered); - // Global variable. Does not need debug info because the debugger knows about - // its memory location. + if (bp != NULL || bnd != NULL) { // it is a global + if (bp != NULL) { + emit_globalset(ctx, bnd, bp, rval_info, AtomicOrdering::Unordered); + // Global variable. Does not need debug info because the debugger knows about + // its memory location. + } return; } int sl = jl_slot_number(l) - 1; // it's a local variable jl_varinfo_t &vi = ctx.slots[sl]; - jl_cgval_t rval_info = emit_expr(ctx, r, ssaval); emit_varinfo_assign(ctx, vi, rval_info, l); } @@ -4686,7 +4743,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } } if (jl_is_globalref(expr)) { - return emit_globalref(ctx, jl_globalref_mod(expr), jl_globalref_name(expr)); + return emit_globalref(ctx, jl_globalref_mod(expr), jl_globalref_name(expr), AtomicOrdering::Unordered); } if (jl_is_linenode(expr)) { jl_error("LineNumberNode in value position"); @@ -4832,6 +4889,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return ghostValue(ctx, jl_nothing_type); } bp = julia_binding_gv(ctx, bnd); + bp = julia_binding_pvalue(ctx, bp); bp_owner = literal_pointer_val(ctx, (jl_value_t*)mod); } else if (jl_is_slot(mn) || jl_is_argument(mn)) { @@ -4879,9 +4937,9 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } if (jl_is_symbol(sym)) { jl_binding_t *bnd = NULL; - (void)global_binding_pointer(ctx, mod, sym, &bnd, true); assert(bnd); - ctx.builder.CreateCall(prepare_call(jldeclareconst_func), - literal_pointer_val(ctx, bnd)); + Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true); + if (bp) + ctx.builder.CreateCall(prepare_call(jldeclareconst_func), bp); } } else if (head == jl_new_sym) { @@ -8093,6 +8151,7 @@ static void init_jit_functions(void) 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_or_error); add_named_global(jlboundp_func, &jl_boundp); for (auto it : builtin_func_map) add_named_global(it.second, it.first); diff --git a/src/dump.c b/src/dump.c index f32461e6f7916..ef8db36411549 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2535,8 +2535,8 @@ static void jl_reinit_item(jl_value_t *v, int how, arraylist_t *tracee_list) jl_module_t *mod = (jl_module_t*)v; if (mod->parent == mod) // top level modules handled by loader break; - jl_binding_t *b = jl_get_binding_wr(mod->parent, mod->name, 1); - jl_declare_constant(b); // this can throw + jl_binding_t *b = jl_get_binding_wr(mod->parent, mod->name, 1); // this can throw + jl_declare_constant(b); // this can also throw if (b->value != NULL) { if (!jl_is_module(b->value)) { jl_errorf("Invalid redefinition of constant %s.", diff --git a/src/interpreter.c b/src/interpreter.c index 26038b4cfef35..60bd4a6e1ce7e 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -483,7 +483,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, sym = (jl_sym_t*)lhs; } JL_GC_PUSH1(&rhs); - jl_binding_t *b = jl_get_binding_wr(modu, sym, 1); + jl_binding_t *b = jl_get_binding_wr_or_error(modu, sym); jl_checked_assignment(b, rhs); JL_GC_POP(); } diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index ac004cda653b7..e2f8679ebbd57 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -207,6 +207,7 @@ XX(jl_get_binding) \ XX(jl_get_binding_for_method_def) \ XX(jl_get_binding_or_error) \ + XX(jl_get_binding_wr_or_error) \ XX(jl_get_binding_wr) \ XX(jl_get_cpu_name) \ XX(jl_get_current_task) \ @@ -411,7 +412,6 @@ XX(jl_set_ARGS) \ XX(jl_set_const) \ XX(jl_set_errno) \ - XX(jl_set_global) \ XX(jl_set_istopmod) \ XX(jl_set_module_compile) \ XX(jl_set_module_infer) \ diff --git a/src/julia.h b/src/julia.h index 4b5948583bf9c..702175d2cc86d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1601,16 +1601,16 @@ 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_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 error); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr_or_error(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); JL_DLLEXPORT int jl_defines_or_exports_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_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); -JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT); JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT); -JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_value_t *rhs JL_ROOTED_ARGUMENT); +JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs JL_MAYBE_UNROOTED); JL_DLLEXPORT void jl_declare_constant(jl_binding_t *b); 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); diff --git a/src/module.c b/src/module.c index 249d0d548cd43..b3e4b0584f024 100644 --- a/src/module.c +++ b/src/module.c @@ -172,7 +172,7 @@ static jl_binding_t *new_binding(jl_sym_t *name) } // 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 error) +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc) { JL_LOCK(&m->lock); jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&m->bindings, var); @@ -183,20 +183,23 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, if (b->owner == NULL) { b->owner = m; } - else if (error) { + else if (alloc) { JL_UNLOCK(&m->lock); jl_errorf("cannot assign a value to imported variable %s.%s from module %s", jl_symbol_name(b->owner->name), jl_symbol_name(var), jl_symbol_name(m->name)); } } } - else { + else if (alloc) { b = new_binding(var); b->owner = m; *bp = b; JL_GC_PROMISE_ROOTED(b); jl_gc_wb_buf(m, b, sizeof(jl_binding_t)); } + else { + b = NULL; + } JL_UNLOCK(&m->lock); return b; @@ -207,16 +210,11 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, // NOTE: Must hold m->lock while calling these. #ifdef __clang_gcanalyzer__ jl_binding_t *_jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT; -jl_binding_t **_jl_get_module_binding_bp(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT; #else static inline jl_binding_t *_jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT { return (jl_binding_t*)ptrhash_get(&m->bindings, var); } -static inline jl_binding_t **_jl_get_module_binding_bp(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT -{ - return (jl_binding_t**)ptrhash_bp(&m->bindings, var); -} #endif @@ -234,8 +232,9 @@ JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var) { JL_LOCK(&m->lock); - jl_binding_t **bp = _jl_get_module_binding_bp(m, var); + jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&m->bindings, var); jl_binding_t *b = *bp; + JL_GC_PROMISE_ROOTED(b); if (b != HT_NOTFOUND) { if (b->owner != m) { @@ -261,6 +260,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ b = new_binding(var); b->owner = m; *bp = b; + JL_GC_PROMISE_ROOTED(b); jl_gc_wb_buf(m, b, sizeof(jl_binding_t)); } @@ -310,14 +310,14 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl !tempb->deprecated && !b->deprecated && !(tempb->constp && tempb->value && b->constp && b->value == tempb->value)) { if (warn) { + // mark this binding resolved (by creating it or setting the owner), to avoid repeating the warning + (void)jl_get_binding_wr(m, var, 1); JL_UNLOCK(&m->lock); 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)); - // mark this binding resolved, to avoid repeating the warning - (void)jl_get_binding_wr(m, var, 0); JL_LOCK(&m->lock); } return NULL; @@ -390,6 +390,11 @@ JL_DLLEXPORT jl_value_t *jl_binding_type(jl_module_t *m, jl_sym_t *var) return ty ? ty : jl_nothing; } +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr_or_error(jl_module_t *m, jl_sym_t *var) +{ + return jl_get_binding_wr(m, var, 1); +} + JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) { return jl_get_binding_(m, var, NULL); @@ -665,14 +670,6 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) return b->value; } -JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) -{ - JL_TYPECHK(jl_set_global, module, (jl_value_t*)m); - JL_TYPECHK(jl_set_global, symbol, (jl_value_t*)var); - jl_binding_t *bp = jl_get_binding_wr(m, var, 1); - jl_checked_assignment(bp, val); -} - JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) { jl_binding_t *bp = jl_get_binding_wr(m, var, 1); @@ -686,7 +683,7 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var return; } } - jl_value_t *old_ty = NULL; + jl_value_t *old_ty = NULL; jl_atomic_cmpswap_relaxed(&bp->ty, &old_ty, (jl_value_t*)jl_any_type); } jl_errorf("invalid redefinition of constant %s", @@ -807,9 +804,14 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_binding_t *b) JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs) { jl_value_t *old_ty = NULL; - if (!jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type) && !jl_isa(rhs, old_ty)) { - jl_errorf("cannot assign an incompatible value to the global %s.", - jl_symbol_name(b->name)); + 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); + if (!jl_isa(rhs, old_ty)) + jl_errorf("cannot assign an incompatible value to the global %s.", + jl_symbol_name(b->name)); + JL_GC_POP(); + } } if (b->constp) { jl_value_t *old = NULL; diff --git a/src/staticdata.c b/src/staticdata.c index f697bc88e313d..4eeef024139c5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -79,7 +79,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 154 +#define NUM_TAGS 155 // 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. @@ -253,6 +253,8 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_builtin__typebody); INSERT_TAG(jl_builtin_donotdelete); INSERT_TAG(jl_builtin_getglobal); + INSERT_TAG(jl_builtin_setglobal); + // n.b. must update NUM_TAGS when you add something here // All optional tags must be placed at the end, so that we // don't accidentally have a `NULL` in the middle diff --git a/src/toplevel.c b/src/toplevel.c index b9b2bb7535707..54a657df05db6 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -298,7 +298,7 @@ static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f } void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) { - // create uninitialized mutable binding for "global x" decl + // create uninitialized mutable binding for "global x" decl sometimes or probably size_t i, l = jl_array_len(ex->args); for (i = 0; i < l; i++) { jl_value_t *arg = jl_exprarg(ex, i); @@ -313,10 +313,13 @@ void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) { gm = m; gs = (jl_sym_t*)arg; } - jl_binding_t *b = jl_get_binding_wr(gm, gs, 0); - if (set_type && b) { - jl_value_t *old_ty = NULL; - jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type); + if (!jl_binding_resolved_p(gm, gs)) { + jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); + if (set_type) { + jl_value_t *old_ty = NULL; + // maybe set the type too, perhaps + jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, (jl_value_t*)jl_any_type); + } } } } @@ -589,7 +592,7 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym if (jl_binding_resolved_p(m, name)) { b = jl_get_binding(m, name); if ((!b->constp && b->owner != m) || (b->value && b->value != (jl_value_t*)import)) { - jl_errorf("importing %s into %s conflicts with an existing identifier", + jl_errorf("importing %s into %s conflicts with an existing global", jl_symbol_name(name), jl_symbol_name(m->name)); } } diff --git a/stdlib/Distributed/src/clusterserialize.jl b/stdlib/Distributed/src/clusterserialize.jl index 28025ae867c78..0acd4ce68c45b 100644 --- a/stdlib/Distributed/src/clusterserialize.jl +++ b/stdlib/Distributed/src/clusterserialize.jl @@ -170,7 +170,7 @@ function deserialize_global_from_main(s::ClusterSerializer, sym) if sym_isconst ccall(:jl_set_const, Cvoid, (Any, Any, Any), Main, sym, v) else - ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, sym, v) + setglobal!(Main, sym, v) end return nothing end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 3308760046d4e..8e15bed447682 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -150,8 +150,7 @@ function eval_user_input(@nospecialize(ast), backend::REPLBackend) end value = Core.eval(Main, ast) backend.in_eval = false - # note: use jl_set_global to make sure value isn't passed through `expand` - ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value) + setglobal!(Main, :ans, value) put!(backend.response_channel, Pair{Any, Bool}(value, false)) end break @@ -287,7 +286,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, Base.sigatomic_end() if iserr val = Base.scrub_repl_backtrace(val) - Base.istrivialerror(val) || ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, val) + Base.istrivialerror(val) || setglobal!(Main, :err, val) Base.invokelatest(Base.display_error, errio, val) else if val !== nothing && show_value @@ -304,13 +303,13 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, end end break - catch + catch ex if iserr println(errio) # an error during printing is likely to leave us mid-line println(errio, "SYSTEM (REPL): showing an error caused an error") try excs = Base.scrub_repl_backtrace(current_exceptions()) - ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, excs) + setglobal!(Main, :err, excs) Base.invokelatest(Base.display_error, errio, excs) catch e # at this point, only print the name of the type as a Symbol to diff --git a/test/misc.jl b/test/misc.jl index f89c4daa4840a..9ea610ad659f0 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1017,10 +1017,11 @@ end @testset "exports of modules" begin for (_, mod) in Base.loaded_modules - for v in names(mod) - @test isdefined(mod, v) - end - end + mod === Main && continue # Main exports everything + for v in names(mod) + @test isdefined(mod, v) + end + end end @testset "ordering UUIDs" begin