Skip to content

Commit b76fd9f

Browse files
authored
codegen: fix unsound mark_volatile_vars implementation (JuliaLang#57131)
The previous implementation was incorrect, leading to failing to mark variables correctly. The new implementation is more conservative. This simple analysis assumes that inference has normally run or that performance doesn't matter for a particular block of code. Fixes JuliaLang#56996
1 parent 88c71dd commit b76fd9f

File tree

2 files changed

+93
-73
lines changed

2 files changed

+93
-73
lines changed

Compiler/test/codegen.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,3 +1041,20 @@ struct Vec56937 x::NTuple{8, VecElement{Int}} end
10411041

10421042
x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8)))
10431043
@test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash
1044+
1045+
# issue #56996
1046+
let
1047+
()->() # trigger various heuristics
1048+
Base.Experimental.@force_compile
1049+
default_rng_orig = [] # make a value in a Slot
1050+
try
1051+
# overwrite the gc-slots in the exception branch
1052+
throw(ErrorException("This test is supposed to throw an error"))
1053+
catch ex
1054+
# destroy any values that aren't referenced
1055+
GC.gc()
1056+
# make sure that default_rng_orig value is still valid
1057+
@noinline copy!([], default_rng_orig)
1058+
end
1059+
nothing
1060+
end

src/codegen.cpp

Lines changed: 76 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3282,24 +3282,7 @@ static bool local_var_occurs(jl_value_t *e, int sl)
32823282
return false;
32833283
}
32843284

3285-
static std::set<int> assigned_in_try(jl_array_t *stmts, int s, long l)
3286-
{
3287-
std::set<int> av;
3288-
for(int i=s; i < l; i++) {
3289-
jl_value_t *st = jl_array_ptr_ref(stmts,i);
3290-
if (jl_is_expr(st)) {
3291-
if (((jl_expr_t*)st)->head == jl_assign_sym) {
3292-
jl_value_t *ar = jl_exprarg(st, 0);
3293-
if (jl_is_slotnumber(ar)) {
3294-
av.insert(jl_slot_number(ar)-1);
3295-
}
3296-
}
3297-
}
3298-
}
3299-
return av;
3300-
}
3301-
3302-
static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl<jl_varinfo_t> &slots)
3285+
static bool have_try_block(jl_array_t *stmts)
33033286
{
33043287
size_t slength = jl_array_dim0(stmts);
33053288
for (int i = 0; i < (int)slength; i++) {
@@ -3308,19 +3291,38 @@ static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl<jl_varinfo_t>
33083291
int last = jl_enternode_catch_dest(st);
33093292
if (last == 0)
33103293
continue;
3311-
std::set<int> as = assigned_in_try(stmts, i + 1, last - 1);
3312-
for (int j = 0; j < (int)slength; j++) {
3313-
if (j < i || j > last) {
3314-
std::set<int>::iterator it = as.begin();
3315-
for (; it != as.end(); it++) {
3316-
if (local_var_occurs(jl_array_ptr_ref(stmts, j), *it)) {
3317-
jl_varinfo_t &vi = slots[*it];
3318-
vi.isVolatile = true;
3319-
}
3320-
}
3294+
return 1;
3295+
}
3296+
}
3297+
return 0;
3298+
}
3299+
3300+
// conservative marking of all variables potentially used after a catch block that were assigned before it
3301+
static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl<jl_varinfo_t> &slots, const std::set<int> &bbstarts)
3302+
{
3303+
if (!have_try_block(stmts))
3304+
return;
3305+
size_t slength = jl_array_dim0(stmts);
3306+
BitVector assigned_in_block(slots.size()); // conservatively only ignore slots assigned in the same basic block
3307+
for (int j = 0; j < (int)slength; j++) {
3308+
if (bbstarts.count(j + 1))
3309+
assigned_in_block.reset();
3310+
jl_value_t *stmt = jl_array_ptr_ref(stmts, j);
3311+
if (jl_is_expr(stmt)) {
3312+
jl_expr_t *e = (jl_expr_t*)stmt;
3313+
if (e->head == jl_assign_sym) {
3314+
jl_value_t *l = jl_exprarg(e, 0);
3315+
if (jl_is_slotnumber(l)) {
3316+
assigned_in_block.set(jl_slot_number(l)-1);
33213317
}
33223318
}
33233319
}
3320+
for (int slot = 0; slot < (int)slots.size(); slot++) {
3321+
if (!assigned_in_block.test(slot) && local_var_occurs(stmt, slot)) {
3322+
jl_varinfo_t &vi = slots[slot];
3323+
vi.isVolatile = true;
3324+
}
3325+
}
33243326
}
33253327
}
33263328

@@ -8439,7 +8441,6 @@ static jl_llvm_functions_t
84398441
ctx.code = src->code;
84408442
ctx.source = src;
84418443

8442-
std::map<int, BasicBlock*> labels;
84438444
ctx.module = jl_is_method(lam->def.method) ? lam->def.method->module : lam->def.module;
84448445
ctx.linfo = lam;
84458446
ctx.name = name_from_method_instance(lam);
@@ -8499,6 +8500,49 @@ static jl_llvm_functions_t
84998500
if (dbgFuncName.empty()) // Should never happen anymore?
85008501
debug_enabled = false;
85018502

