Skip to content

Commit cdc2d03

Browse files
jaladreipsigcbot
authored andcommitted
Fix lifetime leaking incorrectly interacting with loops.
A case was found where, for a given alloca, * all users were contained in a single loop (so there are no cases where the lifetime contains an exiting edge), * one of the instructions was leaking the lifetime The result was that the entire loop was included in the lifetime set, but the preheader wasn't. This ended up crashing because there was no single block that could be marked as starting the lifetime. The fix is to make liveness leak analyzing block work on it's own separate sets of basic block, then merge the result with the actual bbIn and bbOut sets.
1 parent a459d3d commit cdc2d03

File tree

3 files changed

+76
-22
lines changed

3 files changed

+76
-22
lines changed

IGC/AdaptorCommon/RayTracing/MergeAllocas.cpp

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,8 @@ AllocationBasedLivenessAnalysis::LivenessData* AllocationBasedLivenessAnalysis::
224224
auto* DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
225225
auto* LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
226226

227-
bool hasNoLifetimeEnd = false;
228-
229227
SetVector<Instruction*> allUsers;
228+
SetVector<Instruction*> lifetimeLeakingUsers;
230229
SmallVector<Use*> worklist;
231230

232231
for (auto& use : I->uses())
@@ -266,31 +265,31 @@ AllocationBasedLivenessAnalysis::LivenessData* AllocationBasedLivenessAnalysis::
266265

267266
break;
268267
case Instruction::PtrToInt:
269-
hasNoLifetimeEnd = true;
268+
lifetimeLeakingUsers.insert(II);
270269
break;
271270
case Instruction::Store:
272271
{
273272
auto* storeI = cast<StoreInst>(II);
274273
if (storeI->getValueOperand() == cast<Value>(use))
275-
hasNoLifetimeEnd = true;
274+
lifetimeLeakingUsers.insert(II);
276275
}
277276
break;
278277
case Instruction::Call:
279278
{
280279
auto* callI = cast<CallInst>(II);
281280
if (!callI->doesNotCapture(use->getOperandNo()))
282-
hasNoLifetimeEnd = true;
281+
lifetimeLeakingUsers.insert(II);
283282
}
284283
break;
285284
case Instruction::Load:
286285
break;
287286
default: // failsafe for handling "unapproved" instructions
288-
hasNoLifetimeEnd = true;
287+
lifetimeLeakingUsers.insert(II);
289288
break;
290289
}
291290
}
292291

293-
return new LivenessData(I, allUsers, *LI, *DT, commonDominator, hasNoLifetimeEnd);
292+
return new LivenessData(I, std::move(allUsers), *LI, *DT, commonDominator, std::move(lifetimeLeakingUsers));
294293
}
295294

296295
template<typename range>
@@ -327,8 +326,14 @@ static inline void doWorkLoop(
327326

328327
}
329328

