Skip to content

Commit

Permalink
fall back to run time lookup when compiled code needs to access a global
Browse files Browse the repository at this point in the history
that cannot be found yet.

fixes JuliaLang#7864
  • Loading branch information
JeffBezanson committed Jun 12, 2015
1 parent 3c9dd87 commit 19ae2ea
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 30 deletions.
10 changes: 8 additions & 2 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,15 +541,21 @@ 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
// binding->value are prefixed with *
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 ---
Expand Down
83 changes: 60 additions & 23 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 11 additions & 3 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 19ae2ea

Please sign in to comment.