rpcsx/rpcsx-gpu/lib/gcn-shader/include/shader/analyze.hpp
2024-10-01 22:04:40 +03:00

447 lines
13 KiB
C++

#pragma once
#include "ModuleInfo.hpp"
#include "SemanticInfo.hpp"
#include "dialect/memssa.hpp"
#include "graph.hpp"
#include "ir/Instruction.hpp"
#include "ir/Value.hpp"
#include "rx/FunctionRef.hpp"
#include "rx/TypeId.hpp"
#include <map>
#include <ostream>
#include <unordered_set>
#include <utility>
#include <vector>
namespace shader {
struct DomTree;
struct PostDomTree;
class CFG {
public:
class Node {
ir::Value mLabel;
ir::Instruction mTerminator;
std::unordered_set<Node *> mPredecessors;
std::unordered_set<Node *> mSuccessors;
public:
using Iterator = std::unordered_set<Node *>::iterator;
Node() = default;
Node(ir::Value label) : mLabel(label) {}
ir::Value getLabel() { return mLabel; }
void setTerminator(ir::Instruction inst) { mTerminator = inst; }
bool hasTerminator() { return mTerminator != nullptr; }
ir::Instruction getTerminator() { return mTerminator; }
void addEdge(Node *to) {
to->mPredecessors.insert(this);
mSuccessors.insert(to);
}
bool hasPredecessor(Node *node) { return mPredecessors.contains(node); }
bool hasSuccessor(Node *node) { return mSuccessors.contains(node); }
auto &getPredecessors() { return mPredecessors; }
auto &getSuccessors() { return mSuccessors; }
std::size_t getPredecessorCount() { return mPredecessors.size(); }
std::size_t getSuccessorCount() { return mSuccessors.size(); }
bool hasPredecessors() { return !mPredecessors.empty(); }
bool hasSuccessors() { return !mSuccessors.empty(); }
template <typename T = ir::Instruction> auto range() {
return ir::range<T>(mLabel, mTerminator.getNext());
}
template <typename T = ir::Instruction> auto rangeWithoutLabel() {
return ir::range<T>(mLabel.getNext(),
mTerminator ? mTerminator.getNext() : nullptr);
}
template <typename T = ir::Instruction> auto rangeWithoutTerminator() {
return ir::range<T>(mLabel, mTerminator);
}
template <typename T = ir::Instruction>
auto rangeWithoutLabelAndTerminator() {
return ir::range<T>(mLabel.getNext(), mTerminator);
}
};
private:
std::map<ir::Value, Node> mNodes;
std::vector<Node *> mPreorderNodes;
std::vector<Node *> mPostorderNodes;
Node *mEntryNode = nullptr;
public:
bool empty() { return mNodes.empty(); }
void clear() {
mNodes.clear();
mPreorderNodes.clear();
mPostorderNodes.clear();
mEntryNode = nullptr;
}
void addPreorderNode(Node *node) { mPreorderNodes.push_back(node); }
void addPostorderNode(Node *node) { mPostorderNodes.push_back(node); }
Node *getEntryNode() { return mEntryNode; }
ir::Value getEntryLabel() { return getEntryNode()->getLabel(); }
void setEntryNode(Node *node) { mEntryNode = node; }
std::span<Node *> getPreorderNodes() { return mPreorderNodes; }
std::span<Node *> getPostorderNodes() { return mPostorderNodes; }
Node *getOrCreateNode(ir::Value label) {
return &mNodes.emplace(label, label).first->second;
}
Node *getNode(ir::Value label) {
if (auto it = mNodes.find(label); it != mNodes.end()) {
return &it->second;
}
return nullptr;
}
auto &getSuccessors(ir::Value label) {
return getNode(label)->getSuccessors();
}
auto &getPredecessors(ir::Value label) {
return getNode(label)->getPredecessors();
}
void print(std::ostream &os, ir::NameStorage &ns, bool subgraph = false,
std::string_view nameSuffix = "");
std::string genTest();
CFG buildView(CFG::Node *from, PostDomTree *domTree = nullptr,
const std::unordered_set<ir::Value> &stopLabels = {},
ir::Value continueLabel = nullptr);
CFG buildView(ir::Value from, PostDomTree *domTree = nullptr,
const std::unordered_set<ir::Value> &stopLabels = {},
ir::Value continueLabel = nullptr) {
return buildView(getNode(from), domTree, stopLabels, continueLabel);
}
};
class MemorySSA {
public:
ir::Context context;
ir::Region region;
std::map<ir::Value, ir::memssa::Var> variableToVar;
std::map<ir::Instruction, std::map<ir::memssa::Var, ir::memssa::Def>>
userDefs;
ir::memssa::Var getVar(ir::Value variable, std::span<const ir::Operand> path);
ir::memssa::Var getVar(ir::Value pointer);
ir::memssa::Def getDef(ir::Instruction user, ir::memssa::Var var) {
auto userIt = userDefs.find(user);
if (userIt == userDefs.end()) {
return {};
}
if (auto it = userIt->second.find(var); it != userIt->second.end()) {
return it->second;
}
return {};
}
ir::memssa::Def getDef(ir::Instruction user, ir::Value pointer) {
if (auto var = getVar(pointer)) {
return getDef(user, var);
}
return {};
}
ir::Instruction getDefInst(ir::Instruction user, ir::Value pointer) {
if (auto def = getDef(user, pointer)) {
return def.getLinkedInst();
}
return {};
}
void print(std::ostream &os, ir::Region irRegion, ir::NameStorage &ns);
void print(std::ostream &os, ir::NameStorage &ns);
void dump();
private:
ir::memssa::Var getVarImpl(ir::Value variable);
};
bool isWithoutSideEffects(ir::InstructionId id);
bool isTerminator(ir::Instruction inst);
bool isBranch(ir::Instruction inst);
ir::Value unwrapPointer(ir::Value pointer);
graph::DomTree<ir::Value> buildDomTree(CFG &cfg, ir::Value root = nullptr);
graph::DomTree<ir::Value> buildPostDomTree(CFG &cfg, ir::Value root);
CFG buildCFG(ir::Instruction firstInstruction,
const std::unordered_set<ir::Value> &exitLabels = {},
ir::Value continueLabel = nullptr);
MemorySSA buildMemorySSA(CFG &cfg, ModuleInfo *moduleInfo = nullptr);
MemorySSA buildMemorySSA(CFG &cfg, const SemanticInfo &instructionSemantic,
std::function<ir::Value(int)> getRegisterVarCb);
bool dominates(ir::Instruction a, ir::Instruction b, bool isPostDom,
graph::DomTree<ir::Value> &domTree);
ir::Value findNearestCommonDominator(ir::Instruction a, ir::Instruction b,
graph::DomTree<ir::Value> &domTree);
class BackEdgeStorage {
std::unordered_map<ir::Value, std::unordered_set<ir::Value>> backEdges;
public:
BackEdgeStorage() = default;
BackEdgeStorage(CFG &cfg);
const std::unordered_set<ir::Value> *get(ir::Value value) {
if (auto it = backEdges.find(value); it != backEdges.end()) {
return &it->second;
}
return nullptr;
}
auto &all() { return backEdges; }
};
struct AnalysisStorage {
template <typename... T>
requires(sizeof...(T) > 0)
bool invalidate() {
bool invalidated = false;
((invalidated = invalidate(rx::TypeId::get<T>()) || invalidated), ...);
return invalidated;
}
bool invalidate(rx::TypeId id) {
if (auto it = mStorage.find(id); it != mStorage.end()) {
return std::exchange(it->second.invalid, true) == false;
}
return false;
}
void invalidateAll() {
for (auto &entry : mStorage) {
entry.second.invalid = true;
}
}
template <typename T, typename... ArgsT>
T &get(ArgsT &&...args)
requires requires { T(std::forward<ArgsT>(args)...); }
{
void *result = getImpl(
rx::TypeId::get<T>(), getDeleter<T>(),
[&] {
return std::make_unique<T>(std::forward<ArgsT>(args)...).release();
},
[&](void *object) {
*reinterpret_cast<T *>(object) = T(std::forward<ArgsT>(args)...);
});
return *static_cast<T *>(result);
}
template <typename T, typename BuilderFn>
T &get(BuilderFn &&builder)
requires requires { T(std::forward<BuilderFn>(builder)()); }
{
void *result = getImpl(
rx::TypeId::get<T>(), getDeleter<T>(),
[&] {
return std::make_unique<T>(std::forward<BuilderFn>(builder)())
.release();
},
[&](void *object) {
*reinterpret_cast<T *>(object) = std::forward<BuilderFn>(builder)();
});
return *static_cast<T *>(result);
}
private:
template <typename T> static void (*getDeleter())(void *) {
return +[](void *data) { delete static_cast<T *>(data); };
}
void *getImpl(rx::TypeId typeId, void (*deleter)(void *),
rx::FunctionRef<void *()> constructor,
rx::FunctionRef<void(void *)> placementConstructor) {
auto [it, inserted] = mStorage.emplace(typeId, getNullPointer());
if (inserted) {
it->second.object =
std::unique_ptr<void, void (*)(void *)>(constructor(), deleter);
} else if (it->second.invalid) {
placementConstructor(it->second.object.get());
it->second.invalid = false;
}
return it->second.object.get();
}
static constexpr std::unique_ptr<void, void (*)(void *)> getNullPointer() {
return {nullptr, [](void *) {}};
}
struct Entry {
std::unique_ptr<void, void (*)(void *)> object;
bool invalid = false;
};
std::map<rx::TypeId, Entry> mStorage;
};
struct PostDomTree : graph::DomTree<ir::Value> {
PostDomTree() = default;
PostDomTree(graph::DomTree<ir::Value> &&other)
: graph::DomTree<ir::Value>::DomTree(std::move(other)) {}
PostDomTree(CFG &cfg, ir::Value root)
: PostDomTree(buildPostDomTree(cfg, root)) {}
};
struct DomTree : graph::DomTree<ir::Value> {
DomTree() = default;
DomTree(graph::DomTree<ir::Value> &&other)
: graph::DomTree<ir::Value>::DomTree(std::move(other)) {}
DomTree(CFG &cfg, ir::Value root = nullptr)
: DomTree(buildDomTree(cfg, root)) {}
};
template <typename T, std::size_t> struct Tag : T {
using T::T;
using T::operator=;
Tag(T &&other) : T(std::move(other)) {}
Tag(const T &other) : T(other) {}
Tag &operator=(T &&other) {
T::operator=(std::move(other));
return *this;
}
Tag &operator=(const T &other) {
T::operator=(other);
return *this;
}
};
struct Construct {
Construct *parent;
std::forward_list<Construct> children;
ir::Value header;
ir::Value merge;
ir::Value loopBody;
ir::Value loopContinue;
AnalysisStorage analysis;
static std::unique_ptr<Construct> createRoot(ir::RegionLike region,
ir::Value merge) {
auto result = std::make_unique<Construct>();
auto &cfg =
result->analysis.get<CFG>([&] { return buildCFG(region.getFirst()); });
result->header = cfg.getEntryLabel();
result->merge = merge;
return result;
}
Construct *createChild(ir::Value header, ir::Value merge) {
auto &result = children.emplace_front();
result.parent = this;
result.header = header;
result.merge = merge;
return &result;
}
Construct *createChild(ir::Value header, ir::Value merge,
ir::Value loopContinue, ir::Value loopBody) {
auto &result = children.emplace_front();
result.parent = this;
result.header = header;
result.merge = merge;
result.loopContinue = loopContinue;
result.loopBody = loopBody;
return &result;
}
Construct createTemporaryChild(ir::Value header, ir::Value merge) {
Construct result;
result.parent = this;
result.header = header;
result.merge = merge;
return result;
}
CFG &getCfg() {
return analysis.get<CFG>([this] {
if (parent != nullptr) {
return parent->getCfg().buildView(
header,
&parent->getPostDomTree(),
{header, merge});
}
return buildCFG(header);
});
}
CFG &getCfgWithoutContinue() {
if (loopContinue == nullptr) {
return getCfg();
}
return analysis.get<Tag<CFG, kWithoutContinue>>([this] {
if (parent != nullptr) {
return parent->getCfg().buildView(
header,
&parent->getPostDomTree(),
{header, merge}, loopContinue);
}
return buildCFG(header, {}, loopContinue);
});
}
DomTree &getDomTree() { return analysis.get<DomTree>(getCfg(), header); }
PostDomTree &getPostDomTree() {
return analysis.get<PostDomTree>(getCfg(), merge);
}
BackEdgeStorage &getBackEdgeStorage() {
return analysis.get<BackEdgeStorage>(getCfg());
}
BackEdgeStorage &getBackEdgeWithoutContinueStorage() {
if (loopContinue == nullptr) {
return getBackEdgeStorage();
}
return analysis.get<Tag<BackEdgeStorage, kWithoutContinue>>(
getCfgWithoutContinue());
}
auto getBackEdges(ir::Value node) { return getBackEdgeStorage().get(node); }
auto getBackEdgesWithoutContinue(ir::Value node) {
return getBackEdgeWithoutContinueStorage().get(node);
}
auto getBackEdges() { return getBackEdges(header); }
void invalidate();
void invalidateAll();
bool isNull() const { return header == nullptr; }
void removeLastChild() { children.pop_front(); }
private:
enum {
kWithoutContinue,
};
};
} // namespace shader