// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/condition.h" #include "shader_recompiler/frontend/maxwell/instruction.h" #include "shader_recompiler/frontend/maxwell/location.h" #include "shader_recompiler/frontend/maxwell/opcode.h" namespace Shader::Maxwell::Flow { using BlockId = u32; using FunctionId = size_t; constexpr BlockId UNREACHABLE_BLOCK_ID{static_cast(-1)}; enum class EndClass { Branch, Exit, Return, Unreachable, }; enum class Token { SSY, PBK, PEXIT, PRET, PCNT, PLONGJMP, }; struct StackEntry { auto operator<=>(const StackEntry&) const noexcept = default; Token token; Location target; }; class Stack { public: void Push(Token token, Location target); [[nodiscard]] std::pair Pop(Token token) const; [[nodiscard]] std::optional Peek(Token token) const; [[nodiscard]] Stack Remove(Token token) const; private: boost::container::small_vector entries; }; struct Block { [[nodiscard]] bool Contains(Location pc) const noexcept; Location begin; Location end; EndClass end_class; BlockId id; Stack stack; IR::Condition cond; BlockId branch_true; BlockId branch_false; }; struct Label { Location address; BlockId block_id; Stack stack; }; struct Function { Function(Location start_address); Location entrypoint; BlockId current_block_id{0}; boost::container::small_vector labels; boost::container::small_vector blocks; boost::container::small_vector blocks_data; }; class CFG { enum class AnalysisState { Branch, Continue, }; public: explicit CFG(Environment& env, Location start_address); [[nodiscard]] std::string Dot() const; [[nodiscard]] std::span Functions() const noexcept { return std::span(functions.data(), functions.size()); } private: void AnalyzeLabel(FunctionId function_id, Label& label); /// Inspect already visited blocks. /// Return true when the block has already been visited [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label); [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc); void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class, IR::Condition cond); /// Return true when the branch instruction is confirmed to be a branch [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst, Opcode opcode); void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, bool is_absolute); void AnalyzeBRX(Block& block, Location pc, Instruction inst, bool is_absolute); void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute); AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst); /// Return the branch target block id [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id); Environment& env; boost::container::small_vector functions; FunctionId current_function_id{0}; }; } // namespace Shader::Maxwell::Flow