#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERCFG_H_ #define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERCFG_H_ #include "common/common.h" #include "common/stringUtils.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include namespace Libs::Graphics::ShaderRecompiler::CFG { enum class BranchCondition { Always, SccZero, SccNonZero, VccZero, VccNonZero, ExecZero, ExecNonZero, ScalarInstruction, Expression, Unknown }; struct ConditionExpression { enum class Op { Constant, Variable, Native, Not, Or }; Op op; uint32_t lhs; uint32_t rhs = UINT32_MAX; }; enum class TerminatorKind { Branch, ConditionalBranch, IndirectBranch, Return, Unsupported }; enum class FailureKind { None, InvalidInput, UnsupportedInstruction, InvalidBranchTarget, MissingFallthrough, InvalidLabel, IrreducibleControlFlow, StructuredControlFlow }; struct Terminator { TerminatorKind kind = TerminatorKind::Return; BranchCondition condition = BranchCondition::Always; uint32_t true_block = UINT32_MAX; uint32_t false_block = UINT32_MAX; uint32_t merge_block = UINT32_MAX; uint32_t continue_block = UINT32_MAX; uint32_t indirect_pc_sgpr = UINT32_MAX; uint32_t indirect_selector_code = UINT32_MAX; std::vector indirect_target_pcs; std::vector indirect_targets; std::vector indirect_selector_values; std::vector indirect_selector_targets; uint32_t expression = UINT32_MAX; bool loop_header = false; }; struct BasicBlock { uint32_t id = 0; uint32_t start_pc = 0; uint32_t end_pc = 0; uint32_t inst_begin = 0; uint32_t inst_end = 0; struct Assignment { uint32_t variable; uint32_t expression; }; std::vector assignments; std::vector predecessors; std::vector successors; std::vector dominators; Terminator terminator; }; struct BackEdge { uint32_t from = UINT32_MAX; uint32_t to = UINT32_MAX; bool natural = false; }; struct NaturalLoop { uint32_t header = UINT32_MAX; uint32_t latch = UINT32_MAX; std::vector body_blocks; }; struct StronglyConnectedComponent { std::vector blocks; std::vector entry_blocks; bool irreducible = false; }; struct Graph { std::vector blocks; std::vector expressions; std::vector back_edges; std::vector natural_loops; std::vector components; std::vector code_table_load_pcs; uint32_t entry_block = UINT32_MAX; bool irreducible = false; bool unsupported = false; FailureKind failure_kind = FailureKind::None; uint32_t failure_block = UINT32_MAX; std::string unsupported_reason; const BasicBlock* FindBlock(uint32_t id) const; BasicBlock* FindBlock(uint32_t id); const BasicBlock* FindBlockByPc(uint32_t pc) const; BasicBlock* FindBlockByPc(uint32_t pc); bool Dominates(uint32_t dominator, uint32_t block) const; }; Graph BuildGraph(const Decoder::Program& program); bool MayWriteScalarRegister(const Decoder::Instruction& inst, uint32_t code); uint32_t FindScalarDefinition(const Decoder::Program& program, const Graph& graph, uint32_t before, uint32_t code, std::vector& crossed_calls); // Returns structured control flow or failure diagnostics without changing the native graph. // On failure, failure_block is an original block ID or UINT32_MAX. Graph Structurize(const Graph& graph); std::string BranchConditionToString(BranchCondition condition); std::string FailureKindToString(FailureKind kind); std::string GraphToString(const Graph& graph); } // namespace Libs::Graphics::ShaderRecompiler::CFG #endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERCFG_H_ */