2021-01-09 06:30:07 +00:00
|
|
|
// Copyright 2021 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
#include <span>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <boost/container/small_vector.hpp>
|
2021-02-11 19:39:06 +00:00
|
|
|
#include <boost/intrusive/set.hpp>
|
2021-01-09 06:30:07 +00:00
|
|
|
|
|
|
|
#include "shader_recompiler/environment.h"
|
|
|
|
#include "shader_recompiler/frontend/ir/condition.h"
|
2022-03-19 04:59:56 +00:00
|
|
|
#include "shader_recompiler/frontend/ir/reg.h"
|
2021-01-09 06:30:07 +00:00
|
|
|
#include "shader_recompiler/frontend/maxwell/instruction.h"
|
|
|
|
#include "shader_recompiler/frontend/maxwell/location.h"
|
2021-02-06 02:11:23 +00:00
|
|
|
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
2021-02-11 19:39:06 +00:00
|
|
|
#include "shader_recompiler/object_pool.h"
|
|
|
|
|
2021-01-09 06:30:07 +00:00
|
|
|
namespace Shader::Maxwell::Flow {
|
|
|
|
|
2021-03-30 01:13:37 +00:00
|
|
|
struct Block;
|
|
|
|
|
2021-01-09 06:30:07 +00:00
|
|
|
using FunctionId = size_t;
|
|
|
|
|
|
|
|
enum class EndClass {
|
|
|
|
Branch,
|
2021-03-27 21:30:24 +00:00
|
|
|
IndirectBranch,
|
2021-03-14 06:41:05 +00:00
|
|
|
Call,
|
2021-01-09 06:30:07 +00:00
|
|
|
Exit,
|
|
|
|
Return,
|
2021-03-19 22:28:31 +00:00
|
|
|
Kill,
|
2021-01-09 06:30:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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<Location, Stack> Pop(Token token) const;
|
|
|
|
[[nodiscard]] std::optional<Location> Peek(Token token) const;
|
|
|
|
[[nodiscard]] Stack Remove(Token token) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
boost::container::small_vector<StackEntry, 3> entries;
|
|
|
|
};
|
|
|
|
|
2021-03-30 01:13:37 +00:00
|
|
|
struct IndirectBranch {
|
|
|
|
Block* block;
|
|
|
|
u32 address;
|
|
|
|
};
|
|
|
|
|
2021-02-11 19:39:06 +00:00
|
|
|
struct Block : boost::intrusive::set_base_hook<
|
|
|
|
// Normal link is ~2.5% faster compared to safe link
|
|
|
|
boost::intrusive::link_mode<boost::intrusive::normal_link>> {
|
2021-01-09 06:30:07 +00:00
|
|
|
[[nodiscard]] bool Contains(Location pc) const noexcept;
|
|
|
|
|
2021-02-11 19:39:06 +00:00
|
|
|
bool operator<(const Block& rhs) const noexcept {
|
|
|
|
return begin < rhs.begin;
|
|
|
|
}
|
|
|
|
|
2021-01-09 06:30:07 +00:00
|
|
|
Location begin;
|
|
|
|
Location end;
|
2021-04-10 06:32:55 +00:00
|
|
|
EndClass end_class{};
|
|
|
|
IR::Condition cond{};
|
2021-04-04 23:00:34 +00:00
|
|
|
Stack stack;
|
2021-04-10 06:32:55 +00:00
|
|
|
Block* branch_true{};
|
|
|
|
Block* branch_false{};
|
|
|
|
FunctionId function_call{};
|
|
|
|
Block* return_block{};
|
|
|
|
IR::Reg branch_reg{};
|
|
|
|
s32 branch_offset{};
|
2021-03-30 01:13:37 +00:00
|
|
|
std::vector<IndirectBranch> indirect_branches;
|
2021-01-09 06:30:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Label {
|
|
|
|
Location address;
|
2021-02-11 19:39:06 +00:00
|
|
|
Block* block;
|
2021-01-09 06:30:07 +00:00
|
|
|
Stack stack;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Function {
|
2021-03-14 06:41:05 +00:00
|
|
|
explicit Function(ObjectPool<Block>& block_pool, Location start_address);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
|
|
|
Location entrypoint;
|
|
|
|
boost::container::small_vector<Label, 16> labels;
|
2021-02-11 19:39:06 +00:00
|
|
|
boost::intrusive::set<Block> blocks;
|
2021-01-09 06:30:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class CFG {
|
|
|
|
enum class AnalysisState {
|
|
|
|
Branch,
|
|
|
|
Continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
2021-04-18 17:10:55 +00:00
|
|
|
explicit CFG(Environment& env, ObjectPool<Block>& block_pool, Location start_address,
|
|
|
|
bool exits_to_dispatcher = false);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
2021-02-03 00:07:00 +00:00
|
|
|
CFG& operator=(const CFG&) = delete;
|
|
|
|
CFG(const CFG&) = delete;
|
|
|
|
|
|
|
|
CFG& operator=(CFG&&) = delete;
|
|
|
|
CFG(CFG&&) = delete;
|
|
|
|
|
2021-01-09 06:30:07 +00:00
|
|
|
[[nodiscard]] std::string Dot() const;
|
|
|
|
|
|
|
|
[[nodiscard]] std::span<const Function> Functions() const noexcept {
|
|
|
|
return std::span(functions.data(), functions.size());
|
|
|
|
}
|
2021-02-11 19:39:06 +00:00
|
|
|
[[nodiscard]] std::span<Function> Functions() noexcept {
|
|
|
|
return std::span(functions.data(), functions.size());
|
|
|
|
}
|
2021-01-09 06:30:07 +00:00
|
|
|
|
2021-04-18 17:10:55 +00:00
|
|
|
[[nodiscard]] bool ExitsToDispatcher() const {
|
|
|
|
return exits_to_dispatcher;
|
|
|
|
}
|
|
|
|
|
2021-01-09 06:30:07 +00:00
|
|
|
private:
|
|
|
|
void AnalyzeLabel(FunctionId function_id, Label& label);
|
|
|
|
|
|
|
|
/// Inspect already visited blocks.
|
|
|
|
/// Return true when the block has already been visited
|
2021-02-03 00:07:00 +00:00
|
|
|
bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
2021-02-11 19:39:06 +00:00
|
|
|
AnalysisState AnalyzeInst(Block* block, FunctionId function_id, Location pc);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
2021-02-11 19:39:06 +00:00
|
|
|
void AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, EndClass insn_end_class,
|
2021-03-19 22:28:31 +00:00
|
|
|
IR::Condition cond);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
|
|
|
/// Return true when the branch instruction is confirmed to be a branch
|
2021-02-11 19:39:06 +00:00
|
|
|
bool AnalyzeBranch(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
2021-02-03 00:07:00 +00:00
|
|
|
Opcode opcode);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
2021-02-11 19:39:06 +00:00
|
|
|
void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
2021-01-09 06:30:07 +00:00
|
|
|
bool is_absolute);
|
2021-03-27 21:30:24 +00:00
|
|
|
AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute,
|
|
|
|
FunctionId function_id);
|
2021-02-11 19:39:06 +00:00
|
|
|
AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
|
|
|
/// Return the branch target block id
|
2021-02-11 19:39:06 +00:00
|
|
|
Block* AddLabel(Block* block, Stack stack, Location pc, FunctionId function_id);
|
2021-01-09 06:30:07 +00:00
|
|
|
|
|
|
|
Environment& env;
|
2021-02-11 19:39:06 +00:00
|
|
|
ObjectPool<Block>& block_pool;
|
2021-01-09 06:30:07 +00:00
|
|
|
boost::container::small_vector<Function, 1> functions;
|
2021-04-06 00:01:01 +00:00
|
|
|
Location program_start;
|
2021-04-18 17:10:55 +00:00
|
|
|
bool exits_to_dispatcher{};
|
|
|
|
Block* dispatch_block{};
|
2021-01-09 06:30:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace Shader::Maxwell::Flow
|