8503+
// First go through and collect all branch targets, so we know where to
8504+
// split basic blocks.
8505+
std::set<int> branch_targets; // 1-indexed, sorted
8506+
for (size_t i = 0; i < stmtslen; ++i) {
8507+
jl_value_t *stmt = jl_array_ptr_ref(stmts, i);
8508+
if (jl_is_gotoifnot(stmt)) {
8509+
int dest = jl_gotoifnot_label(stmt);
8510+
branch_targets.insert(dest);
8511+
// The next 1-indexed statement
8512+
branch_targets.insert(i + 2);
8513+
}
8514+
else if (jl_is_returnnode(stmt)) {
8515+
// We don't do dead branch elimination before codegen
8516+
// so we need to make sure to start a BB after any
8517+
// return node, even if they aren't otherwise branch
8518+
// targets.
8519+
if (i + 2 <= stmtslen)
8520+
branch_targets.insert(i + 2);
8521+
}
8522+
else if (jl_is_enternode(stmt)) {
8523+
branch_targets.insert(i + 1);
8524+
if (i + 2 <= stmtslen)
8525+
branch_targets.insert(i + 2);
8526+
size_t catch_dest = jl_enternode_catch_dest(stmt);
8527+
if (catch_dest)
8528+
branch_targets.insert(catch_dest);
8529+
}
8530+
else if (jl_is_gotonode(stmt)) {
8531+
int dest = jl_gotonode_label(stmt);
8532+
branch_targets.insert(dest);
8533+
if (i + 2 <= stmtslen)
8534+
branch_targets.insert(i + 2);
8535+
}
8536+
else if (jl_is_phinode(stmt)) {
8537+
jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(stmt, 0);
8538+
for (size_t j = 0; j < jl_array_nrows(edges); ++j) {
8539+
size_t edge = jl_array_data(edges, int32_t)[j];
8540+
if (edge == i)
8541+
branch_targets.insert(i + 1);
8542+
}
8543+
}
8544+
}
8545+
85028546
// step 2. process var-info lists to see what vars need boxing
85038547
int n_ssavalues = jl_is_long(src->ssavaluetypes) ? jl_unbox_long(src->ssavaluetypes) : jl_array_nrows(src->ssavaluetypes);
85048548
size_t vinfoslen = jl_array_dim0(src->slotflags);
@@ -8559,7 +8603,7 @@ static jl_llvm_functions_t
85598603
simple_use_analysis(ctx, jl_array_ptr_ref(stmts, i));
85608604

85618605
// determine which vars need to be volatile
8562-
mark_volatile_vars(stmts, ctx.slots);
8606+
mark_volatile_vars(stmts, ctx.slots, branch_targets);
85638607

85648608
// step 4. determine function signature
85658609
if (!specsig)
@@ -9310,8 +9354,8 @@ static jl_llvm_functions_t
93109354

93119355
// step 11b. Do codegen in control flow order
93129356
SmallVector<int, 0> workstack;
9313-
std::map<int, BasicBlock*> BB;
9314-
std::map<size_t, BasicBlock*> come_from_bb;
9357+
DenseMap<size_t, BasicBlock*> BB;
9358+
DenseMap<size_t, BasicBlock*> come_from_bb;
93159359
int cursor = 0;
93169360
int current_label = 0;
93179361
auto find_next_stmt = [&] (int seq_next) {
@@ -9430,47 +9474,6 @@ static jl_llvm_functions_t
94309474

94319475
come_from_bb[0] = ctx.builder.GetInsertBlock();
94329476

9433-
// First go through and collect all branch targets, so we know where to
9434-
// split basic blocks.
9435-
std::set<int> branch_targets; // 1-indexed
9436-
{
9437-
for (size_t i = 0; i < stmtslen; ++i) {
9438-
jl_value_t *stmt = jl_array_ptr_ref(stmts, i);
9439-
if (jl_is_gotoifnot(stmt)) {
9440-
int dest = jl_gotoifnot_label(stmt);
9441-
branch_targets.insert(dest);
9442-
// The next 1-indexed statement
9443-
branch_targets.insert(i + 2);
9444-
} else if (jl_is_returnnode(stmt)) {
9445-
// We don't do dead branch elimination before codegen
9446-
// so we need to make sure to start a BB after any
9447-
// return node, even if they aren't otherwise branch
9448-
// targets.
9449-
if (i + 2 <= stmtslen)
9450-
branch_targets.insert(i + 2);
9451-
} else if (jl_is_enternode(stmt)) {
9452-
branch_targets.insert(i + 1);
9453-
if (i + 2 <= stmtslen)
9454-
branch_targets.insert(i + 2);
9455-
size_t catch_dest = jl_enternode_catch_dest(stmt);
9456-
if (catch_dest)
9457-
branch_targets.insert(catch_dest);
9458-
} else if (jl_is_gotonode(stmt)) {
9459-
int dest = jl_gotonode_label(stmt);
9460-
branch_targets.insert(dest);
9461-
if (i + 2 <= stmtslen)
9462-
branch_targets.insert(i + 2);
9463-
} else if (jl_is_phinode(stmt)) {
9464-
jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(stmt, 0);
9465-
for (size_t j = 0; j < jl_array_nrows(edges); ++j) {
9466-
size_t edge = jl_array_data(edges, int32_t)[j];
9467-
if (edge == i)
9468-
branch_targets.insert(i + 1);
9469-
}
9470-
}
9471-
}
9472-
}
9473-
94749477
for (int label : branch_targets) {
94759478
BasicBlock *bb = BasicBlock::Create(ctx.builder.getContext(),
94769479
"L" + std::to_string(label), f);

0 commit comments

Comments
 (0)