Skip to content

Commit a5157c0

Browse files
authored
bpart: Allow inference/codegen to merge multiple partitions (#57602)
When inference and codegen queries the partitions of a binding, they often only care about a subset of the information in the partition. At the moment, we always truncate world ranges to the most precise partition that contains the relevant query. However, we can instead expand the world range to cover all partitions that are equivalent for the given query. To give a concrete example, both inference and codegen never care about the exported flag in a binding partition, so we should not unnecessarily truncate a world range just because an export was introduced. Further, this commit lays the ground work to stop invalidating code for these same kinds of transitions, although the actual logic to do that will come in a separate PR.
1 parent 75cb2f6 commit a5157c0

File tree

15 files changed

+351
-181
lines changed

15 files changed

+351
-181
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 142 additions & 77 deletions
Large diffs are not rendered by default.

Compiler/src/cicache.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ function intersect(a::WorldRange, b::WorldRange)
4040
return ret
4141
end
4242

43+
function union(a::WorldRange, b::WorldRange)
44+
if b.min_world < a.min_world
45+
(b, a) = (a, b)
46+
end
47+
@assert a.max_world >= b.min_world - 1
48+
return WorldRange(a.min_world, b.max_world)
49+
end
50+
4351
"""
4452
struct WorldView
4553

Compiler/src/ssair/verify.jl

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,14 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
6262
raise_error()
6363
end
6464
elseif isa(op, GlobalRef)
65-
bpart = lookup_binding_partition(min_world(ir.valid_worlds), op)
66-
while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world
67-
imported_binding = partition_restriction(bpart)::Core.Binding
68-
bpart = lookup_binding_partition(min_world(ir.valid_worlds), imported_binding)
69-
end
70-
if (!is_defined_const_binding(binding_kind(bpart)) || (bpart.max_world < max_world(ir.valid_worlds))) &&
71-
(op.mod !== Core) && (op.mod !== Base)
72-
# Core and Base are excluded because the frontend uses them for intrinsics, etc.
73-
# TODO: Decide which way to go with these.
74-
@verify_error "Unbound or partitioned GlobalRef not allowed in value position"
75-
raise_error()
65+
if op.mod !== Core && op.mod !== Base
66+
(valid_worlds, alldef) = scan_leaf_partitions(nothing, op, WorldWithRange(min_world(ir.valid_worlds), ir.valid_worlds)) do _, _, bpart
67+
is_defined_const_binding(binding_kind(bpart))
68+
end
69+
if !alldef || max_world(valid_worlds) < max_world(ir.valid_worlds) || min_world(valid_worlds) > min_world(ir.valid_worlds)
70+
@verify_error "Unbound or partitioned GlobalRef not allowed in value position"
71+
raise_error()
72+
end
7673
end
7774
elseif isa(op, Expr)
7875
# Only Expr(:boundscheck) is allowed in value position

Compiler/src/stmtinfo.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,7 @@ perform such accesses.
490490
"""
491491
struct GlobalAccessInfo <: CallInfo
492492
b::Core.Binding
493-
bpart::Core.BindingPartition
494493
end
495-
GlobalAccessInfo(::Core.Binding, ::Nothing) = NoCallInfo()
496494
function add_edges_impl(edges::Vector{Any}, info::GlobalAccessInfo)
497495
push!(edges, info.b)
498496
end

Compiler/test/effects.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,17 @@ let effects = Base.infer_effects(setglobal!_nothrow_undefinedyet2)
400400
end
401401
@test_throws TypeError setglobal!_nothrow_undefinedyet2()
402402

403+
module ExportMutableGlobal
404+
global mutable_global_for_setglobal_test::Int = 0
405+
export mutable_global_for_setglobal_test
406+
end
407+
using .ExportMutableGlobal: mutable_global_for_setglobal_test
408+
f_assign_imported() = global mutable_global_for_setglobal_test = 42
409+
let effects = Base.infer_effects(f_assign_imported)
410+
@test !Compiler.is_nothrow(effects)
411+
end
412+
@test_throws ErrorException f_assign_imported()
413+
403414
# Nothrow for setfield!
404415
mutable struct SetfieldNothrow
405416
x::Int

base/Base.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ include(strcat(BUILDROOT, "version_git.jl")) # include($BUILDROOT/base/version_g
2121

2222
# Initialize DL_LOAD_PATH as early as possible. We are defining things here in
2323
# a slightly more verbose fashion than usual, because we're running so early.
24-
const DL_LOAD_PATH = String[]
2524
let os = ccall(:jl_get_UNAME, Any, ())
2625
if os === :Darwin || os === :Apple
2726
if Base.DARWIN_FRAMEWORK

base/Base_compiler.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ end
334334

335335
BUILDROOT::String = ""
336336
DATAROOT::String = ""
337+
const DL_LOAD_PATH = String[]
337338

338339
baremodule BuildSettings end
339340

base/runtime_internals.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ function lookup_binding_partition(world::UInt, b::Core.Binding)
225225
ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world)
226226
end
227227

