diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a3981a8796dd7..283b76bde0631 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -541,6 +541,13 @@ static Value *literal_pointer_val(jl_binding_t *p) return julia_gv("jl_bnd#", p->name, p->owner, p); } +static Value *julia_binding_gv(Value *bv) +{ + return builder. + CreateGEP(bv,ConstantInt::get(T_size, + offsetof(jl_binding_t,value)/sizeof(size_t))); +} + static Value *julia_binding_gv(jl_binding_t *b) { // emit a literal_pointer_val to the value field of a jl_binding_t @@ -548,8 +555,7 @@ static Value *julia_binding_gv(jl_binding_t *b) Value *bv = imaging_mode ? builder.CreateBitCast(julia_gv("*", b->name, b->owner, b), jl_ppvalue_llvmt) : literal_static_pointer_val(b,jl_ppvalue_llvmt); - return builder.CreateGEP(bv,ConstantInt::get(T_size, - offsetof(jl_binding_t,value)/sizeof(size_t))); + return julia_binding_gv(bv); } // --- mapping between julia and llvm types --- diff --git a/src/codegen.cpp b/src/codegen.cpp index b9845f6c1525b..9a9b6947b837e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -287,6 +287,7 @@ static Function *jlvboundserror_func; static Function *jlboundserrorv_func; static Function *jlcheckassign_func; static Function *jldeclareconst_func; +static Function *jlgetbindingorerror_func; static Function *jltopeval_func; static Function *jlcopyast_func; static Function *jltuple_func; @@ -581,7 +582,7 @@ static int is_global(jl_sym_t *s, jl_codectx_t *ctx); static Value *make_gcroot(Value *v, jl_codectx_t *ctx, jl_sym_t *var = NULL); static Value *emit_boxed_rooted(jl_value_t *e, jl_codectx_t *ctx); static Value *global_binding_pointer(jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign); + jl_binding_t **pbnd, bool assign, jl_codectx_t *ctx); static Value *emit_checked_var(Value *bp, jl_sym_t *name, jl_codectx_t *ctx, bool isvol=false); static bool might_need_root(jl_value_t *ex); static Value *emit_condition(jl_value_t *cond, const std::string &msg, jl_codectx_t *ctx); @@ -1817,7 +1818,7 @@ static Value *emit_getfield(jl_value_t *expr, jl_sym_t *name, jl_codectx_t *ctx) if (jl_is_module(expr)) { Value *bp = - global_binding_pointer((jl_module_t*)expr, name, NULL, false); + global_binding_pointer((jl_module_t*)expr, name, NULL, false, ctx); // todo: use type info to avoid undef check return emit_checked_var(bp, name, ctx); } @@ -2707,16 +2708,56 @@ static int is_global(jl_sym_t *s, jl_codectx_t *ctx) return (it == ctx->vars.end()); } +static void undef_var_error_if_null(Value *v, jl_sym_t *name, jl_codectx_t *ctx) +{ + Value *ok = builder.CreateICmpNE(v, V_null); + BasicBlock *err = BasicBlock::Create(getGlobalContext(), "err", ctx->f); + BasicBlock *ifok = BasicBlock::Create(getGlobalContext(), "ok"); + builder.CreateCondBr(ok, ifok, err); + builder.SetInsertPoint(err); + builder.CreateCall(prepare_call(jlundefvarerror_func), literal_pointer_val((jl_value_t*)name)); + builder.CreateUnreachable(); + ctx->f->getBasicBlockList().push_back(ifok); + builder.SetInsertPoint(ifok); +} + static Value *global_binding_pointer(jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign) + jl_binding_t **pbnd, bool assign, jl_codectx_t *ctx) { jl_binding_t *b=NULL; - if (!assign) - b = jl_get_binding(m, s); - // if b is NULL, this might be a global that is not set yet but will be, - // so get a pointer for writing even when not assigning. - if (assign || b==NULL) + if (assign) { b = jl_get_binding_wr(m, s); + assert(b != NULL); + } + else { + b = jl_get_binding(m, s); + if (b == NULL) { + // var not found. switch to delayed lookup. + Constant *initnul = ConstantPointerNull::get((PointerType*)jl_pvalue_llvmt); + GlobalVariable *bindinggv = + new GlobalVariable(*jl_Module, jl_pvalue_llvmt, + false, GlobalVariable::PrivateLinkage, + initnul, "delayedvar"); + Value *cachedval = builder.CreateLoad(bindinggv); + BasicBlock *have_val = BasicBlock::Create(jl_LLVMContext, "found"), + *not_found = BasicBlock::Create(jl_LLVMContext, "notfound"); + BasicBlock *currentbb = builder.GetInsertBlock(); + builder.CreateCondBr(builder.CreateICmpNE(cachedval, initnul), have_val, not_found); + ctx->f->getBasicBlockList().push_back(not_found); + builder.SetInsertPoint(not_found); + Value *bval = builder.CreateCall2(prepare_call(jlgetbindingorerror_func), + literal_pointer_val((jl_value_t*)m), + literal_pointer_val((jl_value_t*)s)); + builder.CreateStore(bval, bindinggv); + builder.CreateBr(have_val); + ctx->f->getBasicBlockList().push_back(have_val); + builder.SetInsertPoint(have_val); + PHINode *p = builder.CreatePHI(jl_pvalue_llvmt, 2); + p->addIncoming(cachedval, currentbb); + p->addIncoming(bval, not_found); + return julia_binding_gv(builder.CreateBitCast(p,jl_ppvalue_llvmt)); + } + } if (pbnd) *pbnd = b; return julia_binding_gv(b); } @@ -2737,7 +2778,7 @@ static Value *var_binding_pointer(jl_sym_t *s, jl_binding_t **pbnd, s = jl_symbolnode_sym(s); assert(jl_is_symbol(s)); if (is_global(s, ctx)) { - return global_binding_pointer(ctx->module, s, pbnd, assign); + return global_binding_pointer(ctx->module, s, pbnd, assign, ctx); } jl_varinfo_t &vi = ctx->vars[s]; if (vi.closureidx != -1) { @@ -2770,17 +2811,8 @@ static Value *emit_checked_var(Value *bp, jl_sym_t *name, jl_codectx_t *ctx, boo // in unreachable code, there might be a poorly-typed instance of a variable // that has a concrete type everywhere it's actually used. tolerate this // situation by just skipping the NULL check if it wouldn't be valid. (issue #7836) - if (v->getType() == jl_pvalue_llvmt) { - Value *ok = builder.CreateICmpNE(v, V_null); - BasicBlock *err = BasicBlock::Create(getGlobalContext(), "err", ctx->f); - BasicBlock *ifok = BasicBlock::Create(getGlobalContext(), "ok"); - builder.CreateCondBr(ok, ifok, err); - builder.SetInsertPoint(err); - builder.CreateCall(prepare_call(jlundefvarerror_func), literal_pointer_val((jl_value_t*)name)); - builder.CreateUnreachable(); - ctx->f->getBasicBlockList().push_back(ifok); - builder.SetInsertPoint(ifok); - } + if (v->getType() == jl_pvalue_llvmt) + undef_var_error_if_null(v, name, ctx); return v; } @@ -2805,8 +2837,7 @@ static Value *emit_var(jl_sym_t *sym, jl_value_t *ty, jl_codectx_t *ctx, bool is Value *bp = var_binding_pointer(sym, &jbp, false, ctx); if (bp == NULL) return NULL; - assert(jbp != NULL); - if (jbp->value != NULL) { + if (jbp && jbp->value != NULL) { if (jbp->constp) { if (!isboxed && jl_isbits(jl_typeof(jbp->value))) return emit_unboxed(jbp->value, ctx); @@ -2924,7 +2955,7 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) else if (jl_is_symbolnode(l)) s = jl_symbolnode_sym(l); else if (jl_is_globalref(l)) - bp = global_binding_pointer(jl_globalref_mod(l), jl_globalref_name(l), &bnd, true); + bp = global_binding_pointer(jl_globalref_mod(l), jl_globalref_name(l), &bnd, true, ctx); else assert(false); if (bp == NULL) @@ -5167,6 +5198,12 @@ static void init_julia_llvm_env(Module *m) "jl_declare_constant", m); add_named_global(jldeclareconst_func, (void*)&jl_declare_constant); + jlgetbindingorerror_func = + Function::Create(FunctionType::get(jl_pvalue_llvmt, args_2ptrs, false), + Function::ExternalLinkage, + "jl_get_binding_or_error", m); + add_named_global(jlgetbindingorerror_func, (void*)&jl_get_binding_or_error); + builtin_func_map[jl_f_is] = jlcall_func_to_llvm("jl_f_is", (void*)&jl_f_is, m); builtin_func_map[jl_f_typeof] = jlcall_func_to_llvm("jl_f_typeof", (void*)&jl_f_typeof, m); builtin_func_map[jl_f_sizeof] = jlcall_func_to_llvm("jl_f_sizeof", (void*)&jl_f_sizeof, m); diff --git a/src/julia.h b/src/julia.h index ef99d104f3163..6468fc078622c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1076,9 +1076,10 @@ extern DLLEXPORT jl_module_t *jl_current_module; DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name); // get binding for reading DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var); +DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var); // get binding for assignment -jl_binding_t *jl_get_binding_wr(jl_module_t *m, jl_sym_t *var); -jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var); +DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m, jl_sym_t *var); +DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var); DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var); DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var); diff --git a/src/module.c b/src/module.c index fb8147763b64e..a86eb5a166cdc 100644 --- a/src/module.c +++ b/src/module.c @@ -79,7 +79,7 @@ static jl_binding_t *new_binding(jl_sym_t *name) } // get binding for assignment -jl_binding_t *jl_get_binding_wr(jl_module_t *m, jl_sym_t *var) +DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m, jl_sym_t *var) { jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&m->bindings, var); jl_binding_t *b; @@ -118,7 +118,7 @@ DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var) // get binding for adding a method // like jl_get_binding_wr, but uses existing imports instead of warning // and overwriting. -jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var) +DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var) { jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&m->bindings, var); jl_binding_t *b = *bp; @@ -207,11 +207,19 @@ static jl_binding_t *jl_get_binding_(jl_module_t *m, jl_sym_t *var, modstack_t * return b; } -jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) +DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) { return jl_get_binding_(m, var, NULL); } +DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var) +{ + jl_binding_t *b = jl_get_binding_(m, var, NULL); + if (b == NULL) + jl_undefined_var_error(var); + return b; +} + static int eq_bindings(jl_binding_t *a, jl_binding_t *b) { if (a==b) return 1; diff --git a/test/core.jl b/test/core.jl index 2d42566ff93a0..3b655267b6ed8 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2951,3 +2951,13 @@ let x = T11675{Union()}() end @test_throws UndefRefError f11675(x) end + +# issue #7864 +module M7864 +export x7864 +x7864 = 1 +end + +@test_throws UndefVarError x7864 +using M7864 +@test x7864 == 1