Files
intel-graphics-compiler/IGC/Compiler/CISACodeGen/LoopDCE.cpp
Paige, Alexander 420b632df9 Update IGC code format
Update IGC code format
2025-07-20 06:20:11 +02:00

552 lines
17 KiB
C++

/*========================== begin_copyright_notice ============================
Copyright (C) 2017-2021 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
#define DEBUG_TYPE "slm-blocking"
#include "Compiler/CISACodeGen/LoopDCE.h"
#include "Compiler/CISACodeGen/ShaderCodeGen.hpp"
#include "Compiler/CodeGenContextWrapper.hpp"
#include "Compiler/MetaDataUtilsWrapper.h"
#include "Compiler/IGCPassSupport.h"
#include "common/LLVMWarningsPush.hpp"
#include <llvm/ADT/DepthFirstIterator.h>
#include <llvm/Analysis/InstructionSimplify.h>
#include <llvm/Analysis/LoopInfo.h>
#include <llvm/Analysis/PostDominators.h>
#include <llvm/Analysis/ValueTracking.h>
#include <llvm/IR/Dominators.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Pass.h>
#include "common/LLVMWarningsPop.hpp"
#include "Probe/Assertion.h"
#include <functional> // for std::function
using namespace llvm;
using namespace IGC;
using namespace IGC::IGCMD;
namespace {
class LoopDeadCodeElimination : public FunctionPass {
CodeGenContext *CGC;
const DataLayout *DL;
LoopInfo *LI;
PostDominatorTree *PDT;
public:
static char ID;
LoopDeadCodeElimination() : FunctionPass(ID), CGC(nullptr), DL(nullptr), LI(nullptr), PDT(nullptr) {
initializeLoopDeadCodeEliminationPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<CodeGenContextWrapper>();
AU.addRequired<MetaDataUtilsWrapper>();
AU.addRequired<PostDominatorTreeWrapperPass>();
AU.addPreserved<PostDominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
AU.addPreserved<LoopInfoWrapperPass>();
}
private:
bool processLoop(Loop *L);
};
/// This is to remove any recursive PHINode. For example,
/// Bx:
/// x = phi [] ...[y, By]
/// By:
/// y = phi [] ... [x, Bx]
/// Both x and y are only used by PHINode, thus they can be
/// removed. This recursive PHINodes happens only if there
/// are loops, and could be introduced in SROA.
///
/// This pass detects the cases above and remove those PHINodes
class DeadPHINodeElimination : public FunctionPass {
public:
static char ID;
DeadPHINodeElimination() : FunctionPass(ID) {
initializeDeadPHINodeEliminationPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<CodeGenContextWrapper>();
AU.addRequired<MetaDataUtilsWrapper>();
AU.addRequired<LoopInfoWrapperPass>();
}
};
} // End anonymous namespace
char LoopDeadCodeElimination::ID = 0;
#define PASS_FLAG "igc-loop-dce"
#define PASS_DESC "Advanced DCE on loop"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
namespace IGC {
IGC_INITIALIZE_PASS_BEGIN(LoopDeadCodeElimination, PASS_FLAG, PASS_DESC, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(CodeGenContextWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
IGC_INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
IGC_INITIALIZE_PASS_END(LoopDeadCodeElimination, PASS_FLAG, PASS_DESC, PASS_CFG_ONLY, PASS_ANALYSIS)
} // namespace IGC
FunctionPass *IGC::createLoopDeadCodeEliminationPass() { return new LoopDeadCodeElimination(); }
bool LoopDeadCodeElimination::runOnFunction(Function &F) {
// Skip non-kernel function.
MetaDataUtils *MDU = nullptr;
MDU = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
auto FII = MDU->findFunctionsInfoItem(&F);
if (FII == MDU->end_FunctionsInfo())
return false;
CGC = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
DL = &F.getParent()->getDataLayout();
LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
PDT = &getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree();
bool Changed = false;
// DFT all loops.
for (auto I = LI->begin(), E = LI->end(); I != E; ++I)
for (auto L = df_begin(*I), F = df_end(*I); L != F; ++L)
Changed |= processLoop(*L);
return Changed;
}
static Instruction *getSingleUserInLoop(Value *V, Loop *L) {
Instruction *UserInLoop = nullptr;
for (auto U : V->users()) {
auto I = dyn_cast<Instruction>(U);
if (!I)
return nullptr;
if (!L->contains(I->getParent()))
continue;
if (UserInLoop)
return nullptr;
UserInLoop = I;
}
return UserInLoop;
}
bool LoopDeadCodeElimination::processLoop(Loop *L) {
SmallVector<BasicBlock *, 8> ExitingBlocks;
L->getExitingBlocks(ExitingBlocks);
if (ExitingBlocks.empty())
return false;
bool Changed = false;
for (auto BB : ExitingBlocks) {
auto BI = dyn_cast<BranchInst>(BB->getTerminator());
// Skip exiting block with non-conditional branch.
if (!BI || !BI->isConditional())
continue;
bool ExitingOnTrue = !L->contains(BI->getSuccessor(0));
auto Cond = BI->getCondition();
for (auto U : Cond->users()) {
auto SI = dyn_cast<SelectInst>(U);
// Check that 'select' instruction is within the loop and also that the
// Branch condition is used as the select's condition and not as the select's
// true or false value
if (!SI || !L->contains(SI->getParent()) || SI->getCondition() != Cond)
continue;
// TODO: Handle the trivial case where 'select' is used as a loop-carried
// value.
auto I = getSingleUserInLoop(SI, L);
if (!I)
continue;
auto PN = dyn_cast<PHINode>(I);
if (!PN || L->getHeader() != PN->getParent())
continue;
// v2 := phi(v0/bb0, v1/bb1)
// ...
// bb1:
// v1 := select(cond, va, vb);
// br cond, out_of_loop
//
// replace all uses of v1 in loop with vb
// replace all uses of v1 out of loop with va
Value *NewValInLoop = SI->getFalseValue();
Value *NewValOutLoop = SI->getTrueValue();
if (!ExitingOnTrue)
std::swap(NewValInLoop, NewValOutLoop);
for (auto UI = SI->use_begin(), UE = SI->use_end(); UI != UE; /*EMPTY*/) {
auto &Use = *UI++;
auto I = cast<Instruction>(Use.getUser());
auto NewVal = L->contains(I->getParent()) ? NewValInLoop : NewValOutLoop;
Use.set(NewVal);
}
}
}
return Changed;
}
/// DeadPHINodeElimination
char DeadPHINodeElimination::ID = 0;
#undef PASS_FLAG
#undef PASS_DESC
#undef PASS_CFG_ONLY
#undef PASS_ANALYSIS
#define PASS_FLAG "igc-phielimination"
#define PASS_DESC "Remove Dead Recurisive PHINode"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
namespace IGC {
IGC_INITIALIZE_PASS_BEGIN(DeadPHINodeElimination, PASS_FLAG, PASS_DESC, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(CodeGenContextWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
IGC_INITIALIZE_PASS_END(DeadPHINodeElimination, PASS_FLAG, PASS_DESC, PASS_CFG_ONLY, PASS_ANALYSIS)
} // namespace IGC
FunctionPass *IGC::createDeadPHINodeEliminationPass() { return new DeadPHINodeElimination(); }
static bool eliminateDeadPHINodes(Function &F) {
// This is to eliminate potential recursive phi like the following:
// Bx: x = phi [y0, B0], ...[z, Bz]
// Bz: z = phi [x, Bx], ...[]
// x and z are recursive phi's that are not used anywhere but in those phi's.
// Thus, they can be eliminated.
auto phiUsedOnlyByPhi = [](PHINode *P) {
bool ret = true;
for (auto U : P->users()) {
Instruction *Inst = dyn_cast<Instruction>(U);
if (!Inst || !isa<PHINode>(Inst)) {
ret = false;
break;
}
}
return ret;
};
DenseMap<PHINode *, int> candidates;
for (auto &BI : F) {
BasicBlock *BB = &BI;
for (auto &II : *BB) {
PHINode *Phi = dyn_cast<PHINode>(&II);
if (Phi && phiUsedOnlyByPhi(Phi)) {
candidates[Phi] = 1;
} else if (!Phi) {
// No more phi, stop looping.
break;
}
}
}
/// Iteratively removing non-candidate PHINode by
/// setting its map value to zero.
bool changed;
do {
changed = false;
for (auto MI = candidates.begin(), ME = candidates.end(); MI != ME; ++MI) {
PHINode *P = MI->first;
if (MI->second == 0)
continue;
for (auto U : P->users()) {
PHINode *phiUser = dyn_cast<PHINode>(U);
IGC_ASSERT_MESSAGE(nullptr != phiUser, "ICE: all candidates should have phi as its users!");
auto iter = candidates.find(phiUser);
if (iter == candidates.end()) {
// not candidate as its user is not in the map.
MI->second = 0;
changed = true;
break;
}
// If it is the user of itself, skip.
if (iter->first == MI->first) {
continue;
}
if (iter->second == 0) {
// not candidate as being used by a non-candidate phi
MI->second = 0;
changed = true;
break;
}
}
}
} while (changed);
// Prepare phi for deletion by setting its operands to null
SmallVector<PHINode *, 8> toBeDeleted;
for (auto &MI : candidates) {
PHINode *P = MI.first;
if (MI.second == 0)
continue;
// reset its operands to zero, which will eventually
// make all dead Phi's uses to be empty!
Value *nilVal = Constant::getNullValue(P->getType());
for (int i = 0, e = (int)P->getNumIncomingValues(); i < e; ++i) {
P->setIncomingValue(i, nilVal);
}
toBeDeleted.push_back(P);
}
// Actually delete them.
for (int i = 0, e = (int)toBeDeleted.size(); i < e; ++i) {
PHINode *P = toBeDeleted[i];
P->eraseFromParent();
}
return (toBeDeleted.size() > 0);
}
using BlockValueMap = DenseMap<const BasicBlock *, PHINode *>;
using BlockValueMapVector = SmallVector<BlockValueMap, 16>;
using BlockValueMapDuplicateIDs = SmallVector<SmallVector<size_t, 2>, 16>;
static BlockValueMapVector getNodeLoops(const Loop *L) {
// Traverse the loop L and collect all the phi nodes
// and their phi node predecessors.
std::function<bool(BlockValueMap &, PHINode *)> CollectConnected = [&CollectConnected](BlockValueMap &BlockMap,
PHINode *P) {
// Check if we have never met phi node parent (basic block) before
auto Inserted = BlockMap.insert(make_pair(P->getParent(), P));
if (Inserted.second) {
// New pair of phi node and basic block inserted
// Traverse node's incoming values
for (auto &V : P->incoming_values()) {
if (auto N = dyn_cast<PHINode>(V))
if (!CollectConnected(BlockMap, N))
return false;
}
// Return true if phi node P and all its incoming phi nodes N
// have been successfully inserted into a block map
return true;
}
// Return true if phi node P have already been inserted into a block map before
return Inserted.first->second == P;
};
BlockValueMapVector BlockMaps;
// Start with phi node in a header
for (auto &I : *L->getHeader())
if (auto P = dyn_cast<PHINode>(&I)) {
BlockValueMap BlockMap;
if (CollectConnected(BlockMap, P)) {
BlockMaps.push_back(BlockMap);
}
}
return BlockMaps;
}
static bool isSameTopology(const BlockValueMap &BlockMapX, const BlockValueMap &BlockMapY) {
// Check if phi node recursive loops x and y
// are of the same topology, e.g. each node in x
// loop has unique node in y loop corresponding
// to the same block.
if (BlockMapX.size() != BlockMapY.size())
return false;
for (auto &PairX : BlockMapX) {
auto BlockX = PairX.first;
auto ValueX = PairX.second;
auto PairY = BlockMapY.find(BlockX);
// Return false if loop of y blocks are different
if (PairY == BlockMapY.end())
return false;
auto ValueY = PairY->second;
// Compare two phi nodes x and y
// Check if every non-phi node incomming value of x
// has matching incoming value of y
// Loop over all incoming values of phi node x
for (unsigned int i = 0; i < ValueX->getNumIncomingValues(); ++i) {
auto IncomingValueX = ValueX->getIncomingValue(i);
auto IncomingBlockX = ValueX->getIncomingBlock(i);
// Nodes with undef incoming value considered being unique,
// because we can't tell for sure if it is a duplitcate or not.
if (isa<UndefValue>(IncomingValueX))
return false;
// For all phi node incoming values of x being part of a loop
// check that there are no matching incoming value in y
// having mismatching incoming block
if (auto IncomingPHINodeX = dyn_cast<PHINode>(IncomingValueX)) {
if (BlockMapX.find(IncomingPHINodeX->getParent()) != BlockMapX.end()) {
for (unsigned int j = 0; j < ValueY->getNumIncomingValues(); ++j) {
auto IncomingValueY = ValueY->getIncomingValue(j);
if (IncomingValueX == IncomingValueY)
return false;
// If one value is recurred value, then the other should be recurred value too.
if ((IncomingValueX == ValueX && IncomingValueY != ValueY) ||
(IncomingValueX != ValueX && IncomingValueY == ValueY))
return false;
}
continue;
}
}
bool Found = false;
// For all other incoming values of x check that there is
// a matching incoming value in y having the same incoming block
for (unsigned int j = 0; j < ValueY->getNumIncomingValues(); ++j) {
auto IncomingValueY = ValueY->getIncomingValue(j);
auto IncomingBlockY = ValueY->getIncomingBlock(j);
if (IncomingValueX == IncomingValueY && IncomingBlockX == IncomingBlockY) {
// Found a match
Found = true;
break;
}
}
// Return false if there is a non-phi node incoming value of phi node x
// having no match among the incoming values of y
if (!Found)
return false;
}
}
return true;
}
static BlockValueMapDuplicateIDs splitLoopsIntoDuplicateGroups(const BlockValueMapVector &BlockMaps) {
// Split all found recursive phi node loops into
// groups of duplicates by comparing each loop
// to all others.
BlockValueMapDuplicateIDs DuplicatesIDs;
if (BlockMaps.size()) {
SmallVector<bool, 16> Found(BlockMaps.size(), false);
for (size_t i = 0; i < BlockMaps.size() - 1; ++i) {
SmallVector<size_t, 2> Duplicates;
for (size_t j = i + 1; j < BlockMaps.size(); ++j)
if (!Found[j] && isSameTopology(BlockMaps[i], BlockMaps[j])) {
Duplicates.push_back(j);
Found[j] = true;
}
if (Duplicates.size()) {
Duplicates.push_back(i);
DuplicatesIDs.push_back(Duplicates);
Found[i] = true;
}
}
}
return DuplicatesIDs;
}
static bool replaceNonPHINodeUses(const BlockValueMapVector &BlockMaps,
const BlockValueMapDuplicateIDs &DuplicatesIDs) {
// Replace all non-phi uses of nodes x with nodes y
// within a group according to the correspondence maps.
bool Changed = false;
for (size_t i = 0; i < DuplicatesIDs.size(); ++i) {
auto &Duplicates = DuplicatesIDs[i];
IGC_ASSERT_MESSAGE(Duplicates.size() >= 2, "Number of duplicates is expected to be at least 2");
// Let y be the first loop
auto &BlockMapY = BlockMaps[Duplicates[0]];
for (auto &PairY : BlockMapY) {
auto BlockY = PairY.first;
auto ValueY = PairY.second;
// All other loops are considered x
for (size_t j = 1; j < Duplicates.size(); ++j) {
auto &BlockMapX = BlockMaps[Duplicates[j]];
auto PairX = BlockMapX.find(BlockY);
IGC_ASSERT_MESSAGE(PairX != BlockMapX.end(), "Inconsistent duplicate maps");
auto ValueX = PairX->second;
if (ValueX != ValueY) {
ValueX->replaceAllUsesWith(ValueY);
Changed = true;
}
}
}
}
return Changed;
}
static bool resolveDuplicateLoops(Function &F, const Loop *L) {
// This is to eliminate duplicate recursive phis like the following:
// B0: x0 = phi [v, ...], [xN, BN]
// B0: y0 = phi [v, ...], [yN, BN]
// ...
// B1: x1 = phi [x0, B0], ...
// B1: y1 = phi [y0, B0], ...
// ...
// t = add %x1, 1.0
// ...
// B2: x2 = phi [t, ...], [x1, B1]
// B2: y2 = phi [t, ...], [y1, B1]
// ...
// store yN ...
// xi are recursive duplicate of yi, and are not used anywhere but in those phis.
// Thus, they can be eliminated.
// Find all recursive phis within current loop L:
// loops of xi, yi, etc.
auto BlockMaps = getNodeLoops(L);
// Group all recursive phis within current loop L
// into duplicate groups:
// xi duplicates yi, so put then into the same group
auto DuplicatesIDs = splitLoopsIntoDuplicateGroups(BlockMaps);
// Replace x1 with y1 in t = add %x1, 1.0,
// so all xi become unused anywhere except in xi
// Unused phi nodes will be eliminated by upcomming
// call to eliminateDeadPHINodes(F)
return replaceNonPHINodeUses(BlockMaps, DuplicatesIDs);
}
bool DeadPHINodeElimination::runOnFunction(Function &F) {
bool Changed = false;
if (eliminateDeadPHINodes(F))
Changed = true;
auto LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
for (auto &L : LI->getLoopsInPreorder()) {
if (resolveDuplicateLoops(F, L))
Changed = true;
if (eliminateDeadPHINodes(F))
Changed = true;
}
return Changed;
}