mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-25 22:57:22 +00:00
Shader_Ir: Refactor Decompilation process and allow multiple decompilation modes.
This commit is contained in:
parent
38fc995f6c
commit
47e4f6a52c
15 changed files with 338 additions and 82 deletions
|
@ -87,6 +87,8 @@ set(HASH_FILES
|
||||||
"${VIDEO_CORE}/shader/ast.h"
|
"${VIDEO_CORE}/shader/ast.h"
|
||||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||||
"${VIDEO_CORE}/shader/control_flow.h"
|
"${VIDEO_CORE}/shader/control_flow.h"
|
||||||
|
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||||
"${VIDEO_CORE}/shader/decode.cpp"
|
"${VIDEO_CORE}/shader/decode.cpp"
|
||||||
"${VIDEO_CORE}/shader/expr.cpp"
|
"${VIDEO_CORE}/shader/expr.cpp"
|
||||||
"${VIDEO_CORE}/shader/expr.h"
|
"${VIDEO_CORE}/shader/expr.h"
|
||||||
|
|
|
@ -64,6 +64,8 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||||
"${VIDEO_CORE}/shader/ast.h"
|
"${VIDEO_CORE}/shader/ast.h"
|
||||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||||
"${VIDEO_CORE}/shader/control_flow.h"
|
"${VIDEO_CORE}/shader/control_flow.h"
|
||||||
|
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||||
"${VIDEO_CORE}/shader/decode.cpp"
|
"${VIDEO_CORE}/shader/decode.cpp"
|
||||||
"${VIDEO_CORE}/shader/expr.cpp"
|
"${VIDEO_CORE}/shader/expr.cpp"
|
||||||
"${VIDEO_CORE}/shader/expr.h"
|
"${VIDEO_CORE}/shader/expr.h"
|
||||||
|
|
|
@ -109,6 +109,8 @@ add_library(video_core STATIC
|
||||||
shader/ast.h
|
shader/ast.h
|
||||||
shader/control_flow.cpp
|
shader/control_flow.cpp
|
||||||
shader/control_flow.h
|
shader/control_flow.h
|
||||||
|
shader/compiler_settings.cpp
|
||||||
|
shader/compiler_settings.h
|
||||||
shader/decode.cpp
|
shader/decode.cpp
|
||||||
shader/expr.cpp
|
shader/expr.cpp
|
||||||
shader/expr.h
|
shader/expr.h
|
||||||
|
|
|
@ -352,10 +352,12 @@ public:
|
||||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||||
|
if (!ir.IsFlowStackDisabled()) {
|
||||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||||
code.AddLine("uint {} = 0u;", FlowStackTopName(stack));
|
code.AddLine("uint {} = 0u;", FlowStackTopName(stack));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code.AddLine("while (true) {{");
|
code.AddLine("while (true) {{");
|
||||||
++code.scope;
|
++code.scope;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace OpenGL::GLShader {
|
namespace OpenGL::GLShader {
|
||||||
|
|
||||||
using Tegra::Engines::Maxwell3D;
|
using Tegra::Engines::Maxwell3D;
|
||||||
|
using VideoCommon::Shader::CompileDepth;
|
||||||
|
using VideoCommon::Shader::CompilerSettings;
|
||||||
using VideoCommon::Shader::ProgramCode;
|
using VideoCommon::Shader::ProgramCode;
|
||||||
using VideoCommon::Shader::ShaderIR;
|
using VideoCommon::Shader::ShaderIR;
|
||||||
|
|
||||||
|
@ -31,13 +33,17 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||||
|
|
||||||
)";
|
)";
|
||||||
|
|
||||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
|
CompilerSettings settings;
|
||||||
|
settings.depth = CompileDepth::NoFlowStack;
|
||||||
|
|
||||||
|
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
|
||||||
const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB;
|
const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB;
|
||||||
ProgramResult program = Decompile(device, program_ir, stage, "vertex");
|
ProgramResult program = Decompile(device, program_ir, stage, "vertex");
|
||||||
out += program.first;
|
out += program.first;
|
||||||
|
|
||||||
if (setup.IsDualProgram()) {
|
if (setup.IsDualProgram()) {
|
||||||
const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b);
|
const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b,
|
||||||
|
settings);
|
||||||
ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b");
|
ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b");
|
||||||
out += program_b.first;
|
out += program_b.first;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +86,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||||
|
|
||||||
)";
|
)";
|
||||||
|
|
||||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
|
CompilerSettings settings;
|
||||||
|
settings.depth = CompileDepth::NoFlowStack;
|
||||||
|
|
||||||
|
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
|
||||||
ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry");
|
ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry");
|
||||||
out += program.first;
|
out += program.first;
|
||||||
|
|
||||||
|
@ -114,7 +123,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||||
};
|
};
|
||||||
|
|
||||||
)";
|
)";
|
||||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a);
|
CompilerSettings settings;
|
||||||
|
settings.depth = CompileDepth::NoFlowStack;
|
||||||
|
|
||||||
|
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings);
|
||||||
ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment");
|
ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment");
|
||||||
out += program.first;
|
out += program.first;
|
||||||
|
|
||||||
|
@ -133,7 +145,10 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set
|
||||||
std::string out = "// Shader Unique Id: CS" + id + "\n\n";
|
std::string out = "// Shader Unique Id: CS" + id + "\n\n";
|
||||||
out += GetCommonDeclarations();
|
out += GetCommonDeclarations();
|
||||||
|
|
||||||
const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a);
|
CompilerSettings settings;
|
||||||
|
settings.depth = CompileDepth::NoFlowStack;
|
||||||
|
|
||||||
|
const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings);
|
||||||
ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute");
|
ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute");
|
||||||
out += program.first;
|
out += program.first;
|
||||||
|
|
||||||
|
|
|
@ -363,7 +363,7 @@ std::string ASTManager::Print() {
|
||||||
return printer.GetResult();
|
return printer.GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTManager::ASTManager() = default;
|
ASTManager::ASTManager(bool full_decompile) : full_decompile{full_decompile} {};
|
||||||
|
|
||||||
ASTManager::~ASTManager() {
|
ASTManager::~ASTManager() {
|
||||||
Clear();
|
Clear();
|
||||||
|
@ -383,6 +383,7 @@ ASTManager::ASTManager(ASTManager&& other)
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTManager& ASTManager::operator=(ASTManager&& other) {
|
ASTManager& ASTManager::operator=(ASTManager&& other) {
|
||||||
|
full_decompile = other.full_decompile;
|
||||||
labels_map = std::move(other.labels_map);
|
labels_map = std::move(other.labels_map);
|
||||||
labels_count = other.labels_count;
|
labels_count = other.labels_count;
|
||||||
gotos = std::move(other.gotos);
|
gotos = std::move(other.gotos);
|
||||||
|
@ -434,6 +435,13 @@ void ASTManager::Decompile() {
|
||||||
ASTNode goto_node = *it;
|
ASTNode goto_node = *it;
|
||||||
u32 label_index = goto_node->GetGotoLabel();
|
u32 label_index = goto_node->GetGotoLabel();
|
||||||
ASTNode label = labels[label_index];
|
ASTNode label = labels[label_index];
|
||||||
|
if (!full_decompile) {
|
||||||
|
// We only decompile backward jumps
|
||||||
|
if (!IsBackwardsJump(goto_node, label)) {
|
||||||
|
it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (IndirectlyRelated(goto_node, label)) {
|
if (IndirectlyRelated(goto_node, label)) {
|
||||||
while (!DirectlyRelated(goto_node, label)) {
|
while (!DirectlyRelated(goto_node, label)) {
|
||||||
MoveOutward(goto_node);
|
MoveOutward(goto_node);
|
||||||
|
@ -469,11 +477,91 @@ void ASTManager::Decompile() {
|
||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
if (full_decompile) {
|
||||||
for (ASTNode label : labels) {
|
for (ASTNode label : labels) {
|
||||||
auto& manager = label->GetManager();
|
auto& manager = label->GetManager();
|
||||||
manager.Remove(label);
|
manager.Remove(label);
|
||||||
}
|
}
|
||||||
labels.clear();
|
labels.clear();
|
||||||
|
} else {
|
||||||
|
auto it = labels.begin();
|
||||||
|
while (it != labels.end()) {
|
||||||
|
bool can_remove = true;
|
||||||
|
ASTNode label = *it;
|
||||||
|
for (ASTNode goto_node : gotos) {
|
||||||
|
u32 label_index = goto_node->GetGotoLabel();
|
||||||
|
ASTNode glabel = labels[label_index];
|
||||||
|
if (glabel == label) {
|
||||||
|
can_remove = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (can_remove) {
|
||||||
|
auto& manager = label->GetManager();
|
||||||
|
manager.Remove(label);
|
||||||
|
labels.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const {
|
||||||
|
u32 goto_level = goto_node->GetLevel();
|
||||||
|
u32 label_level = label_node->GetLevel();
|
||||||
|
while (goto_level > label_level) {
|
||||||
|
goto_level--;
|
||||||
|
goto_node = goto_node->GetParent();
|
||||||
|
}
|
||||||
|
while (label_level > goto_level) {
|
||||||
|
label_level--;
|
||||||
|
label_node = label_node->GetParent();
|
||||||
|
}
|
||||||
|
while (goto_node->GetParent() != label_node->GetParent()) {
|
||||||
|
goto_node = goto_node->GetParent();
|
||||||
|
label_node = label_node->GetParent();
|
||||||
|
}
|
||||||
|
ASTNode current = goto_node->GetPrevious();
|
||||||
|
while (current) {
|
||||||
|
if (current == label_node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = current->GetPrevious();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode CommonParent(ASTNode first, ASTNode second) {
|
||||||
|
if (first->GetParent() == second->GetParent()) {
|
||||||
|
return first->GetParent();
|
||||||
|
}
|
||||||
|
u32 first_level = first->GetLevel();
|
||||||
|
u32 second_level = second->GetLevel();
|
||||||
|
u32 min_level;
|
||||||
|
u32 max_level;
|
||||||
|
ASTNode max;
|
||||||
|
ASTNode min;
|
||||||
|
if (first_level > second_level) {
|
||||||
|
min_level = second_level;
|
||||||
|
min = second;
|
||||||
|
max_level = first_level;
|
||||||
|
max = first;
|
||||||
|
} else {
|
||||||
|
min_level = first_level;
|
||||||
|
min = first;
|
||||||
|
max_level = second_level;
|
||||||
|
max = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (max_level > min_level) {
|
||||||
|
max_level--;
|
||||||
|
max = max->GetParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (min->GetParent() != max->GetParent()) {
|
||||||
|
min = min->GetParent();
|
||||||
|
max = max->GetParent();
|
||||||
|
}
|
||||||
|
return min->GetParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) {
|
bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) {
|
||||||
|
|
|
@ -274,7 +274,7 @@ private:
|
||||||
|
|
||||||
class ASTManager final {
|
class ASTManager final {
|
||||||
public:
|
public:
|
||||||
ASTManager();
|
ASTManager(bool full_decompile);
|
||||||
~ASTManager();
|
~ASTManager();
|
||||||
|
|
||||||
ASTManager(const ASTManager& o) = delete;
|
ASTManager(const ASTManager& o) = delete;
|
||||||
|
@ -304,7 +304,18 @@ public:
|
||||||
void SanityCheck();
|
void SanityCheck();
|
||||||
|
|
||||||
bool IsFullyDecompiled() const {
|
bool IsFullyDecompiled() const {
|
||||||
|
if (full_decompile) {
|
||||||
return gotos.size() == 0;
|
return gotos.size() == 0;
|
||||||
|
} else {
|
||||||
|
for (ASTNode goto_node : gotos) {
|
||||||
|
u32 label_index = goto_node->GetGotoLabel();
|
||||||
|
ASTNode glabel = labels[label_index];
|
||||||
|
if (IsBackwardsJump(goto_node, glabel)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode GetProgram() const {
|
ASTNode GetProgram() const {
|
||||||
|
@ -318,6 +329,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const;
|
||||||
|
|
||||||
|
ASTNode CommonParent(ASTNode first, ASTNode second);
|
||||||
|
|
||||||
bool IndirectlyRelated(ASTNode first, ASTNode second);
|
bool IndirectlyRelated(ASTNode first, ASTNode second);
|
||||||
|
|
||||||
bool DirectlyRelated(ASTNode first, ASTNode second);
|
bool DirectlyRelated(ASTNode first, ASTNode second);
|
||||||
|
@ -334,6 +349,7 @@ private:
|
||||||
return new_var;
|
return new_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool full_decompile{};
|
||||||
std::unordered_map<u32, u32> labels_map{};
|
std::unordered_map<u32, u32> labels_map{};
|
||||||
u32 labels_count{};
|
u32 labels_count{};
|
||||||
std::vector<ASTNode> labels{};
|
std::vector<ASTNode> labels{};
|
||||||
|
|
26
src/video_core/shader/compiler_settings.cpp
Normal file
26
src/video_core/shader/compiler_settings.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "video_core/shader/compiler_settings.h"
|
||||||
|
|
||||||
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
std::string CompileDepthAsString(const CompileDepth cd) {
|
||||||
|
switch (cd) {
|
||||||
|
case CompileDepth::BruteForce:
|
||||||
|
return "Brute Force Compile";
|
||||||
|
case CompileDepth::FlowStack:
|
||||||
|
return "Simple Flow Stack Mode";
|
||||||
|
case CompileDepth::NoFlowStack:
|
||||||
|
return "Remove Flow Stack";
|
||||||
|
case CompileDepth::DecompileBackwards:
|
||||||
|
return "Decompile Backward Jumps";
|
||||||
|
case CompileDepth::FullDecompile:
|
||||||
|
return "Full Decompilation";
|
||||||
|
default:
|
||||||
|
return "Unknown Compiler Process";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon::Shader
|
25
src/video_core/shader/compiler_settings.h
Normal file
25
src/video_core/shader/compiler_settings.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
|
|
||||||
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
enum class CompileDepth : u32 {
|
||||||
|
BruteForce = 0,
|
||||||
|
FlowStack = 1,
|
||||||
|
NoFlowStack = 2,
|
||||||
|
DecompileBackwards = 3,
|
||||||
|
FullDecompile = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string CompileDepthAsString(CompileDepth cd);
|
||||||
|
|
||||||
|
struct CompilerSettings {
|
||||||
|
CompileDepth depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon::Shader
|
|
@ -57,8 +57,8 @@ struct BlockInfo {
|
||||||
|
|
||||||
struct CFGRebuildState {
|
struct CFGRebuildState {
|
||||||
explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size,
|
explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size,
|
||||||
const u32 start, ASTManager& manager)
|
const u32 start)
|
||||||
: program_code{program_code}, program_size{program_size}, start{start}, manager{manager} {}
|
: program_code{program_code}, program_size{program_size}, start{start} {}
|
||||||
|
|
||||||
u32 start{};
|
u32 start{};
|
||||||
std::vector<BlockInfo> block_info{};
|
std::vector<BlockInfo> block_info{};
|
||||||
|
@ -71,7 +71,7 @@ struct CFGRebuildState {
|
||||||
std::unordered_map<u32, BlockStack> stacks{};
|
std::unordered_map<u32, BlockStack> stacks{};
|
||||||
const ProgramCode& program_code;
|
const ProgramCode& program_code;
|
||||||
const std::size_t program_size;
|
const std::size_t program_size;
|
||||||
ASTManager& manager;
|
ASTManager* manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BlockCollision : u32 { None, Found, Inside };
|
enum class BlockCollision : u32 { None, Found, Inside };
|
||||||
|
@ -456,67 +456,91 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompileShader(CFGRebuildState& state) {
|
void DecompileShader(CFGRebuildState& state) {
|
||||||
state.manager.Init();
|
state.manager->Init();
|
||||||
for (auto label : state.labels) {
|
for (auto label : state.labels) {
|
||||||
state.manager.DeclareLabel(label);
|
state.manager->DeclareLabel(label);
|
||||||
}
|
}
|
||||||
for (auto& block : state.block_info) {
|
for (auto& block : state.block_info) {
|
||||||
if (state.labels.count(block.start) != 0) {
|
if (state.labels.count(block.start) != 0) {
|
||||||
state.manager.InsertLabel(block.start);
|
state.manager->InsertLabel(block.start);
|
||||||
}
|
}
|
||||||
u32 end = block.branch.ignore ? block.end + 1 : block.end;
|
u32 end = block.branch.ignore ? block.end + 1 : block.end;
|
||||||
state.manager.InsertBlock(block.start, end);
|
state.manager->InsertBlock(block.start, end);
|
||||||
if (!block.branch.ignore) {
|
if (!block.branch.ignore) {
|
||||||
InsertBranch(state.manager, block.branch);
|
InsertBranch(*state.manager, block.branch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// state.manager.ShowCurrentState("Before Decompiling");
|
state.manager->Decompile();
|
||||||
state.manager.Decompile();
|
|
||||||
// state.manager.ShowCurrentState("After Decompiling");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
|
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
|
||||||
u32 start_address, ASTManager& manager) {
|
u32 start_address,
|
||||||
CFGRebuildState state{program_code, program_size, start_address, manager};
|
const CompilerSettings& settings) {
|
||||||
|
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||||
|
if (settings.depth == CompileDepth::BruteForce) {
|
||||||
|
result_out->settings.depth = CompileDepth::BruteForce;
|
||||||
|
return std::move(result_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFGRebuildState state{program_code, program_size, start_address};
|
||||||
// Inspect Code and generate blocks
|
// Inspect Code and generate blocks
|
||||||
state.labels.clear();
|
state.labels.clear();
|
||||||
state.labels.emplace(start_address);
|
state.labels.emplace(start_address);
|
||||||
state.inspect_queries.push_back(state.start);
|
state.inspect_queries.push_back(state.start);
|
||||||
while (!state.inspect_queries.empty()) {
|
while (!state.inspect_queries.empty()) {
|
||||||
if (!TryInspectAddress(state)) {
|
if (!TryInspectAddress(state)) {
|
||||||
return {};
|
result_out->settings.depth = CompileDepth::BruteForce;
|
||||||
|
return std::move(result_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool use_flow_stack = true;
|
||||||
|
|
||||||
|
bool decompiled = false;
|
||||||
|
|
||||||
|
if (settings.depth != CompileDepth::FlowStack) {
|
||||||
// Decompile Stacks
|
// Decompile Stacks
|
||||||
state.queries.push_back(Query{state.start, {}, {}});
|
state.queries.push_back(Query{state.start, {}, {}});
|
||||||
bool decompiled = true;
|
decompiled = true;
|
||||||
while (!state.queries.empty()) {
|
while (!state.queries.empty()) {
|
||||||
if (!TryQuery(state)) {
|
if (!TryQuery(state)) {
|
||||||
decompiled = false;
|
decompiled = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use_flow_stack = !decompiled;
|
||||||
|
|
||||||
// Sort and organize results
|
// Sort and organize results
|
||||||
std::sort(state.block_info.begin(), state.block_info.end(),
|
std::sort(state.block_info.begin(), state.block_info.end(),
|
||||||
[](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; });
|
[](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; });
|
||||||
if (decompiled) {
|
if (decompiled && settings.depth != CompileDepth::NoFlowStack) {
|
||||||
|
ASTManager manager{settings.depth != CompileDepth::DecompileBackwards};
|
||||||
|
state.manager = &manager;
|
||||||
DecompileShader(state);
|
DecompileShader(state);
|
||||||
decompiled = state.manager.IsFullyDecompiled();
|
decompiled = state.manager->IsFullyDecompiled();
|
||||||
if (!decompiled) {
|
if (!decompiled) {
|
||||||
|
if (settings.depth == CompileDepth::FullDecompile) {
|
||||||
LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:");
|
LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:");
|
||||||
state.manager.ShowCurrentState("Of Shader");
|
} else {
|
||||||
state.manager.Clear();
|
LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
state.manager->ShowCurrentState("Of Shader");
|
||||||
|
state.manager->Clear();
|
||||||
|
} else {
|
||||||
auto result_out = std::make_unique<ShaderCharacteristics>();
|
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||||
result_out->decompiled = decompiled;
|
|
||||||
result_out->start = start_address;
|
result_out->start = start_address;
|
||||||
if (decompiled) {
|
result_out->settings.depth = settings.depth;
|
||||||
|
result_out->manager = std::move(manager);
|
||||||
result_out->end = state.block_info.back().end + 1;
|
result_out->end = state.block_info.back().end + 1;
|
||||||
return std::move(result_out);
|
return std::move(result_out);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
result_out->start = start_address;
|
||||||
|
result_out->settings.depth =
|
||||||
|
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
||||||
|
result_out->blocks.clear();
|
||||||
for (auto& block : state.block_info) {
|
for (auto& block : state.block_info) {
|
||||||
ShaderBlock new_block{};
|
ShaderBlock new_block{};
|
||||||
new_block.start = block.start;
|
new_block.start = block.start;
|
||||||
|
@ -530,6 +554,10 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||||
result_out->end = std::max(result_out->end, block.end);
|
result_out->end = std::max(result_out->end, block.end);
|
||||||
result_out->blocks.push_back(new_block);
|
result_out->blocks.push_back(new_block);
|
||||||
}
|
}
|
||||||
|
if (!use_flow_stack) {
|
||||||
|
result_out->labels = std::move(state.labels);
|
||||||
|
return std::move(result_out);
|
||||||
|
}
|
||||||
auto back = result_out->blocks.begin();
|
auto back = result_out->blocks.begin();
|
||||||
auto next = std::next(back);
|
auto next = std::next(back);
|
||||||
while (next != result_out->blocks.end()) {
|
while (next != result_out->blocks.end()) {
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "video_core/engines/shader_bytecode.h"
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
|
||||||
#include "video_core/shader/ast.h"
|
#include "video_core/shader/ast.h"
|
||||||
|
#include "video_core/shader/compiler_settings.h"
|
||||||
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
|
||||||
namespace VideoCommon::Shader {
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
@ -68,12 +69,15 @@ struct ShaderBlock {
|
||||||
|
|
||||||
struct ShaderCharacteristics {
|
struct ShaderCharacteristics {
|
||||||
std::list<ShaderBlock> blocks{};
|
std::list<ShaderBlock> blocks{};
|
||||||
bool decompiled{};
|
std::set<u32> labels{};
|
||||||
u32 start{};
|
u32 start{};
|
||||||
u32 end{};
|
u32 end{};
|
||||||
|
ASTManager manager{true};
|
||||||
|
CompilerSettings settings{};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
|
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size,
|
||||||
u32 start_address, ASTManager& manager);
|
u32 start_address,
|
||||||
|
const CompilerSettings& settings);
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -102,36 +102,72 @@ void ShaderIR::Decode() {
|
||||||
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
||||||
|
|
||||||
decompiled = false;
|
decompiled = false;
|
||||||
const auto info =
|
auto info = ScanFlow(program_code, program_size, main_offset, settings);
|
||||||
ScanFlow(program_code, program_size, main_offset, program_manager);
|
auto& shader_info = *info;
|
||||||
if (info) {
|
|
||||||
const auto& shader_info = *info;
|
|
||||||
coverage_begin = shader_info.start;
|
coverage_begin = shader_info.start;
|
||||||
coverage_end = shader_info.end;
|
coverage_end = shader_info.end;
|
||||||
if (shader_info.decompiled) {
|
switch (shader_info.settings.depth) {
|
||||||
|
case CompileDepth::FlowStack: {
|
||||||
|
for (const auto& block : shader_info.blocks) {
|
||||||
|
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompileDepth::NoFlowStack: {
|
||||||
|
disable_flow_stack = true;
|
||||||
|
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||||
|
if (label == static_cast<u32>(exit_branch)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
basic_blocks.insert({label, nodes});
|
||||||
|
};
|
||||||
|
const auto& blocks = shader_info.blocks;
|
||||||
|
NodeBlock current_block;
|
||||||
|
u32 current_label = static_cast<u32>(exit_branch);
|
||||||
|
for (auto& block : blocks) {
|
||||||
|
if (shader_info.labels.count(block.start) != 0) {
|
||||||
|
insert_block(current_block, current_label);
|
||||||
|
current_block.clear();
|
||||||
|
current_label = block.start;
|
||||||
|
}
|
||||||
|
if (!block.ignore_branch) {
|
||||||
|
DecodeRangeInner(current_block, block.start, block.end);
|
||||||
|
InsertControlFlow(current_block, block);
|
||||||
|
} else {
|
||||||
|
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insert_block(current_block, current_label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompileDepth::DecompileBackwards:
|
||||||
|
case CompileDepth::FullDecompile: {
|
||||||
|
program_manager = std::move(shader_info.manager);
|
||||||
|
disable_flow_stack = true;
|
||||||
decompiled = true;
|
decompiled = true;
|
||||||
ASTDecoder decoder{*this};
|
ASTDecoder decoder{*this};
|
||||||
ASTNode program = GetASTProgram();
|
ASTNode program = GetASTProgram();
|
||||||
decoder.Visit(program);
|
decoder.Visit(program);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method");
|
default:
|
||||||
// we can't decompile it, fallback to standard method
|
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||||
for (const auto& block : shader_info.blocks) {
|
[[fallthrough]];
|
||||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
case CompileDepth::BruteForce: {
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling");
|
|
||||||
|
|
||||||
// Now we need to deal with an undecompilable shader. We need to brute force
|
|
||||||
// a shader that captures every position.
|
|
||||||
coverage_begin = main_offset;
|
coverage_begin = main_offset;
|
||||||
const u32 shader_end = static_cast<u32>(program_size / sizeof(u64));
|
const u32 shader_end = static_cast<u32>(program_size / sizeof(u64));
|
||||||
coverage_end = shader_end;
|
coverage_end = shader_end;
|
||||||
for (u32 label = main_offset; label < shader_end; label++) {
|
for (u32 label = main_offset; label < shader_end; label++) {
|
||||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (settings.depth != shader_info.settings.depth) {
|
||||||
|
LOG_WARNING(
|
||||||
|
HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||||
|
CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) {
|
NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) {
|
||||||
|
|
|
@ -157,7 +157,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||||
"Constant buffer flow is not supported");
|
"Constant buffer flow is not supported");
|
||||||
|
|
||||||
if (decompiled) {
|
if (disable_flow_stack) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||||
"Constant buffer PBK is not supported");
|
"Constant buffer PBK is not supported");
|
||||||
|
|
||||||
if (decompiled) {
|
if (disable_flow_stack) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
|
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
|
||||||
static_cast<u32>(cc));
|
static_cast<u32>(cc));
|
||||||
|
|
||||||
if (decompiled) {
|
if (disable_flow_stack) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
|
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
|
||||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
|
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
|
||||||
static_cast<u32>(cc));
|
static_cast<u32>(cc));
|
||||||
if (decompiled) {
|
if (disable_flow_stack) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,10 @@ using Tegra::Shader::PredCondition;
|
||||||
using Tegra::Shader::PredOperation;
|
using Tegra::Shader::PredOperation;
|
||||||
using Tegra::Shader::Register;
|
using Tegra::Shader::Register;
|
||||||
|
|
||||||
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size)
|
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size,
|
||||||
: program_code{program_code}, main_offset{main_offset}, program_size{size}, program_manager{} {
|
CompilerSettings settings)
|
||||||
|
: program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{},
|
||||||
|
program_manager{true}, settings{settings} {
|
||||||
Decode();
|
Decode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "video_core/engines/shader_bytecode.h"
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
#include "video_core/engines/shader_header.h"
|
#include "video_core/engines/shader_header.h"
|
||||||
#include "video_core/shader/ast.h"
|
#include "video_core/shader/ast.h"
|
||||||
|
#include "video_core/shader/compiler_settings.h"
|
||||||
#include "video_core/shader/node.h"
|
#include "video_core/shader/node.h"
|
||||||
|
|
||||||
namespace VideoCommon::Shader {
|
namespace VideoCommon::Shader {
|
||||||
|
@ -65,7 +66,8 @@ struct GlobalMemoryUsage {
|
||||||
|
|
||||||
class ShaderIR final {
|
class ShaderIR final {
|
||||||
public:
|
public:
|
||||||
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size);
|
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size,
|
||||||
|
CompilerSettings settings);
|
||||||
~ShaderIR();
|
~ShaderIR();
|
||||||
|
|
||||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||||
|
@ -141,6 +143,10 @@ public:
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsFlowStackDisabled() const {
|
||||||
|
return disable_flow_stack;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsDecompiled() const {
|
bool IsDecompiled() const {
|
||||||
return decompiled;
|
return decompiled;
|
||||||
}
|
}
|
||||||
|
@ -368,6 +374,7 @@ private:
|
||||||
const u32 main_offset;
|
const u32 main_offset;
|
||||||
const std::size_t program_size;
|
const std::size_t program_size;
|
||||||
bool decompiled{};
|
bool decompiled{};
|
||||||
|
bool disable_flow_stack{};
|
||||||
|
|
||||||
u32 coverage_begin{};
|
u32 coverage_begin{};
|
||||||
u32 coverage_end{};
|
u32 coverage_end{};
|
||||||
|
@ -375,6 +382,7 @@ private:
|
||||||
std::map<u32, NodeBlock> basic_blocks;
|
std::map<u32, NodeBlock> basic_blocks;
|
||||||
NodeBlock global_code;
|
NodeBlock global_code;
|
||||||
ASTManager program_manager;
|
ASTManager program_manager;
|
||||||
|
CompilerSettings settings{};
|
||||||
|
|
||||||
std::set<u32> used_registers;
|
std::set<u32> used_registers;
|
||||||
std::set<Tegra::Shader::Pred> used_predicates;
|
std::set<Tegra::Shader::Pred> used_predicates;
|
||||||
|
|
Loading…
Reference in a new issue