Files
intel-graphics-compiler/IGC/Compiler/CISACodeGen/GenCodeGenModule.h
Milczek, Szymon 9ae78bef34 Apply rule-of-three
Applies rule-of-three by removing missing copy ctors.
2025-08-20 11:36:17 +02:00

340 lines
12 KiB
C++

/*========================== begin_copyright_notice ============================
Copyright (C) 2017-2023 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
#pragma once
#include "Compiler/MetaDataApi/MetaDataApi.h"
#include "common/LLVMWarningsPush.hpp"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/CallGraph.h"
#include "common/LLVMWarningsPop.hpp"
#include "common/Types.hpp"
#include "Probe/Assertion.h"
namespace IGC {
class GenXFunctionGroupAnalysis;
/// \brief A module pass to initialize GenXFunctionGroupAnalysis and sort function
/// list in a module properly.
///
/// The module pass's results have two parts:
///
/// (1) GenXFunctionGroupAnalysis object, which stores information needed for vISA
/// emission. E.g, each non-empty function belongs to a uniquely defined
/// function group with a kernel function as the head.
///
/// (2) The module itself. All functions reachable from different function
/// groups will be cloned if necessary; they will be ordered such that each
/// callee will be after the caller in the module function list. When clone
/// happens, this module pass returns true, otherwise it returns false.
/// Currently, we assume no kernel functions will be called. This
/// requirement could be enforced before this pass by inlining kernels.
///
class GenXCodeGenModule : public llvm::ModulePass {
public:
static char ID;
GenXCodeGenModule();
~GenXCodeGenModule();
GenXCodeGenModule(const GenXCodeGenModule &) = delete;
GenXCodeGenModule &operator=(const GenXCodeGenModule &) = delete;
virtual llvm::StringRef getPassName() const override { return "GenX CodeGen module"; }
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
bool runOnModule(llvm::Module &M) override;
private:
void processFunction(llvm::Function &F);
void processSCC(std::vector<llvm::CallGraphNode *> *SCCNodes);
void setFuncProperties(llvm::CallGraph &CG);
void copyFuncProperties(llvm::Function *To, llvm::Function *From);
GenXFunctionGroupAnalysis *FGA;
IGC::IGCMD::MetaDataUtils *pMdUtils;
bool Modified;
unsigned m_FunctionCloningThreshold = 0;
// Find the set of functions that cannot be promoted to indirect, must be cloned
void detectUnpromotableFunctions(llvm::Module *pM);
std::set<llvm::Function *> m_UnpromotableFuncs;
};
/// \brief A collection of functions that are reachable from a kernel.
class FunctionGroup {
public:
friend class GenXFunctionGroupAnalysis;
/// \brief use 2-d vector of Functions to represent FunctionGroup.
/// Each sub-vector is a subgroup led by a kernel or a stack-call function.
/// Element [0][0] is the group head. Element[i][0] is the sub-group head.
typedef llvm::SmallVector<llvm::AssertingVH<llvm::Function>, 8> SubGroupContainer;
typedef llvm::SmallVector<SubGroupContainer *, 4> FunctionGroupContainer;
FunctionGroupContainer Functions;
class iterator {
public:
enum POS { BEGIN, END };
iterator(FunctionGroupContainer &FC, POS pos) {
if (pos == BEGIN) {
major = FC.begin();
majorEnd = FC.end();
minor = (*major)->begin();
} else if (pos == END) {
major = FC.end();
majorEnd = FC.end();
minor = FC.back()->end();
}
}
iterator &operator++() {
minor++;
if (minor == (*major)->end()) {
major++;
if (major != majorEnd)
minor = (*major)->begin();
}
return *this;
}
llvm::Function *operator*() { return *minor; }
bool operator==(const iterator &rhs) const { return (major == rhs.major && minor == rhs.minor); }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
private:
FunctionGroupContainer::iterator major;
FunctionGroupContainer::iterator majorEnd;
SubGroupContainer::iterator minor;
};
iterator begin() { return iterator(Functions, iterator::BEGIN); }
iterator end() { return iterator(Functions, iterator::END); }
~FunctionGroup() {
for (auto F : Functions) {
delete F;
}
}
FunctionGroup() = default;
FunctionGroup(const FunctionGroup &) = delete;
FunctionGroup &operator=(const FunctionGroup &) = delete;
/// \brief The entry kernel function of group.
llvm::Function *getHead() const { return Functions.front()->front(); }
/// \brief The tail function of a group.
llvm::Function *back() const { return Functions.back()->back(); }
/// \brief Only one function in this group
bool isSingle() const { return (Functions.size() == 1 && Functions.front()->size() == 1); }
/// \brief Only one function in this group ignoring stack overflow detection methods
bool isSingleIgnoringStackOverflowDetection() const {
auto isNotStackOverflowDetection = [](const llvm::Function *F) {
return !F->getName().startswith("__stackoverflow_detection") && !F->getName().startswith("__stackoverflow_init");
};
return (Functions.size() == 1 &&
std::count_if(Functions.front()->begin(), Functions.front()->end(), isNotStackOverflowDetection) == 1);
}
/// \brief Function group has a subroutine
bool hasSubroutine() const { return m_hasSubroutine; }
/// \brief Function group has a stack call (including indirect calls)
bool hasStackCall() const { return m_hasStackCall; }
bool hasInlineAsm() const { return m_hasInlineAsm; }
/// \brief Function group has a variable length alloca
bool hasVariableLengthAlloca() const { return m_hasVariableLengthAlloca; }
/// \brief Function group has indirect calls (i.e. calls functions in the Indirect Function Group)
bool hasIndirectCall() const { return m_hasIndirectCall; }
/// \brief Function group has nested calls
bool hasNestedCall() const { return m_hasNestedCall; }
/// \brief Function group has indirect calls where the full CG is not available (e.g. calls function pointer, or
/// callees in external module)
bool hasPartialCallGraph() const { return m_hasPartialCallGraph; }
/// \brief Function group has recursion
bool hasRecursion() const { return m_hasRecursion; }
void replaceGroupHead(llvm::Function *OH, llvm::Function *NH) {
IGC_UNUSED(OH);
auto headSG = Functions[0];
llvm::AssertingVH<llvm::Function> &HVH = (*headSG)[0];
IGC_ASSERT_MESSAGE(&(*HVH) == OH, "Group's head isn't set up correctly!");
HVH = NH;
}
// For a single FG, an SIMD mode is valid only if SIMD modes of all
// functions in that group are valid.
bool checkSimdModeValid(SIMDMode Mode) const;
void setSimdModeInvalid(SIMDMode Mode);
private:
bool m_hasSubroutine = false;
bool m_hasStackCall = false;
bool m_hasInlineAsm = false;
bool m_hasVariableLengthAlloca = false;
bool m_hasIndirectCall = false;
bool m_hasRecursion = false;
bool m_hasNestedCall = false;
bool m_hasPartialCallGraph = false;
bool SIMDModeValid[3] = {true, true, true};
};
class GenXFunctionGroupAnalysis : public llvm::ImmutablePass {
enum class FuncPropertyInfo : uint32_t {
FPI_LEAF = 0x1 // bit 0: 1 (leaf function)
};
/// \brief The module being analyzed.
llvm::Module *M;
/// \brief Function groups constructed in this module.
llvm::SmallVector<FunctionGroup *, 8> Groups;
/// \brief Each function belongs to a uniquely defined function group.
llvm::DenseMap<const llvm::Function *, FunctionGroup *> GroupMap;
/// \brief Each function also belongs to a uniquely defined sub-group of a stack-call entry
llvm::DenseMap<const llvm::Function *, llvm::Function *> SubGroupMap;
/// \brief Properties for each function
llvm::DenseMap<const llvm::Function *, uint32_t> FuncProperties;
/// \brief Special group that contains all indirect call functions
/// SIMD variants stored with SIMD8 in idx 0, SIMD16 in idx 1, SIMD32 in idx 2
llvm::SmallVector<FunctionGroup *, 3> IndirectCallGroup{nullptr, nullptr, nullptr};
public:
static char ID;
explicit GenXFunctionGroupAnalysis();
~GenXFunctionGroupAnalysis() { clear(); }
GenXFunctionGroupAnalysis(const GenXFunctionGroupAnalysis &) = delete;
GenXFunctionGroupAnalysis &operator=(const GenXFunctionGroupAnalysis &) = delete;
virtual llvm::StringRef getPassName() const override { return "FunctionGroup analysis"; }
/// This function returns nullptr if no analysis is not available.
llvm::Module *getModule() { return M; }
void setModule(llvm::Module *Mod) { M = Mod; }
void clear();
/// \brief This function rebuilds function groups with the assumption that
/// no function will be directly or undirectly called from more than one
/// kernel, and functions in the module are well-ordered. It returns false if
/// fails to rebuild and returns true otherwise.
///
bool rebuild(llvm::Module *Mod);
// Attach all indirectly called functions to a single kernel group
void addIndirectFuncsToKernelGroup(llvm::Module *pModule);
// Replace OF with NF in Groups and GroupMap
void replaceEntryFunc(llvm::Function *OF, llvm::Function *NF);
/// \brief Verify if this analysis result is valid.
bool verify();
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
/// \brief Get the FunctionGroup containing Function F, else nullptr.
FunctionGroup *getGroup(const llvm::Function *F);
/// \brief Get the FunctionGroup for which Function F is the head, else
/// nullptr.
FunctionGroup *getGroupForHead(const llvm::Function *F);
/// \brief Get the group head for the group to which F belongs.
llvm::Function *getGroupHead(const llvm::Function *F) {
auto FG = getGroup(F);
IGC_ASSERT(nullptr != FG);
return FG->getHead();
}
/// \brief Get the subgroup head for the subgroup to which F belongs
llvm::Function *getSubGroupMap(llvm::Function *F) {
auto I = SubGroupMap.find(F);
if (I == SubGroupMap.end())
return nullptr;
return I->second;
}
void setSubGroupMap(llvm::Function *F, llvm::Function *SubGroupHead) { SubGroupMap[F] = SubGroupHead; }
bool isIndirectCallGroup(const FunctionGroup *FG) {
for (auto ICG : IndirectCallGroup) {
if (FG && FG == ICG)
return true;
}
return false;
}
bool isIndirectCallGroup(const llvm::Function *F) {
IGC_ASSERT(F);
FunctionGroup *FG = getGroup(F);
return isIndirectCallGroup(FG);
}
/// \brief Check whether this is a group header.
bool isGroupHead(const llvm::Function *F) { return getGroupForHead(F) != nullptr; }
/// \brief Check whether this is the last function in a function group. This
/// order is also reflected in the module function list.
bool isGroupTail(const llvm::Function *F) {
FunctionGroup *FG = getGroup(F);
IGC_ASSERT_MESSAGE(nullptr != FG, "not in function group");
return F == FG->back();
}
bool isLeafFunc(const llvm::Function *F) {
auto FI = FuncProperties.find(F);
if (FI != FuncProperties.end()) {
return (FI->second & (uint32_t)FuncPropertyInfo::FPI_LEAF);
}
return false;
}
void setLeafFunc(const llvm::Function *F) {
auto II = FuncProperties.find(F);
uint32_t P = (II != FuncProperties.end()) ? II->second : 0;
FuncProperties[F] = (P | (uint32_t)FuncPropertyInfo::FPI_LEAF);
}
void copyFuncProperties(const llvm::Function *To, const llvm::Function *From) {
auto II = FuncProperties.find(From);
if (II != FuncProperties.end()) {
FuncProperties[To] = II->second;
}
}
/// get or create the function group that holds all indirectly-called functions
FunctionGroup *getOrCreateIndirectCallGroup(llvm::Module *pModule, int SimdSize = 0);
/// for multi SIMD variant compilation, clone functions from the default dummy kernel group
/// into the SIMD variant version of the dummy kernel.
void CloneFunctionGroupForMultiSIMDCompile(llvm::Module *pModule);
/// check if function is stack-called
bool useStackCall(llvm::Function *F);
/// sets function group attribute flags
void setGroupAttributes();
typedef llvm::SmallVectorImpl<FunctionGroup *>::iterator iterator;
iterator begin() { return iterator(Groups.begin()); }
iterator end() { return iterator(Groups.end()); }
/// \brief Returns the number of groups, aka. kernels.
size_t size() { return Groups.size(); }
/// \brife add Function F to FunctionGroup FG.
void addToFunctionGroup(llvm::Function *F, FunctionGroup *FG, llvm::Function *SubGrpH);
/// \brief Create a new FunctionGroup with head F.
FunctionGroup *createFunctionGroup(llvm::Function *F);
void print(llvm::raw_ostream &os);
#if defined(_DEBUG)
void dump();
#endif
};
llvm::ModulePass *createGenXCodeGenModulePass();
llvm::ImmutablePass *createGenXFunctionGroupAnalysisPass();
llvm::Pass *createSubroutineInlinerPass();
} // namespace IGC