Skip to content

Commit 460dcdc

Browse files
jaladreipsigcbot
authored andcommitted
Reimplement inline raytracing with multiple HW stacks
Reimplement inline raytracing with multiple HW stacks
1 parent cd15d75 commit 460dcdc

19 files changed

+1627
-187
lines changed

IGC/AdaptorCommon/LivenessUtils/AllocationLivenessAnalyzer.cpp

Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ SPDX-License-Identifier: MIT
3232
using namespace llvm;
3333
using namespace IGC;
3434

35-
AllocationLivenessAnalyzer::LivenessData AllocationLivenessAnalyzer::ProcessInstruction(Instruction* I)
35+
AllocationLivenessAnalyzer::LivenessData AllocationLivenessAnalyzer::ProcessInstruction(
36+
Instruction* I,
37+
DominatorTree& DT,
38+
LoopInfo& LI
39+
)
3640
{
3741
// static allocas are usually going to be in the entry block
3842
// that's a practice, but we only care about the last block that dominates all uses
3943
BasicBlock* commonDominator = nullptr;
40-
auto* DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
41-
auto* LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
42-
4344
SetVector<Instruction*> allUsers;
4445
SetVector<Instruction*> lifetimeLeakingUsers;
4546
SmallVector<Use*> worklist;
@@ -49,7 +50,7 @@ AllocationLivenessAnalyzer::LivenessData AllocationLivenessAnalyzer::ProcessInst
4950
auto* UasI = cast<Instruction>(use.getUser());
5051
if (commonDominator)
5152
{
52-
commonDominator = DT->findNearestCommonDominator(commonDominator, UasI->getParent());
53+
commonDominator = DT.findNearestCommonDominator(commonDominator, UasI->getParent());
5354
}
5455
else
5556
{
@@ -59,53 +60,76 @@ AllocationLivenessAnalyzer::LivenessData AllocationLivenessAnalyzer::ProcessInst
5960
worklist.push_back(&use);
6061
}
6162

63+
auto addUsesFn = [&worklist](auto uses) {
64+
for (auto& use : uses)
65+
worklist.push_back(&use);
66+
};
67+
6268
// figure out the potential accesses to the memory via GEP and bitcasts
6369
while (!worklist.empty())
6470
{
6571
auto* use = worklist.pop_back_val();
6672
auto* II = cast<Instruction>(use->getUser());
6773

68-
if (allUsers.contains(II))
74+
if (!allUsers.insert(II))
6975
continue;
7076

71-
allUsers.insert(II);
77+
// a possible optimization here:
78+
// 1. find all reachable blocks
79+
// 2. cull uses that are not reachable from the allocation
80+
81+
commonDominator = DT.findNearestCommonDominator(commonDominator, II->getParent());
7282

7383
switch (II->getOpcode())
7484
{
7585
case Instruction::PHI:
7686
case Instruction::GetElementPtr:
7787
case Instruction::BitCast:
7888
case Instruction::Select:
79-
for (auto& use : II->uses())
80-
worklist.push_back(&use);
81-
89+
addUsesFn(II->uses());
8290
break;
8391
case Instruction::PtrToInt:
8492
lifetimeLeakingUsers.insert(II);
8593
break;
8694
case Instruction::Store:
8795
{
8896
auto* storeI = cast<StoreInst>(II);
89-
if (storeI->getValueOperand() == cast<Value>(use))
90-
lifetimeLeakingUsers.insert(II);
97+
if (storeI->getValueOperand() == use->get())
98+
{
99+
SmallVector<Instruction*> origins;
100+
if (Provenance::tryFindPointerOrigin(storeI->getPointerOperand(), origins))
101+
{
102+
for (auto* origin : origins)
103+
addUsesFn(origin->uses());
104+
}
105+
else
106+
{
107+
lifetimeLeakingUsers.insert(II);
108+
}
109+
}
91110
}
92111
break;
93112
case Instruction::Call:
94113
{
95114
auto* callI = cast<CallInst>(II);
96115
if (!callI->doesNotCapture(use->getOperandNo()))
97116
lifetimeLeakingUsers.insert(II);
117+
118+
if (II->getType()->isPointerTy())
119+
addUsesFn(II->uses());
98120
}
99121
break;
100122
case Instruction::Load:
123+
if (II->getType()->isPointerTy())
124+
addUsesFn(II->uses());
101125
break;
102126
default: // failsafe for handling "unapproved" instructions
103127
lifetimeLeakingUsers.insert(II);
104128
break;
105129
}
106130
}
107131

108-
return LivenessData(I, std::move(allUsers), *LI, *DT, commonDominator, std::move(lifetimeLeakingUsers));
132+
return LivenessData(I, std::move(allUsers), LI, DT, commonDominator, std::move(lifetimeLeakingUsers));
109133
}
110134

