diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index e09ea892ee488..d769cfa5ee469 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -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(CI->getArgOperand(1))->getLimitedValue(); + if (auto change = dyn_cast(CI->getArgOperand(1))) + Depth -= change->getLimitedValue(); + else if (auto Phi = dyn_cast(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::max(); + for (Value *Incoming : Phi->incoming_values()) { + MinPhiDepth = std::min(MinPhiDepth, cast(Incoming)->getLimitedValue()); + } + Depth -= MinPhiDepth; + } } assert(Depth >= 0); if (Depth > MaxDepth) @@ -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); @@ -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(it.first->getArgOperand(1))->getLimitedValue(); + uint64_t minPops = std::numeric_limits::max(); + if (auto change = dyn_cast(it.first->getArgOperand(1))) + minPops = change->getLimitedValue(); + else if (auto Phi = dyn_cast(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::max(); + for (Value *Incoming : Phi->incoming_values()) { + MinPhiDepth = std::min(MinPhiDepth, cast(Incoming)->getLimitedValue()); + } + minPops = MinPhiDepth; + } + int npops = minPops; for (int i = 0; i < npops; ++i) { assert(StartDepth-i >= 0); Value *lifetime_args[] = { diff --git a/test/exceptions.jl b/test/exceptions.jl index eb0bbaec35090..4f05076d825ef 100644 --- a/test/exceptions.jl +++ b/test/exceptions.jl @@ -400,3 +400,28 @@ end catch current_exceptions() end) == 2 + +# Trigger merging of branches in LLVM so we have a dynamic pop_handler +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 diff --git a/test/llvmpasses/image-codegen.jl b/test/llvmpasses/image-codegen.jl index 2e52245b7d3b9..d782d1bccccfa 100644 --- a/test/llvmpasses/image-codegen.jl +++ b/test/llvmpasses/image-codegen.jl @@ -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 diff --git a/test/llvmpasses/lower-handlers.ll b/test/llvmpasses/lower-handlers.ll index 7f0648a1a8bf5..172c9820cba02 100644 --- a/test/llvmpasses/lower-handlers.ll +++ b/test/llvmpasses/lower-handlers.ll @@ -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 } diff --git a/test/llvmpasses/pipeline-prints.ll b/test/llvmpasses/pipeline-prints.ll index ecb70953026c2..119363c5179c8 100644 --- a/test/llvmpasses/pipeline-prints.ll +++ b/test/llvmpasses/pipeline-prints.ll @@ -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