Skip to content

Commit aff6512

Browse files
authored
codegen: Restore ct->scope in jl_eh_restore_state (JuliaLang#55907)
This eliminates the need to associate a `catch` with every `with(...) do ... end` block, which was really just acting as a landing pad to restore `jl_current_task->scope` in the majority of cases. This change does not actually update lowering to remove the unnecessary `catch` block - that's left as a follow-up.
1 parent 0de9164 commit aff6512

File tree

4 files changed

+42
-42
lines changed

4 files changed

+42
-42
lines changed

src/codegen.cpp

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,7 +1955,7 @@ class jl_codectx_t {
19551955
// local var info. globals are not in here.
19561956
SmallVector<jl_varinfo_t, 0> slots;
19571957
std::map<int, jl_varinfo_t> phic_slots;
1958-
std::map<int, std::pair<Value*, Value*> > scope_restore;
1958+
std::map<int, Value*> scope_tokens;
19591959
SmallVector<jl_cgval_t, 0> SAvalues;
19601960
SmallVector<std::tuple<jl_cgval_t, BasicBlock *, AllocaInst *, PHINode *, SmallVector<PHINode*,0>, jl_value_t *>, 0> PhiNodes;
19611961
SmallVector<bool, 0> ssavalue_assigned;
@@ -6573,8 +6573,6 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
65736573
}
65746574
else if (head == jl_leave_sym) {
65756575
int hand_n_leave = 0;
6576-
Value *scope_to_restore = nullptr;
6577-
Value *scope_ptr = nullptr;
65786576
for (size_t i = 0; i < jl_expr_nargs(ex); ++i) {
65796577
jl_value_t *arg = args[i];
65806578
if (arg == jl_nothing)
@@ -6584,20 +6582,27 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
65846582
jl_value_t *enter_stmt = jl_array_ptr_ref(ctx.code, enter_idx);
65856583
if (enter_stmt == jl_nothing)
65866584
continue;
6587-
if (ctx.scope_restore.count(enter_idx))
6588-
std::tie(scope_to_restore, scope_ptr) = ctx.scope_restore[enter_idx];
6585+
if (ctx.scope_tokens.count(enter_idx)) {
6586+
// TODO: The semantics of `gc_preserve` are not perfect here. An `Expr(:enter, ...)` block may
6587+
// have multiple exits, but effects of `preserve_end` are only extended to the end of the
6588+
// dominance of each `Expr(:leave, ...)`.
6589+
//
6590+
// That means that a scope object can suddenly end up preserved again outside of an
6591+
// `Expr(:enter, ...)` region where it ought to be dead. It'd be preferable if the effects
6592+
// of gc_preserve_end propagated through a control-flow joins as long as all incoming
6593+
// agree about the preserve state.
6594+
//
6595+
// This is correct as-is anyway - it just means the scope lives longer than it needs to
6596+
// if the `Expr(:enter, ...)` has multiple exits.
6597+
ctx.builder.CreateCall(prepare_call(gc_preserve_end_func), {ctx.scope_tokens[enter_idx]});
6598+
}
65896599
if (jl_enternode_catch_dest(enter_stmt)) {
65906600
// We're not actually setting up the exception frames for these, so
65916601
// we don't need to exit them.
65926602
hand_n_leave += 1;
65936603
}
65946604
}
65956605
ctx.builder.CreateCall(prepare_call(jlleave_noexcept_func), {get_current_task(ctx), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), hand_n_leave)});
6596-
if (scope_to_restore) {
6597-
jl_aliasinfo_t scope_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe);
6598-
scope_ai.decorateInst(
6599-
ctx.builder.CreateAlignedStore(scope_to_restore, scope_ptr, ctx.types().alignof_ptr));
6600-
}
66016606
}
66026607
else if (head == jl_pop_exception_sym) {
66036608
jl_cgval_t excstack_state = emit_expr(ctx, jl_exprarg(expr, 0));
@@ -7180,7 +7185,7 @@ static Value *get_tls_world_age_field(jl_codectx_t &ctx)
71807185
static Value *get_scope_field(jl_codectx_t &ctx)
71817186
{
71827187
Value *ct = get_current_task(ctx);
7183-
return emit_ptrgep(ctx, ct, offsetof(jl_task_t, scope), "current_scope");
7188+
return emit_ptrgep(ctx, ct, offsetof(jl_task_t, scope), "scope");
71847189
}
71857190

71867191
Function *get_or_emit_fptr1(StringRef preal_decl, Module *M)
@@ -9604,28 +9609,6 @@ static jl_llvm_functions_t
96049609
continue;
96059610
}
96069611
else if (jl_is_enternode(stmt)) {
9607-
// For the two-arg version of :enter, twiddle the scope
9608-
Value *scope_ptr = NULL;
9609-
Value *old_scope = NULL;
9610-
jl_aliasinfo_t scope_ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe);
9611-
if (jl_enternode_scope(stmt)) {
9612-
jl_cgval_t new_scope = emit_expr(ctx, jl_enternode_scope(stmt));
9613-
if (new_scope.typ == jl_bottom_type) {
9614-
// Probably dead code, but let's be loud about it in case it isn't, so we fail
9615-
// at the point of the miscompile, rather than later when something attempts to
9616-
// read the scope.
9617-
emit_error(ctx, "(INTERNAL ERROR): Attempted to execute EnterNode with bad scope");
9618-
find_next_stmt(-1);
9619-
continue;
9620-
}
9621-
Value *new_scope_boxed = boxed(ctx, new_scope);
9622-
scope_ptr = get_scope_field(ctx);
9623-
old_scope = scope_ai.decorateInst(
9624-
ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, scope_ptr, ctx.types().alignof_ptr));
9625-
scope_ai.decorateInst(
9626-
ctx.builder.CreateAlignedStore(new_scope_boxed, scope_ptr, ctx.types().alignof_ptr));
9627-
ctx.scope_restore[cursor] = std::make_pair(old_scope, scope_ptr);
9628-
}
96299612
int lname = jl_enternode_catch_dest(stmt);
96309613
if (lname) {
96319614
// Save exception stack depth at enter for use in pop_exception
@@ -9651,16 +9634,31 @@ static jl_llvm_functions_t
96519634
ctx.builder.SetInsertPoint(catchpop);
96529635
{
96539636
ctx.builder.CreateCall(prepare_call(jlleave_func), {get_current_task(ctx), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 1)});
9654-
if (old_scope) {
9655-
scope_ai.decorateInst(
9656-
ctx.builder.CreateAlignedStore(old_scope, scope_ptr, ctx.types().alignof_ptr));
9657-
}
96589637
ctx.builder.CreateBr(handlr);
96599638
}
96609639
ctx.builder.SetInsertPoint(tryblk);
96619640
auto ehptr = emit_ptrgep(ctx, ct, offsetof(jl_task_t, eh));
96629641
ctx.builder.CreateAlignedStore(ehbuf, ehptr, ctx.types().alignof_ptr);
96639642
}
9643+
// For the two-arg version of :enter, twiddle the scope
9644+
if (jl_enternode_scope(stmt)) {
9645+
jl_cgval_t scope = emit_expr(ctx, jl_enternode_scope(stmt));
9646+
if (scope.typ == jl_bottom_type) {
9647+
// Probably dead code, but let's be loud about it in case it isn't, so we fail
9648+
// at the point of the miscompile, rather than later when something attempts to
9649+
// read the scope.
9650+
emit_error(ctx, "(INTERNAL ERROR): Attempted to execute EnterNode with bad scope");
9651+
find_next_stmt(-1);
9652+
continue;
9653+
}
9654+
Value *scope_boxed = boxed(ctx, scope);
9655+
StoreInst *scope_store = ctx.builder.CreateAlignedStore(scope_boxed, get_scope_field(ctx), ctx.types().alignof_ptr);
9656+
jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(scope_store);
9657+
// GC preserve the scope, since it is not rooted in the `jl_handler_t *`
9658+
// and may be removed from jl_current_task by any nested block and then
9659+
// replaced later
9660+
ctx.scope_tokens[cursor] = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {scope_boxed});
9661+
}
96649662
}
96659663
else {
96669664
emit_stmtpos(ctx, stmt, cursor);

src/interpreter.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -527,16 +527,14 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
527527
}
528528
s->locals[jl_source_nslots(s->src) + ip] = jl_box_ulong(jl_excstack_state(ct));
529529
if (jl_enternode_scope(stmt)) {
530-
jl_value_t *old_scope = ct->scope;
531-
JL_GC_PUSH1(&old_scope);
532-
jl_value_t *new_scope = eval_value(jl_enternode_scope(stmt), s);
533-
ct->scope = new_scope;
530+
jl_value_t *scope = eval_value(jl_enternode_scope(stmt), s);
531+
JL_GC_PUSH1(&scope);
532+
ct->scope = scope;
534533
if (!jl_setjmp(__eh.eh_ctx, 1)) {
535534
ct->eh = &__eh;
536535
eval_body(stmts, s, next_ip, toplevel);
537536
jl_unreachable();
538537
}
539-
ct->scope = old_scope;
540538
JL_GC_POP();
541539
}
542540
else {

src/julia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,7 @@ typedef struct _jl_excstack_t jl_excstack_t;
22632263
typedef struct _jl_handler_t {
22642264
jl_jmp_buf eh_ctx;
22652265
jl_gcframe_t *gcstack;
2266+
jl_value_t *scope;
22662267
struct _jl_handler_t *prev;
22672268
int8_t gc_state;
22682269
size_t locks_len;

src/rtutils.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ JL_DLLEXPORT void jl_enter_handler(jl_task_t *ct, jl_handler_t *eh)
244244
// Must have no safepoint
245245
eh->prev = ct->eh;
246246
eh->gcstack = ct->gcstack;
247+
eh->scope = ct->scope;
247248
eh->gc_state = jl_atomic_load_relaxed(&ct->ptls->gc_state);
248249
eh->locks_len = ct->ptls->locks.len;
249250
eh->defer_signal = ct->ptls->defer_signal;
@@ -273,6 +274,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh)
273274
sig_atomic_t old_defer_signal = ptls->defer_signal;
274275
ct->eh = eh->prev;
275276
ct->gcstack = eh->gcstack;
277+
ct->scope = eh->scope;
276278
small_arraylist_t *locks = &ptls->locks;
277279
int unlocks = locks->len > eh->locks_len;
278280
if (unlocks) {
@@ -311,6 +313,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh)
311313
JL_DLLEXPORT void jl_eh_restore_state_noexcept(jl_task_t *ct, jl_handler_t *eh)
312314
{
313315
assert(ct->gcstack == eh->gcstack && "Incorrect GC usage under try catch");
316+
ct->scope = eh->scope;
314317
ct->eh = eh->prev;
315318
ct->ptls->defer_signal = eh->defer_signal; // optional, but certain try-finally (in stream.jl) may be slightly harder to write without this
316319
}

0 commit comments

Comments
 (0)