111135
void AllocationLivenessAnalyzer::getAnalysisUsage(llvm::AnalysisUsage& AU) const
@@ -264,6 +288,8 @@ AllocationLivenessAnalyzer::LivenessData::LivenessData(
264288
}
265289
}
266290

291+
// at this point we have all the blocks we need, so fill out the start/end data
292+
267293
// substract the inflow blocks from the outflow blocks to find the block which starts the lifetime - there should be only one!
268294
auto bbOutOnly = bbOut;
269295
set_subtract(bbOutOnly, bbIn);
@@ -287,7 +313,7 @@ AllocationLivenessAnalyzer::LivenessData::LivenessData(
287313
{
288314
if (usersOfAllocation.contains(&I))
289315
{
290-
lifetimeEnds.push_back(&I);
316+
lifetimeEndInstructions.push_back(&I);
291317
break;
292318
}
293319
}
@@ -297,6 +323,7 @@ AllocationLivenessAnalyzer::LivenessData::LivenessData(
297323
}
298324
else
299325
{
326+
// find all blocks where lifetime flows in, but doesnt flow out
300327
auto bbOnlyIn = bbIn;
301328
set_subtract(bbOnlyIn, bbOut);
302329

@@ -306,12 +333,29 @@ AllocationLivenessAnalyzer::LivenessData::LivenessData(
306333
{
307334
if (usersOfAllocation.contains(&I))
308335
{
309-
lifetimeEnds.push_back(&I);
336+
lifetimeEndInstructions.push_back(&I);
310337
break;
311338
}
312339
}
313340
}
314341
}
342+
343+
// collect lifetime end edges (where outflow block has successors that aren't inflow blocks)
344+
for (auto* bb : bbOut)
345+
{
346+
// however, we can't just add successors
347+
// because then we can accidentally execute lifetime end instruction twice
348+
// which can end up causing issues similar to double-free
349+
// we need to make sure every successor has a single predecessor
350+
SmallVector<BasicBlock*> successors(llvm::successors(bb));
351+
for (auto* succ : successors)
352+
{
353+
if (bbIn.contains(succ))
354+
continue;
355+
356+
lifetimeEndEdges.push_back({ bb, succ });
357+
}
358+
}
315359
}
316360

