Skip to content

Commit 290c619

Browse files
authored
AllocOpt: Handle objref with no preserve_end in a single block. (#50277)
This fixes the AllocOpt pass when there is a single block and no gc_preserve_end. The dominator tree traversal now considers the starting (gc_preverse_begin) block as well and does not introduce a lifetime end when there is no gc_preserve_end for the objref.
1 parent 3b854f4 commit 290c619

File tree

2 files changed

+53
-21
lines changed

2 files changed

+53
-21
lines changed

src/llvm-alloc-opt.cpp

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -433,36 +433,45 @@ void Optimizer::insertLifetime(Value *ptr, Constant *sz, Instruction *orig)
433433
abort();
434434
}
435435
#endif
436-
// Record extra BBs that contain invisible uses.
436+
437+
// Record extra BBs that contain invisible uses with gc_preserve_{begin,end}.
438+
// We traverse the dominator tree starting at each `gc_preserve_begin` and marking blocks
439+
// as users until a corresponding `gc_preserve_end` is found. Blocks containing
440+
// the `gc_preserve_end` have already been marked in the previous step.
437441
SmallSet<BasicBlock*, 8> extra_use;
438442
SmallVector<DomTreeNodeBase<BasicBlock>*, 8> dominated;
439443
for (auto preserve: use_info.preserves) {
440-
for (auto RN = DT.getNode(preserve->getParent()); RN;
441-
RN = dominated.empty() ? nullptr : dominated.pop_back_val()) {
442-
for (auto N: *RN) {
443-
auto bb = N->getBlock();
444-
if (extra_use.count(bb))
445-
continue;
446-
bool ended = false;
447-
for (auto end: preserve->users()) {
448-
auto end_bb = cast<Instruction>(end)->getParent();
449-
auto end_node = DT.getNode(end_bb);
450-
if (end_bb == bb || (end_node && DT.dominates(end_node, N))) {
451-
ended = true;
452-
break;
453-
}
444+
assert(dominated.empty());
445+
dominated.push_back(DT.getNode(preserve->getParent()));
446+
while (!dominated.empty()) {
447+
auto N = dominated.pop_back_val();
448+
if (!N) {
449+
dominated.clear();
450+
break;
451+
}
452+
auto bb = N->getBlock();
453+
if (extra_use.count(bb))
454+
continue;
455+
bool ended = false;
456+
for (auto end: preserve->users()) {
457+
auto end_bb = cast<Instruction>(end)->getParent();
458+
auto end_node = DT.getNode(end_bb);
459+
if (end_bb == bb || (end_node && DT.dominates(end_node, N))) {
460+
ended = true;
461+
break;
454462
}
455-
if (ended)
456-
continue;
457-
bbs.insert(bb);
458-
extra_use.insert(bb);
459-
dominated.push_back(N);
460463
}
464+
if (ended)
465+
continue;
466+
bbs.insert(bb);
467+
extra_use.insert(bb);
468+
dominated.append(N->begin(), N->end());
461469
}
462-
assert(dominated.empty());
463470
}
471+
464472
// For each BB, find the first instruction(s) where the allocation is possibly dead.
465473
// If all successors are live, then there isn't one.
474+
// If the BB has "invisible" uses, then there isn't one.
466475
// If all successors are dead, then it's the first instruction after the last use
467476
// within the BB.
468477
// If some successors are live and others are dead, it's the first instruction in

test/llvmpasses/alloc-opt-pass.ll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ declare {}*** @julia.get_pgcstack()
103103
declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, i64, {} addrspace(10)*)
104104
declare {}* @julia.pointer_from_objref({} addrspace(11)*)
105105
declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1)
106+
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1)
106107
declare token @llvm.julia.gc_preserve_begin(...)
107108
declare void @llvm.julia.gc_preserve_end(token)
108109

@@ -139,3 +140,25 @@ L2:
139140
ret void
140141
}
141142
; CHECK-LABEL: }{{$}}
143+
144+
; CHECK-LABEL: @lifetime_no_preserve_end
145+
; CHECK: alloca
146+
; CHECK-NOT: call token(...) @llvm.julia.gc_preserve_begin
147+
; CHECK: call void @llvm.lifetime.start
148+
; CHECK-NOT: call void @llvm.lifetime.end
149+
define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret({}) %0) {
150+
%pgcstack = call {}*** @julia.get_pgcstack()
151+
%ptls = call {}*** @julia.ptls_states()
152+
%ptls_i8 = bitcast {}*** %ptls to i8*
153+
%v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag)
154+
%token = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v)
155+
%v_derived = addrspacecast {} addrspace(10)* %v to {} addrspace(11)*
156+
%ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %v_derived)
157+
%ptr_raw = bitcast {}* %ptr to i8*
158+
call void @external_function() ; safepoint
159+
%ret_raw = bitcast {}* %0 to i8*
160+
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %ret_raw, i8 * align 8 %ptr_raw, i64 0, i1 false)
161+
%ret_raw2 = bitcast {}* %0 to i8*
162+
ret void
163+
}
164+
; CHECK-LABEL: }{{$}}

0 commit comments

Comments
 (0)