Skip to content

Commit

Permalink
Fix assertion/crash when optimizing function with dead basic block (#…
Browse files Browse the repository at this point in the history
…54690)

AllocOpt probably needs to handle that in other places more smartly but
this seems to at least stop it crashing. Fixes issue found in
#54604 (comment) by
@topolarity.

(cherry picked from commit 5cb1107)
  • Loading branch information
gbaraldi authored and KristofferC committed Jul 24, 2024
1 parent 4b063cf commit 6ee9546
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 60 deletions.
2 changes: 2 additions & 0 deletions src/llvm-alloc-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ void Optimizer::insertLifetime(Value *ptr, Constant *sz, Instruction *orig)
auto bb = use->getParent();
if (!bbs.insert(bb).second)
continue;
if (pred_empty(bb))
continue; // No predecessors so the block is dead
assert(lifetime_stack.empty());
Lifetime::Frame cur{bb};
while (true) {
Expand Down
18 changes: 18 additions & 0 deletions test/compiler/codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -852,3 +852,21 @@ end
# https://github.com/JuliaLang/julia/issues/51233
obj51233 = (1,)
@test_throws ErrorException obj51233.x

bar54599() = Base.inferencebarrier(true) ? (Base.PkgId(Main),1) : nothing

function foo54599()
pkginfo = @noinline bar54599()
pkgid = pkginfo !== nothing ? pkginfo[1] : nothing
@noinline println(devnull, pkgid)
pkgid.uuid !== nothing ? pkgid.uuid : false
end

#this function used to crash allocopt due to a no predecessors bug
barnopreds() = Base.inferencebarrier(true) ? (Base.PkgId(Test),1) : nothing
function foonopreds()
pkginfo = @noinline barnopreds()
pkgid = pkginfo !== nothing ? pkginfo[1] : nothing
pkgid.uuid !== nothing ? pkgid.uuid : false
end
@test foonopreds() !== nothing
156 changes: 96 additions & 60 deletions test/llvmpasses/alloc-opt-pass.ll
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,23 @@
; CHECK-NEXT: br label %L3

; CHECK: L3:
define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) {
%pgcstack = call {}*** @julia.get_pgcstack()
%ptls = call {}*** @julia.ptls_states()
%ptls_i8 = bitcast {}*** %ptls to i8*
define void @preserve_branches(ptr %fptr, i1 %b, i1 %b2) {
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
br i1 %b, label %L1, label %L3

L1:
%v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag)
%tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* nonnull %v)
L1: ; preds = %0
%v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag)
%tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) nonnull %v)
call void @external_function()
br i1 %b2, label %L2, label %L3

L2:
L2: ; preds = %L1
call void @external_function()
br label %L3

L3:
L3: ; preds = %L2, %L1, %0
ret void
}
; CHECK-LABEL: }{{$}}
Expand All @@ -58,24 +58,24 @@ L3:
; CHECK-NEXT: br label %L3

; CHECK: L3:
define void @preserve_branches2(i8* %fptr, i1 %b, i1 %b2) {
%pgcstack = call {}*** @julia.get_pgcstack()
%ptls = call {}*** @julia.ptls_states()
%ptls_i8 = bitcast {}*** %ptls to i8*
%v2 = call {} addrspace(10)* @external_function2()
define void @preserve_branches2(ptr %fptr, i1 %b, i1 %b2) {
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
%v2 = call ptr addrspace(10) @external_function2()
br i1 %b, label %L1, label %L3

L1:
%v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag)
%tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v, {} addrspace(10)* nonnull %v2)
L1: ; preds = %0
%v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag)
%tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v, ptr addrspace(10) nonnull %v2)
call void @external_function()
br i1 %b2, label %L2, label %L3

L2:
L2: ; preds = %L1
call void @external_function()
br label %L3

L3:
L3: ; preds = %L2, %L1, %0
ret void
}
; CHECK-LABEL: }{{$}}
Expand All @@ -85,26 +85,30 @@ L3:
; CHECK-NOT: alloca i96
; CHECK: ret void
define void @legal_int_types() {
%pgcstack = call {}*** @julia.get_pgcstack()
%ptls = call {}*** @julia.ptls_states()
%ptls_i8 = bitcast {}*** %ptls to i8*
%var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 12, {} addrspace(10)* @tag)
%var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)*
%var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2)
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
%var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 12, ptr addrspace(10) @tag)
%var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11)
%var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2)
ret void
}
; CHECK-LABEL: }{{$}}


declare void @external_function()
declare {} addrspace(10)* @external_function2()
declare {}*** @julia.ptls_states()
declare {}*** @julia.get_pgcstack()
declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, i64, {} addrspace(10)*)
declare {}* @julia.pointer_from_objref({} addrspace(11)*)
declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1)
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1)