330-
AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocationInstruction, const SetVector<Instruction*>& usersOfAllocation, const LoopInfo& LI, const DominatorTree& DT, BasicBlock* userDominatorBlock, bool isLifetimeInfinite)
331-
{
329+
AllocationBasedLivenessAnalysis::LivenessData::LivenessData(
330+
Instruction* allocationInstruction,
331+
SetVector<Instruction*>&& usersOfAllocation,
332+
const LoopInfo& LI,
333+
const DominatorTree& DT,
334+
BasicBlock* userDominatorBlock,
335+
SetVector<Instruction*>&& lifetimeLeakingUsers
336+
) {
332337
if (!userDominatorBlock)
333338
userDominatorBlock = allocationInstruction->getParent();
334339

@@ -363,25 +368,32 @@ AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocat
363368
);
364369

365370
// handle infinite lifetime
366-
if (isLifetimeInfinite)
371+
if (!lifetimeLeakingUsers.empty())
367372
{
368373
// traverse all the successors until there are no left.
369-
auto bbInOnly = bbIn;
370-
set_subtract(bbInOnly, bbOut);
371-
372-
for (auto* bb : bbInOnly)
373-
worklist.push_back(bb);
374+
decltype(bbIn) leakingbbIn;
375+
decltype(bbOut) leakingbbOut;
374376

375-
// in case the only use is the one that causes lifetime escape
376-
worklist.push_back(userDominatorBlock);
377+
for (auto* I : lifetimeLeakingUsers)
378+
worklist.push_back(I->getParent());
377379

378380
doWorkLoop<llvm::succ_range>(
379381
worklist,
380-
bbOut,
381-
bbIn,
382+
leakingbbOut,
383+
leakingbbIn,
382384
[&](auto* currbb) { return llvm::successors(currbb); },
383385
[&](auto* currbb) { return false; }
384386
);
387+
388+
// add terminators to users, so we can later add them to our lifetimeEnd vector
389+
auto leakingbbOnlyIn = leakingbbIn;
390+
set_subtract(leakingbbOnlyIn, leakingbbOut);
391+
392+
for (auto* bb : leakingbbOnlyIn)
393+
usersOfAllocation.insert(bb->getTerminator());
394+
395+
set_union(bbIn, leakingbbIn);
396+
set_union(bbOut, leakingbbOut);
385397
}
386398

387399
// if the lifetime escapes any loop, we should make sure all the loops blocks are included
@@ -469,7 +481,7 @@ AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocat
469481
{
470482
for (auto& I : llvm::reverse(*bb))
471483
{
472-
if (usersOfAllocation.contains(&I) || isLifetimeInfinite)
484+
if (usersOfAllocation.contains(&I))
473485
{
474486
lifetimeEnds.push_back(&I);
475487
break;

IGC/AdaptorCommon/RayTracing/MergeAllocas.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ namespace IGC
5656

5757
LivenessData(
5858
llvm::Instruction* allocationInstruction,
59-
const llvm::SetVector<llvm::Instruction*>& usersOfAllocation,
59+
llvm::SetVector<llvm::Instruction*>&& usersOfAllocation,
6060
const llvm::LoopInfo& LI,
6161
const llvm::DominatorTree& DT,
6262
llvm::BasicBlock* userDominatorBlock = nullptr,
63-
bool isLifetimeInfinite = false
63+
llvm::SetVector<llvm::Instruction*>&& lifetimeLeakingUsers = {}
6464
);
6565

6666
bool OverlapsWith(const LivenessData& LD) const;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
;=========================== begin_copyright_notice ============================
2+
;
3+
; Copyright (C) 2025 Intel Corporation
4+
;
5+
; SPDX-License-Identifier: MIT
6+
;
7+
;============================ end_copyright_notice =============================
8+
9+
target datalayout = "e-p:32:32:32-p1:64:64:64-p2:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:32:32-v96:32:32-v128:32:32-a0:0:32-n8:16:32-S32"
10+
target triple = "dxil-ms-dx"
11+
12+
; RUN: igc_opt --opaque-pointers -S --platformbmg --igc-merge-allocas %s | FileCheck %s
13+
14+
; Test if lifetime leaking instruction will interact correctly with loops that contain the entirety of the lifetime
15+
; CHECK-NOT: error
16+
17+
define void @test() #0 {
18+
entry:
19+
%alloca1 = alloca i32
20+
br label %looppreheader
21+
22+
looppreheader:
23+
br label %loopheader
24+
25+
loopheader:
26+
%p2i = ptrtoint ptr %alloca1 to i64
27+
br i1 false, label %loopbody, label %if.end
28+
29+
loopbody:
30+
%a = load i32, ptr %alloca1
31+
store i32 10, ptr %alloca1
32+
br i1 false, label %exit, label %if.end
33+
34+
if.end:
35+
%b = load i32, ptr %alloca1
36+
br label %loopheader
37+
38+
exit:
39+
ret void
40+
}
41+
42+
attributes #0 = { nounwind readnone }

0 commit comments

Comments
 (0)