317361
bool AllocationLivenessAnalyzer::LivenessData::OverlapsWith(const LivenessData& LD) const
@@ -330,7 +374,7 @@ bool AllocationLivenessAnalyzer::LivenessData::OverlapsWith(const LivenessData&
330374
for (auto& [LD1, LD2] : { std::make_pair(this, &LD), std::make_pair(&LD, this) })
331375
{
332376
// TODO: replace the whole logic with ContainsInstruction checks
333-
for (auto* I : LD1->lifetimeEnds)
377+
for (auto* I : LD1->lifetimeEndInstructions)
334378
{
335379
// what if LD1 is contained in a single block
336380
if (I->getParent() == LD1->lifetimeStart->getParent())
@@ -341,7 +385,7 @@ bool AllocationLivenessAnalyzer::LivenessData::OverlapsWith(const LivenessData&
341385
bool lifetimeStart = LD2->lifetimeStart->getParent() == bb && LD2->lifetimeStart->comesBefore(I);
342386

343387
auto* LD1_lifetimeStart = LD1->lifetimeStart; // we have to copy LD1.lifetimeStart to avoid clang complaining about LD1 being captured by the lambda
344-
bool lifetimeEnd = any_of(LD2->lifetimeEnds, [&](auto* lifetimeEnd) {
388+
bool lifetimeEnd = any_of(LD2->lifetimeEndInstructions, [&](auto* lifetimeEnd) {
345389
return lifetimeEnd->getParent() == bb && LD1_lifetimeStart->comesBefore(lifetimeEnd);
346390
});
347391

@@ -381,7 +425,7 @@ bool AllocationLivenessAnalyzer::LivenessData::ContainsInstruction(const llvm::I
381425
if (I.comesBefore(lifetimeStart))
382426
return false;
383427

384-
if (lifetimeEnds[0]->comesBefore(&I))
428+
if (lifetimeEndInstructions[0]->comesBefore(&I))
385429
return false;
386430

387431
return true;
@@ -396,9 +440,54 @@ bool AllocationLivenessAnalyzer::LivenessData::ContainsInstruction(const llvm::I
396440
if (lifetimeStart->getParent() == bb && !I.comesBefore(lifetimeStart))
397441
return true;
398442

399-
bool overlapsWithEnd = any_of(lifetimeEnds, [&](auto* lifetimeEnd) {
443+
bool overlapsWithEnd = any_of(lifetimeEndInstructions, [&](auto* lifetimeEnd) {
400444
return lifetimeEnd->getParent() == bb && !lifetimeEnd->comesBefore(&I);
401445
});
402446

403447
return overlapsWithEnd;
404448
}
449+
450+
namespace IGC
451+
{
452+
namespace Provenance
453+
{
454+
static bool tryFindPointerOriginImpl(Value* ptr, SmallVectorImpl<Instruction*>& origins, DenseSet<Value*>& cache);
455+
456+
bool tryFindPointerOrigin(Value* ptr, SmallVectorImpl<Instruction*>& origins)
457+
{
458+
origins.clear();
459+
460+
DenseSet<Value*> cache;
461+
bool found = tryFindPointerOriginImpl(ptr, origins, cache);
462+
463+
IGC_ASSERT_MESSAGE(found && !origins.empty(), "Origin reported as found but no origins were added!");
464+
465+
return found;
466+
}
467+
468+
static bool tryFindPointerOrigin(GetElementPtrInst* Ptr, SmallVectorImpl<Instruction*>& origins, DenseSet<Value*>& cache)
469+
{
470+
return tryFindPointerOriginImpl(Ptr->getPointerOperand(), origins, cache);
471+
}
472+
473+
static bool tryFindPointerOriginImpl(Value* ptr, SmallVectorImpl<Instruction*>& origins, DenseSet<Value*>& cache)
474+
{
475+
if (!cache.insert(ptr).second)
476+
return true;
477+
478+
if (auto* GEP = dyn_cast<GetElementPtrInst>(ptr))
479+
{
480+
return tryFindPointerOrigin(GEP, origins, cache);
481+
}
482+
483+
if (auto* allocaI = dyn_cast<AllocaInst>(ptr))
484+
{
485+
origins.push_back(allocaI);
486+
return true;
487+
}
488+
489+
return false;
490+
}
491+
492+
}
493+
}

IGC/AdaptorCommon/LivenessUtils/AllocationLivenessAnalyzer.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ namespace IGC
2727
public:
2828
struct LivenessData {
2929
llvm::Instruction* lifetimeStart = nullptr;
30-
llvm::SmallVector<llvm::Instruction*> lifetimeEnds;
30+
llvm::SmallVector<llvm::Instruction*> lifetimeEndInstructions;
31+
32+
struct Edge
33+
{
34+
llvm::BasicBlock* from;
35+
llvm::BasicBlock* to;
36+
};
37+
38+
llvm::SmallVector<Edge> lifetimeEndEdges;
3139

3240
llvm::DenseSet<llvm::BasicBlock*> bbIn;
3341
llvm::DenseSet<llvm::BasicBlock*> bbOut;
@@ -48,9 +56,19 @@ namespace IGC
4856
AllocationLivenessAnalyzer(char& pid) : llvm::FunctionPass(pid) {}
4957

5058
protected:
51-
LivenessData ProcessInstruction(llvm::Instruction* I);
59+
LivenessData ProcessInstruction(
60+
llvm::Instruction* I,
61+
llvm::DominatorTree& DT,
62+
llvm::LoopInfo& LI
63+
);
5264

5365
void getAnalysisUsage(llvm::AnalysisUsage& AU) const override;
5466
virtual void getAdditionalAnalysisUsage(llvm::AnalysisUsage& AU) const = 0;
5567
};
68+
69+
namespace Provenance
70+
{
71+
bool tryFindPointerOrigin(llvm::Value* ptr, llvm::SmallVectorImpl<llvm::Instruction*>& origins);
72+
}
73+
5674
} // namespace IGC

IGC/AdaptorCommon/LivenessUtils/MergeAllocas.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ bool MergeAllocas::runOnFunction(Function &F) {
179179
return false;
180180
}
181181

182+
auto& DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
183+
auto& LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
184+
182185
// in the past, the analysis pass used to be responsible for the liveness data objects
183186
// the pass got deleted as a part of refactor (it was leaking memory anyway),
184187
// so to avoid changing the logic, we create a backing storage for the liveness data objects
@@ -190,7 +193,7 @@ bool MergeAllocas::runOnFunction(Function &F) {
190193

191194
for (auto& I : make_filter_range(instructions(F), [](auto& I) { return isa<AllocaInst>(&I); }))
192195
{
193-
storage.push_back(ProcessInstruction(&I));
196+
storage.push_back(ProcessInstruction(&I, DT, LI));
194197
ABLA.push_back(std::make_pair(&I, &storage.back()));
195198
}
196199

0 commit comments

Comments
 (0)