declare ptr addrspace(10) @external_function2()

declare ptr @julia.ptls_states()

declare ptr @julia.get_pgcstack()

declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10))

declare ptr @julia.pointer_from_objref(ptr addrspace(11))

declare token @llvm.julia.gc_preserve_begin(...)

declare void @llvm.julia.gc_preserve_end(token)

; CHECK-LABEL: @memref_collision
Expand All @@ -121,44 +125,76 @@ declare void @llvm.julia.gc_preserve_end(token)
; CHECK: L2:
; CHECK: load i
define void @memref_collision(i64 %x) {
%pgcstack = call {}*** @julia.get_pgcstack()
%ptls = call {}*** @julia.ptls_states()
%ptls_i8 = bitcast {}*** %ptls to i8*
%v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag)
%v_p = bitcast {} addrspace(10)* %v to i64 addrspace(10)*
store i64 %x, i64 addrspace(10)* %v_p
br i1 0, label %L1, label %L2

L1:
%v1 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)*
%v1_x = load {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %v1
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
%v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag)
%v_p = bitcast ptr addrspace(10) %v to ptr addrspace(10)
store i64 %x, ptr addrspace(10) %v_p, align 4
br i1 false, label %L1, label %L2

L1: ; preds = %0
%v1 = bitcast ptr addrspace(10) %v to ptr addrspace(10)
%v1_x = load ptr addrspace(10), ptr addrspace(10) %v1, align 8
ret void

L2:
%v2 = bitcast {} addrspace(10)* %v to i64 addrspace(10)*
%v2_x = load i64, i64 addrspace(10)* %v2
L2: ; preds = %0
%v2 = bitcast ptr addrspace(10) %v to ptr addrspace(10)
%v2_x = load i64, ptr addrspace(10) %v2, align 4
ret void
}

; CHECK-LABEL: }{{$}}

; CHECK-LABEL: @lifetime_no_preserve_end
; CHECK: alloca
; CHECK-NOT: call token(...) @llvm.julia.gc_preserve_begin
; CHECK: call void @llvm.lifetime.start
; CHECK-NOT: call void @llvm.lifetime.end
define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret({}) %0) {
%pgcstack = call {}*** @julia.get_pgcstack()
%ptls = call {}*** @julia.ptls_states()
%ptls_i8 = bitcast {}*** %ptls to i8*
%v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag)
%token = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v)
%v_derived = addrspacecast {} addrspace(10)* %v to {} addrspace(11)*
%ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %v_derived)
%ptr_raw = bitcast {}* %ptr to i8*
call void @external_function() ; safepoint
%ret_raw = bitcast {}* %0 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %ret_raw, i8 * align 8 %ptr_raw, i64 0, i1 false)
%ret_raw2 = bitcast {}* %0 to i8*
define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret({}) %0) {
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
%v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag)
%token = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v)
%v_derived = addrspacecast ptr addrspace(10) %v to ptr addrspace(11)
%ptr = call nonnull ptr @julia.pointer_from_objref(ptr addrspace(11) %v_derived)
%ptr_raw = bitcast ptr %ptr to ptr
call void @external_function()
%ret_raw = bitcast ptr %0 to ptr
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %ret_raw, ptr align 8 %ptr_raw, i64 0, i1 false)
%ret_raw2 = bitcast ptr %0 to ptr
ret void
}
; CHECK-LABEL: }{{$}}

; Test that the pass handles dead basic blocks with references to the allocation
; CHECK-LABEL: @nopreds
; CHECK: alloca i8, i64 0, align 1
; CHECK: call void @llvm.lifetime.start
define swiftcc { ptr addrspace(10), i8 } @nopreds() {
top:
%0 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr null, i64 0, ptr addrspace(10) null)
%1 = addrspacecast ptr addrspace(10) %0 to ptr addrspace(11)
br label %common.ret

common.ret: ; preds = %union_move9, %top
ret { ptr addrspace(10), i8 } zeroinitializer

union_move9: ; No predecessors!
call void @llvm.memcpy.p0.p11.i64(ptr null, ptr addrspace(11) %1, i64 0, i1 false)
br label %common.ret
}
; CHECK-LABEL: }{{$}}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p11.p0.i64(ptr addrspace(11) noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p11.i64(ptr noalias nocapture writeonly, ptr addrspace(11) noalias nocapture readonly, i64, i1 immarg) #0
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #1 = { allockind("alloc") }
attributes #2 = { allockind("alloc,uninitialized") }
attributes #3 = { allockind("alloc,zeroed") }

0 comments on commit 6ee9546

Please sign in to comment.