228+
function lookup_binding_partition(world::UInt, b::Core.Binding, previous_partition::Core.BindingPartition)
229+
ccall(:jl_get_binding_partition_with_hint, Ref{Core.BindingPartition}, (Any, Any, UInt), b, previous_partition, world)
230+
end
231+
228232
function convert(::Type{Core.Binding}, gr::Core.GlobalRef)
229233
if isdefined(gr, :binding)
230234
return gr.binding

src/codegen.cpp

Lines changed: 47 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,14 +2928,13 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29282928
{
29292929
if (jl_is_symbol(ex)) {
29302930
jl_sym_t *sym = (jl_sym_t*)ex;
2931-
jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0);
2932-
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2931+
jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 1);
29332932
int possibly_deprecated = 0;
2934-
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
2935-
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) {
2933+
jl_value_t *cval = jl_get_binding_leaf_partitions_value_if_const(bnd, &possibly_deprecated, ctx.min_world, ctx.max_world);
2934+
if (cval) {
29362935
if (possibly_deprecated)
29372936
emit_depwarn_check(ctx, bnd);
2938-
return bpart->restriction;
2937+
return cval;
29392938
}
29402939
return NULL;
29412940
}
@@ -2957,13 +2956,9 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29572956
jl_sym_t *s = NULL;
29582957
if (jl_is_globalref(ex)) {
29592958
s = jl_globalref_name(ex);
2960-
jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0);
2961-
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2959+
jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 1);
29622960
int possibly_deprecated = 0;
2963-
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
2964-
jl_value_t *v = NULL;
2965-
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
2966-
v = bpart->restriction;
2961+
jl_value_t *v = jl_get_binding_leaf_partitions_value_if_const(bnd, &possibly_deprecated, ctx.min_world, ctx.max_world);
29672962
if (v) {
29682963
if (possibly_deprecated)
29692964
emit_depwarn_check(ctx, bnd);
@@ -2985,13 +2980,9 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29852980
// Assumes that the module is rooted somewhere.
29862981
s = (jl_sym_t*)static_eval(ctx, jl_exprarg(e, 2));
29872982
if (s && jl_is_symbol(s)) {
2988-
jl_binding_t *bnd = jl_get_module_binding(m, s, 0);
2989-
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2983+
jl_binding_t *bnd = jl_get_module_binding(m, s, 1);
29902984
int possibly_deprecated = 0;
2991-
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
2992-
jl_value_t *v = NULL;
2993-
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
2994-
v = bpart->restriction;
2985+
jl_value_t *v = jl_get_binding_leaf_partitions_value_if_const(bnd, &possibly_deprecated, ctx.min_world, ctx.max_world);
29952986
if (v) {
29962987
if (possibly_deprecated)
29972988
emit_depwarn_check(ctx, bnd);
@@ -3235,48 +3226,32 @@ static jl_cgval_t emit_globalref_runtime(jl_codectx_t &ctx, jl_binding_t *bnd, j
32353226
static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *name, AtomicOrdering order)
32363227
{
32373228
jl_binding_t *bnd = jl_get_module_binding(mod, name, 1);
3238-
assert(bnd);
3239-
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
3240-
if (!bpart) {
3229+
struct restriction_kind_pair rkp = { NULL, NULL, BINDING_KIND_GUARD, 0 };
3230+
if (!jl_get_binding_leaf_partitions_restriction_kind(bnd, &rkp, ctx.min_world, ctx.max_world)) {
32413231
return emit_globalref_runtime(ctx, bnd, mod, name);
32423232
}
3243-
int possibly_deprecated = 0;
3244-
int saw_explicit = 0;
3245-
while (bpart) {
3246-
if (!saw_explicit && (bpart->kind & BINDING_FLAG_DEPWARN))
3247-
possibly_deprecated = 1;
3248-
enum jl_partition_kind kind = jl_binding_kind(bpart);
3249-
if (!jl_bkind_is_some_import(kind))
3250-
break;
3251-
if (kind != BINDING_KIND_IMPLICIT)
3252-
saw_explicit = 1;
3253-
bnd = (jl_binding_t*)bpart->restriction;
3254-
bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
3255-
}
3256-
Value *bp = NULL;
3257-
if (bpart) {
3258-
enum jl_partition_kind kind = jl_binding_kind(bpart);
3259-
if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) {
3260-
if (possibly_deprecated) {
3261-
bp = julia_binding_gv(ctx, bnd);
3262-
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
3263-
}
3264-
jl_value_t *constval = bpart->restriction;
3265-
if (!constval) {
3266-
undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod);
3267-
return jl_cgval_t();
3268-
}
3269-
return mark_julia_const(ctx, constval);
3233+
if (jl_bkind_is_some_constant(rkp.kind) && rkp.kind != BINDING_KIND_BACKDATED_CONST) {
3234+
if (rkp.maybe_depwarn) {
3235+
Value *bp = julia_binding_gv(ctx, bnd);
3236+
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
3237+
}
3238+
jl_value_t *constval = rkp.restriction;
3239+
if (!constval) {
3240+
undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod);
3241+
return jl_cgval_t();
32703242
}
3243+
return mark_julia_const(ctx, constval);
32713244
}
3272-
if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) {
3245+
if (rkp.kind != BINDING_KIND_GLOBAL) {
32733246
return emit_globalref_runtime(ctx, bnd, mod, name);
32743247
}
3275-
bp = julia_binding_gv(ctx, bnd);
3276-
if (possibly_deprecated) {
3248+
Value *bp = julia_binding_gv(ctx, bnd);
3249+
if (rkp.maybe_depwarn) {
32773250
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
32783251
}
3279-
jl_value_t *ty = bpart->restriction;
3252+
if (bnd != rkp.binding_if_global)
3253+
bp = julia_binding_gv(ctx, rkp.binding_if_global);
3254+
jl_value_t *ty = rkp.restriction;
32803255
Value *bpval = julia_binding_pvalue(ctx, bp);
32813256
if (ty == nullptr)
32823257
ty = (jl_value_t*)jl_any_type;
@@ -3844,27 +3819,27 @@ static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_
38443819
{
38453820
Value *isnull = NULL;
38463821
jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0);
3847-
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
3848-
enum jl_partition_kind kind = bpart ? jl_binding_kind(bpart) : BINDING_KIND_GUARD;
3849-
if (kind == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(kind)) {
3850-
if (jl_get_binding_value_if_const(bnd))
3851-
return mark_julia_const(ctx, jl_true);
3852-
Value *bp = julia_binding_gv(ctx, bnd);
3853-
bp = julia_binding_pvalue(ctx, bp);
3854-
LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)));
3855-
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_binding);
3856-
ai.decorateInst(v);
3857-
v->setOrdering(get_llvm_atomic_order(order));
3858-
isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue));
3859-
}
3860-
else {
3861-
Value *v = ctx.builder.CreateCall(prepare_call(jlboundp_func), {
3862-
literal_pointer_val(ctx, (jl_value_t*)modu),
3863-
literal_pointer_val(ctx, (jl_value_t*)name),
3864-
ConstantInt::get(getInt32Ty(ctx.builder.getContext()), allow_import)
3865-
});
3866-
isnull = ctx.builder.CreateICmpNE(v, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0));
3867-
}
3822+
struct restriction_kind_pair rkp = { NULL, NULL, BINDING_KIND_GUARD, 0 };
3823+
if (allow_import && jl_get_binding_leaf_partitions_restriction_kind(bnd, &rkp, ctx.min_world, ctx.max_world)) {
3824+
if (jl_bkind_is_some_constant(rkp.kind))
3825+
return mark_julia_const(ctx, rkp.restriction);
3826+
if (rkp.kind == BINDING_KIND_GLOBAL) {
3827+
Value *bp = julia_binding_gv(ctx, rkp.binding_if_global);
3828+
bp = julia_binding_pvalue(ctx, bp);
3829+
LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)));
3830+
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_binding);
3831+
ai.decorateInst(v);
3832+
v->setOrdering(get_llvm_atomic_order(order));
3833+
isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue));
3834+
return mark_julia_type(ctx, isnull, false, jl_bool_type);
3835+
}
3836+
}
3837+
Value *v = ctx.builder.CreateCall(prepare_call(jlboundp_func), {
3838+
literal_pointer_val(ctx, (jl_value_t*)modu),
3839+
literal_pointer_val(ctx, (jl_value_t*)name),
3840+
ConstantInt::get(getInt32Ty(ctx.builder.getContext()), allow_import)
3841+
});
3842+
isnull = ctx.builder.CreateICmpNE(v, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0));
38683843
return mark_julia_type(ctx, isnull, false, jl_bool_type);
38693844
}
38703845

src/dlload.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,9 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
318318
While these exist as OS concepts on Darwin, we want to use them on other platforms
319319
such as Windows, so we emulate them here.
320320
*/
321-
if (!abspath && !is_atpath && jl_base_module != NULL) {
321+
if (!abspath && !is_atpath && jl_base_module != NULL && jl_typeinf_world != 1) {
322322
jl_binding_t *b = jl_get_module_binding(jl_base_module, jl_symbol("DL_LOAD_PATH"), 0);
323-
jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_get_binding_value(b) : NULL);
323+
jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_get_binding_value_in_world(b, jl_typeinf_world) : NULL);
324324
if (DL_LOAD_PATH != NULL) {
325325
size_t j;
326326
for (j = 0; j < jl_array_nrows(DL_LOAD_PATH); j++) {

0 commit comments

Comments
 (0)