gl_shader_decompiler: return error on decompilation failure
Internally these errors are handled by exceptions. Only fallbackable errors (that can be handled by CPU shader emulation) is reported. Completely ill-formed shader is still ASSERTed. Code logic related stuff is DEBUG_ASSERTed
This commit is contained in:
parent
4991b15ee5
commit
11c2f11872
2 changed files with 36 additions and 15 deletions
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <nihstro/shader_bytecode.h>
|
#include <nihstro/shader_bytecode.h>
|
||||||
|
@ -21,6 +22,11 @@ using nihstro::SwizzlePattern;
|
||||||
|
|
||||||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
||||||
|
|
||||||
|
class DecompileFail : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
/// Describes the behaviour of code path of a given entry point and a return point.
|
/// Describes the behaviour of code path of a given entry point and a return point.
|
||||||
enum class ExitMethod {
|
enum class ExitMethod {
|
||||||
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
||||||
|
@ -54,7 +60,8 @@ public:
|
||||||
|
|
||||||
// Recursively finds all subroutines.
|
// Recursively finds all subroutines.
|
||||||
const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END);
|
const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END);
|
||||||
ASSERT(program_main.exit_method == ExitMethod::AlwaysEnd);
|
if (program_main.exit_method != ExitMethod::AlwaysEnd)
|
||||||
|
throw DecompileFail("Program does not always end");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<Subroutine> GetSubroutines() {
|
std::set<Subroutine> GetSubroutines() {
|
||||||
|
@ -74,6 +81,8 @@ private:
|
||||||
|
|
||||||
Subroutine subroutine{begin, end};
|
Subroutine subroutine{begin, end};
|
||||||
subroutine.exit_method = Scan(begin, end, subroutine.labels);
|
subroutine.exit_method = Scan(begin, end, subroutine.labels);
|
||||||
|
if (subroutine.exit_method == ExitMethod::Undetermined)
|
||||||
|
throw DecompileFail("Recursive function detected");
|
||||||
return *subroutines.insert(std::move(subroutine)).first;
|
return *subroutines.insert(std::move(subroutine)).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +196,7 @@ private:
|
||||||
class ShaderWriter {
|
class ShaderWriter {
|
||||||
public:
|
public:
|
||||||
void AddLine(const std::string& text) {
|
void AddLine(const std::string& text) {
|
||||||
ASSERT(scope >= 0);
|
DEBUG_ASSERT(scope >= 0);
|
||||||
if (!text.empty()) {
|
if (!text.empty()) {
|
||||||
shader_source += std::string(static_cast<size_t>(scope) * 4, ' ');
|
shader_source += std::string(static_cast<size_t>(scope) * 4, ' ');
|
||||||
}
|
}
|
||||||
|
@ -377,7 +386,7 @@ private:
|
||||||
if (reg.empty() || dest_mask_num_components == 0) {
|
if (reg.empty() || dest_mask_num_components == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASSERT(value_num_components >= dest_num_components || value_num_components == 1);
|
DEBUG_ASSERT(value_num_components >= dest_num_components || value_num_components == 1);
|
||||||
|
|
||||||
std::string dest = reg + (dest_num_components != 1 ? dest_mask_swizzle : "");
|
std::string dest = reg + (dest_num_components != 1 ? dest_mask_swizzle : "");
|
||||||
|
|
||||||
|
@ -560,7 +569,7 @@ private:
|
||||||
LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x",
|
LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x",
|
||||||
(int)instr.opcode.Value().EffectiveOpCode(),
|
(int)instr.opcode.Value().EffectiveOpCode(),
|
||||||
instr.opcode.Value().GetInfo().name, instr.hex);
|
instr.opcode.Value().GetInfo().name, instr.hex);
|
||||||
DEBUG_ASSERT(false);
|
throw DecompileFail("Unhandled instruction");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,6 +613,7 @@ private:
|
||||||
LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x",
|
LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x",
|
||||||
(int)instr.opcode.Value().EffectiveOpCode(),
|
(int)instr.opcode.Value().EffectiveOpCode(),
|
||||||
instr.opcode.Value().GetInfo().name, instr.hex);
|
instr.opcode.Value().GetInfo().name, instr.hex);
|
||||||
|
throw DecompileFail("Unhandled instruction");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -757,6 +767,7 @@ private:
|
||||||
LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
|
LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
|
||||||
(int)instr.opcode.Value().EffectiveOpCode(),
|
(int)instr.opcode.Value().EffectiveOpCode(),
|
||||||
instr.opcode.Value().GetInfo().name, instr.hex);
|
instr.opcode.Value().GetInfo().name, instr.hex);
|
||||||
|
throw DecompileFail("Unhandled instruction");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,7 +873,7 @@ private:
|
||||||
--shader.scope;
|
--shader.scope;
|
||||||
shader.AddLine("}\n");
|
shader.AddLine("}\n");
|
||||||
|
|
||||||
ASSERT(shader.scope == 0);
|
DEBUG_ASSERT(shader.scope == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,14 +903,21 @@ bool exec_shader();
|
||||||
)";
|
)";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DecompileProgram(const ProgramCode& program_code, const SwizzleData& swizzle_data,
|
boost::optional<std::string> DecompileProgram(const ProgramCode& program_code,
|
||||||
u32 main_offset, const RegGetter& inputreg_getter,
|
const SwizzleData& swizzle_data, u32 main_offset,
|
||||||
const RegGetter& outputreg_getter, bool sanitize_mul, bool is_gs) {
|
const RegGetter& inputreg_getter,
|
||||||
|
const RegGetter& outputreg_getter, bool sanitize_mul,
|
||||||
|
bool is_gs) {
|
||||||
|
|
||||||
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
|
try {
|
||||||
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, inputreg_getter,
|
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
|
||||||
outputreg_getter, sanitize_mul, is_gs);
|
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset,
|
||||||
return generator.GetShaderCode();
|
inputreg_getter, outputreg_getter, sanitize_mul, is_gs);
|
||||||
|
return generator.GetShaderCode();
|
||||||
|
} catch (const DecompileFail& exception) {
|
||||||
|
LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what());
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Decompiler
|
} // namespace Decompiler
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/shader/shader.h"
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
|
@ -18,9 +19,11 @@ using RegGetter = std::function<std::string(u32)>;
|
||||||
|
|
||||||
std::string GetCommonDeclarations();
|
std::string GetCommonDeclarations();
|
||||||
|
|
||||||
std::string DecompileProgram(const ProgramCode& program_code, const SwizzleData& swizzle_data,
|
boost::optional<std::string> DecompileProgram(const ProgramCode& program_code,
|
||||||
u32 main_offset, const RegGetter& inputreg_getter,
|
const SwizzleData& swizzle_data, u32 main_offset,
|
||||||
const RegGetter& outputreg_getter, bool sanitize_mul, bool is_gs);
|
const RegGetter& inputreg_getter,
|
||||||
|
const RegGetter& outputreg_getter, bool sanitize_mul,
|
||||||
|
bool is_gs);
|
||||||
|
|
||||||
} // namespace Decompiler
|
} // namespace Decompiler
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
Loading…
Reference in a new issue