Skip to content

Commit

Permalink
Fix lower handlers pass to handle merging branches into leave_funcs. …
Browse files Browse the repository at this point in the history
…Also add tests and fix llvmpasses
  • Loading branch information
gbaraldi committed Oct 18, 2024
1 parent bafd135 commit 39746e3
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 32 deletions.
26 changes: 23 additions & 3 deletions src/llvm-lower-handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,16 @@ static bool lowerExcHandlers(Function &F) {
EnterDepth[CI] = Depth++;
else if (Callee == leave_func || Callee == leave_noexcept_func) {
LeaveDepth[CI] = Depth;
Depth -= cast<ConstantInt>(CI->getArgOperand(1))->getLimitedValue();
if (auto change = dyn_cast<ConstantInt>(CI->getArgOperand(1)))
Depth -= change->getLimitedValue();
else if (auto Phi = dyn_cast<PHINode>(CI->getArgOperand(1))) {
//This should really do a dataflow analysis but assuming worst case means that we will always have enough space
uint64_t MinPhiDepth = std::numeric_limits<uint64_t>::max();
for (Value *Incoming : Phi->incoming_values()) {
MinPhiDepth = std::min(MinPhiDepth, cast<ConstantInt>(Incoming)->getLimitedValue());
}
Depth -= MinPhiDepth;
}
}
assert(Depth >= 0);
if (Depth > MaxDepth)
Expand All @@ -175,7 +184,7 @@ static bool lowerExcHandlers(Function &F) {
unsigned allocaAddressSpace = F.getParent()->getDataLayout().getAllocaAddrSpace();
for (int i = 0; i < MaxDepth; ++i) {
auto *buff = new AllocaInst(Type::getInt8Ty(F.getContext()), allocaAddressSpace,
handler_sz, Align(16), "", firstInst);
handler_sz, Align(16), "depth" + std::to_string(i), firstInst);
if (allocaAddressSpace) {
AddrSpaceCastInst *buff_casted = new AddrSpaceCastInst(buff, PointerType::get(F.getContext(), AddressSpace::Generic));
buff_casted->insertAfter(buff);
Expand Down Expand Up @@ -232,7 +241,18 @@ static bool lowerExcHandlers(Function &F) {
// Insert lifetime end intrinsics after every leave.
for (auto it : LeaveDepth) {
int StartDepth = it.second - 1;
int npops = cast<ConstantInt>(it.first->getArgOperand(1))->getLimitedValue();
uint64_t minPops = std::numeric_limits<uint64_t>::max();
if (auto change = dyn_cast<ConstantInt>(it.first->getArgOperand(1)))
minPops = change->getLimitedValue();
else if (auto Phi = dyn_cast<PHINode>(it.first->getArgOperand(1))) {
//This should really do a dataflow analysis but assuming worst case means that we will always have enough space
uint64_t MinPhiDepth = std::numeric_limits<uint64_t>::max();
for (Value *Incoming : Phi->incoming_values()) {
MinPhiDepth = std::min(MinPhiDepth, cast<ConstantInt>(Incoming)->getLimitedValue());
}
minPops = MinPhiDepth;
}
int npops = minPops;
for (int i = 0; i < npops; ++i) {
assert(StartDepth-i >= 0);
Value *lifetime_args[] = {
Expand Down
26 changes: 26 additions & 0 deletions test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using InteractiveUtils

a::Int = 3
function poll_fd2()
local timer
try
try
try
global a = 1
finally
global a = 2
end
return events
catch ex
return FDEvent()
end
finally
if @isdefined(timer)
global a = 2
else
global a = 4
end
end
end

code_llvm(poll_fd2, ())

Check warning on line 26 in test.jl

View workflow job for this annotation

GitHub Actions / Check whitespace

Whitespace check

no trailing newline
26 changes: 26 additions & 0 deletions test/exceptions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,29 @@ end
catch
current_exceptions()
end) == 2


a1234123::Int = 3
function poll_fd2()
local timer
try
try
try
global a1234123 = 1
finally
global a1234123 = 2
end
return events
catch ex
return FDEvent()
end
finally
if @isdefined(timer)
global a1234123 = 2
else
global a1234123 = 4
end
end
end
@test_throws UndefVarError poll_fd2() #shouldn't crash
# Trigger merging of branches in LLVM so we have a dynamic pop_handler
2 changes: 1 addition & 1 deletion test/llvmpasses/image-codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# CHECK-NOT: private global
# CHECK: jl_global
# COM: we emit both declarations and definitions, so we may see either style in the IR
# CHECK-SAME: = {{(external )?}}global
# CHECK-SAME: = {{(external )?}}
# CHECK: julia_f_
# CHECK-NOT: internal global
# CHECK-NOT: private global
Expand Down
95 changes: 76 additions & 19 deletions test/llvmpasses/lower-handlers.ll
Original file line number Diff line number Diff line change
@@ -1,31 +1,88 @@
; This file is a part of Julia. License is MIT: https://julialang.org/license

; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LowerExcHandlers)' -S %s | FileCheck %s
; ModuleID = 'lower-handlers.ll'
; ModuleID = 'lower-handlers2.ll'
source_filename = "lower-handlers.ll"

attributes #1 = { returns_twice }
declare {i32, i8*} @julia.except_enter({}*) #1
declare void @ijl_pop_handler({}*, i32)
declare i8**** @julia.ptls_states()
declare i8**** @julia.get_pgcstack()
declare ptr @julia.get_pgcstack()

declare i64 @ijl_excstack_state(ptr)

declare { i32, ptr } @julia.except_enter(ptr)

declare void @ijl_pop_handler(ptr, i32)

declare void @ijl_pop_handler_noexcept(ptr, i32)

declare void @ijl_restore_excstack(ptr, i64)

declare ptr @julia.ptls_states()

define void @simple() {
top:
%pgcstack = call i8**** @julia.get_pgcstack()
%pgcstack = call ptr @julia.get_pgcstack()
; CHECK: call void @llvm.lifetime.start
; CHECK: call void @ijl_enter_handler
; CHECK: setjmp
%rb = call {i32, i8*} @julia.except_enter({}* null)
%r = extractvalue {i32, i8*} %rb, 0
%b = extractvalue {i32, i8*} %rb, 1
%cmp = icmp eq i32 %r, 0
br i1 %cmp, label %try, label %catch
try:
%lcssa = phi {i32, i8*} [ %rb, %top ]
br label %after
catch:
br label %after
after:
call void @ijl_pop_handler({}* null, i32 1)
%rb = call { i32, ptr } @julia.except_enter(ptr null)
%r = extractvalue { i32, ptr } %rb, 0
%b = extractvalue { i32, ptr } %rb, 1
%cmp = icmp eq i32 %r, 0
br i1 %cmp, label %try, label %catch

try: ; preds = %top
%lcssa = phi { i32, ptr } [ %rb, %top ]
br label %after

catch: ; preds = %top
br label %after

after: ; preds = %catch, %try
call void @ijl_pop_handler(ptr null, i32 1)
; CHECK: llvm.lifetime.end
ret void
}

define ptr addrspace(10) @julia_poll_fd2_1135(){
top:
; CHECK: %depth0 = alloca i8, i32 256, align 16
; CHECK: %depth1 = alloca i8, i32 256, align 16
%pgcstack = call ptr @julia.get_pgcstack()
%current_task7 = getelementptr inbounds i8, ptr %pgcstack, i64 -112
%0 = call i64 @ijl_excstack_state(ptr nonnull %current_task7)
; CHECK: call void @llvm.lifetime.start
; CHECK: call void @ijl_enter_handler
; CHECK: setjmp
%1 = call { i32, ptr } @julia.except_enter(ptr nonnull %current_task7)
%2 = extractvalue { i32, ptr } %1, 0
%.not2 = icmp eq i32 %2, 0
br i1 %.not2, label %try, label %L50

L50: ; preds = %top
call void @ijl_pop_handler(ptr nonnull %current_task7, i32 1)
; CHECK: llvm.lifetime.end
unreachable

try: ; preds = %top
%3 = call i64 @ijl_excstack_state(ptr nonnull %current_task7)
; CHECK: call void @llvm.lifetime.start
; CHECK: call void @ijl_enter_handler
; CHECK: setjmp
%4 = call { i32, ptr } @julia.except_enter(ptr nonnull %current_task7)
%5 = extractvalue { i32, ptr } %4, 0
%6 = icmp eq i32 %5, 0
br i1 %6, label %common.ret, label %catch_pop14

catch_pop14: ; preds = %try
call void @ijl_pop_handler(ptr nonnull %current_task7, i32 1)
; CHECK: llvm.lifetime.end
call void @ijl_restore_excstack(ptr nonnull %current_task7, i64 %3)
br label %common.ret

common.ret: ; preds = %catch_pop14, %try
%.sink = phi i32 [ 1, %catch_pop14 ], [ 2, %try ]
call void @ijl_pop_handler_noexcept(ptr nonnull %current_task7, i32 %.sink)
; CHECK: llvm.lifetime.end
ret void
ret ptr addrspace(10) null
}
12 changes: 3 additions & 9 deletions test/llvmpasses/pipeline-prints.ll
Original file line number Diff line number Diff line change
Expand Up @@ -285,25 +285,19 @@ attributes #2 = { inaccessiblemem_or_argmemonly }

; COM: InstSimplify/InstCombine should kill this zext-trunc pair
; AFTEREARLYSIMPLIFICATION: [[ZEXT:%.*]] = zext i1 {{%.*}} to i8
; AFTEREARLYSIMPLIFICATION-NEXT: trunc i8 [[ZEXT]] to i1

; BEFOREEARLYOPTIMIZATION: [[ZEXT:%.*]] = zext i1 {{%.*}} to i8
; BEFOREEARLYOPTIMIZATION-NEXT: trunc i8 [[ZEXT]] to i1

; AFTEREARLYOPTIMIZATION-NOT: zext i1 {{%.*}} to i8
; AFTEREARLYOPTIMIZATION-NOT: trunc i8 {{%.*}} to i1

; BEFORELOOPOPTIMIZATION-NOT: zext i1 {{%.*}} to i8
; BEFORELOOPOPTIMIZATION-NOT: trunc i8 {{%.*}} to i1

; COM: Loop simplification makes the exit condition obvious
; AFTERLOOPSIMPLIFICATION: L35.lr.ph:
; AFTERLOOPSIMPLIFICATION: add nuw nsw

; COM: Scalar optimization removes the previous add from the preheader
; AFTERSCALAROPTIMIZATION: L35.lr.ph:
; AFTERSCALAROPTIMIZATION-NOT: add nuw nsw
; AFTERSCALAROPTIMIZATION: br label %L35
; COM: Scalar optimization removes the preheader
; AFTERSCALAROPTIMIZATION: L17:
; AFTERSCALAROPTIMIZATION: icmp eq i64 {{%.*}}, 1,

; COM: Vectorization does stuff
; AFTERVECTORIZATION: vector.body
Expand Down

0 comments on commit 39746e3

Please sign in to comment.