Skip to content

Commit

Permalink
Automatically :leave the exception frame on the catch edge (#52245)
Browse files Browse the repository at this point in the history
Right now, we require a :leave expression at both the end of a try
region and as the first expression in the catch block. This doesn't
really make sense. Throwing the exception should leave the exception
frame implicitly. This isn't a huge saving, but does save one IR node
(and generated call in the native code) per try/catch block.
  • Loading branch information
Keno authored Nov 25, 2023
1 parent 18c6c66 commit ea261ce
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 32 deletions.
7 changes: 5 additions & 2 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ function compute_trycatch(code::Vector{Any}, ip::BitSet)
handler_id = length(handlers)
handler_at[pc + 1] = (handler_id, 0)
push!(ip, pc + 1)
handler_at[l] = (handler_id, handler_id)
handler_at[l] = (0, handler_id)
push!(ip, l)
end
end
Expand Down Expand Up @@ -399,7 +399,10 @@ function compute_trycatch(code::Vector{Any}, ip::BitSet)
elseif isa(stmt, Expr)
head = stmt.head
if head === :enter
# Already set above
l = stmt.args[1]::Int
# We assigned a handler number above. Here we just merge that
# with out current handler information.
handler_at[l] = (cur_stacks[1], handler_at[l][2])
cur_stacks = (handler_at[pc´][1], cur_stacks[2])
elseif head === :leave
l = 0
Expand Down
7 changes: 6 additions & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8745,11 +8745,16 @@ static jl_llvm_functions_t
sj->setCanReturnTwice();
Value *isz = ctx.builder.CreateICmpEQ(sj, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0));
BasicBlock *tryblk = BasicBlock::Create(ctx.builder.getContext(), "try", f);
BasicBlock *catchpop = BasicBlock::Create(ctx.builder.getContext(), "catch_pop", f);
BasicBlock *handlr = NULL;
handlr = BB[lname];
workstack.push_back(lname - 1);
come_from_bb[cursor + 1] = ctx.builder.GetInsertBlock();
ctx.builder.CreateCondBr(isz, tryblk, handlr);
ctx.builder.CreateCondBr(isz, tryblk, catchpop);
ctx.builder.SetInsertPoint(catchpop);
ctx.builder.CreateCall(prepare_call(jlleave_func),
ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 1));
ctx.builder.CreateBr(handlr);
ctx.builder.SetInsertPoint(tryblk);
}
else {
Expand Down
24 changes: 13 additions & 11 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,15 +550,17 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
s->locals[jl_source_nslots(s->src) + ip] = jl_box_ulong(jl_excstack_state());
if (!jl_setjmp(__eh.eh_ctx, 1)) {
return eval_body(stmts, s, next_ip, toplevel);
}
else if (s->continue_at) { // means we reached a :leave expression
ip = s->continue_at;
s->continue_at = 0;
continue;
}
else { // a real exception
ip = catch_ip;
continue;
} else {
jl_eh_restore_state(&__eh);
if (s->continue_at) { // means we reached a :leave expression
ip = s->continue_at;
s->continue_at = 0;
continue;
}
else { // a real exception
ip = catch_ip;
continue;
}
}
}
else if (head == jl_leave_sym) {
Expand All @@ -575,11 +577,11 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
}
if (hand_n_leave > 0) {
assert(hand_n_leave > 0);
// equivalent to jl_pop_handler(hand_n_leave), but retaining eh for longjmp:
// equivalent to jl_pop_handler(hand_n_leave), longjmping
// to the :enter code above instead, which handles cleanup
jl_handler_t *eh = ct->eh;
while (--hand_n_leave > 0)
eh = eh->prev;
jl_eh_restore_state(eh);
// leave happens during normal control flow, but we must
// longjmp to pop the eval_body call for each enter.
s->continue_at = next_ip;
Expand Down
4 changes: 2 additions & 2 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -4755,9 +4755,9 @@ f(x) = yt(x)
(let ((v3 (compile (cadddr e) break-labels value tail))) ;; emit else block code
(if val (emit-assignment val v3)))
(if endl (emit `(goto ,endl)))))
;; emit either catch or finally block
;; emit either catch or finally block. A combined try/catch/finally block was split into
;; separate trycatch and tryfinally blocks earlier.
(mark-label catch)
(emit `(leave ,handler-token))
(if finally
(begin (enter-finally-block '(call (top rethrow)) #f) ;; enter block via exception
(mark-label endl) ;; non-exceptional control flow enters here
Expand Down
24 changes: 11 additions & 13 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4416,7 +4416,7 @@ g41908() = f41908(Any[1][1])
# issue #42022
let x = Tuple{Int,Any}[
#= 1=# (0, Expr(:(=), Core.SlotNumber(3), 1))
#= 2=# (0, Expr(:enter, 18))
#= 2=# (0, Expr(:enter, 17))
#= 3=# (2, Expr(:(=), Core.SlotNumber(3), 2.0))
#= 4=# (2, Expr(:enter, 12))
#= 5=# (4, Expr(:(=), Core.SlotNumber(3), '3'))
Expand All @@ -4425,18 +4425,16 @@ let x = Tuple{Int,Any}[
#= 8=# (0, Core.ReturnNode(1))
#= 9=# (4, Expr(:call, GlobalRef(Main, :throw)))
#=10=# (4, Expr(:leave, Core.SSAValue(4)))
#=11=# (2, Core.GotoNode(16))
#=12=# (4, Expr(:leave, Core.SSAValue(4)))
#=13=# (2, Expr(:(=), Core.SlotNumber(4), Expr(:the_exception)))
#=14=# (2, Expr(:call, GlobalRef(Main, :rethrow)))
#=15=# (2, Expr(:pop_exception, Core.SSAValue(4)))
#=16=# (2, Expr(:leave, Core.SSAValue(2)))
#=17=# (0, Core.GotoNode(22))
#=18=# (2, Expr(:leave, Core.SSAValue(2)))
#=19=# (0, Expr(:(=), Core.SlotNumber(5), Expr(:the_exception)))
#=20=# (0, nothing)
#=21=# (0, Expr(:pop_exception, Core.SSAValue(2)))
#=22=# (0, Core.ReturnNode(Core.SlotNumber(3)))
#=11=# (2, Core.GotoNode(15))
#=12=# (2, Expr(:(=), Core.SlotNumber(4), Expr(:the_exception)))
#=13=# (2, Expr(:call, GlobalRef(Main, :rethrow)))
#=14=# (2, Expr(:pop_exception, Core.SSAValue(4)))
#=15=# (2, Expr(:leave, Core.SSAValue(2)))
#=16=# (0, Core.GotoNode(20))
#=17=# (0, Expr(:(=), Core.SlotNumber(5), Expr(:the_exception)))
#=18=# (0, nothing)
#=19=# (0, Expr(:pop_exception, Core.SSAValue(2)))
#=20=# (0, Core.ReturnNode(Core.SlotNumber(3)))
]
handler_at, handlers = Core.Compiler.compute_trycatch(last.(x), Core.Compiler.BitSet())
@test map(x->x[1] == 0 ? 0 : handlers[x[1]].enter_idx, handler_at) == first.(x)
Expand Down
1 change: 0 additions & 1 deletion test/compiler/interpreter_exec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ let m = Meta.@lower 1 + 1
# block 6
Core.PhiCNode(Any[Core.SSAValue(5), Core.SSAValue(7), Core.SSAValue(9)]), # NULL, :a, :b
Core.PhiCNode(Any[Core.SSAValue(6)]), # NULL
Expr(:leave, Core.SSAValue(4)),
# block 7
ReturnNode(Core.SSAValue(11)),
]
Expand Down
4 changes: 2 additions & 2 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2089,8 +2089,8 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1]
end
@test popfirst!(lines2) == "$(QuoteNode(2))"
@test pop!(lines2) == " └─── \$(QuoteNode(4))"
@test pop!(lines1) == "18 └─── return %22"
@test pop!(lines2) == " │ return %22"
@test pop!(lines1) == "18 └─── return %21"
@test pop!(lines2) == " │ return %21"
@test pop!(lines2) == "18 │ \$(QuoteNode(3))"
@test lines1 == lines2

Expand Down

0 comments on commit ea261ce

Please sign in to comment.