Skip to content

Commit e36f65f

Browse files
authored
codegen: fix gc rooting bug (#51744)
ccall was not creating roots for the contents of struct values which contained roots on the stack, as expected to align with `GC.@preserve`, and causing many segfaults for #51319
1 parent 0b31e8b commit e36f65f

File tree

4 files changed

+33
-25
lines changed

4 files changed

+33
-25
lines changed

src/ccall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs)
13931393
if (jl_is_long(argi_root))
13941394
continue;
13951395
jl_cgval_t arg_root = emit_expr(ctx, argi_root);
1396-
Value *gc_root = get_gc_root_for(arg_root);
1396+
Value *gc_root = get_gc_root_for(ctx, arg_root);
13971397
if (gc_root)
13981398
gc_uses.push_back(gc_root);
13991399
}

src/cgutils.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -314,19 +314,34 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V)
314314
return Call;
315315
}
316316

317-
static Value *get_gc_root_for(const jl_cgval_t &x)
317+
static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt);
318+
static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile=false);
319+
320+
static Value *get_gc_root_for(jl_codectx_t &ctx, const jl_cgval_t &x)
318321
{
319-
if (x.Vboxed)
322+
if (x.constant || x.typ == jl_bottom_type)
323+
return nullptr;
324+
if (x.Vboxed) // superset of x.isboxed
320325
return x.Vboxed;
321-
if (x.ispointer() && !x.constant) {
326+
assert(!x.isboxed);
327+
#ifndef NDEBUG
328+
if (x.ispointer()) {
322329
assert(x.V);
323330
if (PointerType *T = dyn_cast<PointerType>(x.V->getType())) {
324-
if (T->getAddressSpace() == AddressSpace::Tracked ||
325-
T->getAddressSpace() == AddressSpace::Derived) {
326-
return x.V;
331+
assert(T->getAddressSpace() != AddressSpace::Tracked);
332+
if (T->getAddressSpace() == AddressSpace::Derived) {
333+
// n.b. this IR would not be valid after LLVM-level inlining,
334+
// since codegen does not have a way to determine the whether
335+
// this argument value needs to be re-rooted
327336
}
328337
}
329338
}
339+
#endif
340+
if (jl_is_concrete_immutable(x.typ) && !jl_is_pointerfree(x.typ)) {
341+
Type *T = julia_type_to_llvm(ctx, x.typ);
342+
return emit_unbox(ctx, T, x, x.typ);
343+
}
344+
// nothing here to root, move along
330345
return nullptr;
331346
}
332347

@@ -1786,9 +1801,6 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v
17861801
return im1;
17871802
}
17881803

1789-
static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt);
1790-
static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value* dest, MDNode *tbaa_dest, unsigned alignment, bool isVolatile=false);
1791-
17921804
static void emit_write_barrier(jl_codectx_t&, Value*, ArrayRef<Value*>);
17931805
static void emit_write_barrier(jl_codectx_t&, Value*, Value*);
17941806
static void emit_write_multibarrier(jl_codectx_t&, Value*, Value*, jl_value_t*);

src/codegen.cpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,9 +3064,12 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a
30643064
varg2 = emit_pointer_from_objref(ctx, varg2);
30653065
Value *gc_uses[2];
30663066
int nroots = 0;
3067-
if ((gc_uses[nroots] = get_gc_root_for(arg1)))
3067+
// these roots may seem a bit overkill, but we want to make sure
3068+
// that a!=b implies (a,)!=(b,) even if a and b are unused and
3069+
// therefore could be freed and then the memory for a reused for b
3070+
if ((gc_uses[nroots] = get_gc_root_for(ctx, arg1)))
30683071
nroots++;
3069-
if ((gc_uses[nroots] = get_gc_root_for(arg2)))
3072+
if ((gc_uses[nroots] = get_gc_root_for(ctx, arg2)))
30703073
nroots++;
30713074
OperandBundleDef OpBundle("jl_roots", makeArrayRef(gc_uses, nroots));
30723075
auto answer = ctx.builder.CreateCall(prepare_call(memcmp_func), {
@@ -5863,16 +5866,9 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
58635866
}
58645867
SmallVector<Value*, 0> vals;
58655868
for (size_t i = 0; i < nargs; ++i) {
5866-
const jl_cgval_t &ai = argv[i];
5867-
if (ai.constant || ai.typ == jl_bottom_type)
5868-
continue;
5869-
if (ai.isboxed) {
5870-
vals.push_back(ai.Vboxed);
5871-
}
5872-
else if (jl_is_concrete_immutable(ai.typ) && !jl_is_pointerfree(ai.typ)) {
5873-
Type *at = julia_type_to_llvm(ctx, ai.typ);
5874-
vals.push_back(emit_unbox(ctx, at, ai, ai.typ));
5875-
}
5869+
Value *gc_root = get_gc_root_for(ctx, argv[i]);
5870+
if (gc_root)
5871+
vals.push_back(gc_root);
58765872
}
58775873
Value *token = vals.empty()
58785874
? (Value*)ConstantTokenNone::get(ctx.builder.getContext())

test/compiler/codegen.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,13 @@ end
581581
end
582582
@test occursin("llvm.julia.gc_preserve_begin", get_llvm(f3, Tuple{Bool}, true, false, false))
583583

584-
# unions of immutables (JuliaLang/julia#39501)
584+
# PhiNode of unions of immutables (JuliaLang/julia#39501)
585585
function f2(cond)
586-
val = cond ? 1 : 1f0
586+
val = cond ? 1 : ""
587587
GC.@preserve val begin end
588588
return cond
589589
end
590-
@test !occursin("llvm.julia.gc_preserve_begin", get_llvm(f2, Tuple{Bool}, true, false, false))
590+
@test occursin("llvm.julia.gc_preserve_begin", get_llvm(f2, Tuple{Bool}, true, false, false))
591591
# make sure the fix for the above doesn't regress #34241
592592
function f4(cond)
593593
val = cond ? ([1],) : ([1f0],)

0 commit comments

Comments
 (0)