From 11c2f11872fa8db5f3a07705078aacf6662c0b63 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 25 Mar 2018 16:08:27 +0300 Subject: [PATCH] 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 --- .../renderer_opengl/gl_shader_decompiler.cpp | 42 +++++++++++++------ .../renderer_opengl/gl_shader_decompiler.h | 9 ++-- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index ad4d56932..6b5a75497 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -21,6 +22,11 @@ using nihstro::SwizzlePattern; 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. enum class ExitMethod { Undetermined, ///< Internal value. Only occur when analyzing JMP loop. @@ -54,7 +60,8 @@ public: // Recursively finds all subroutines. 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 GetSubroutines() { @@ -74,6 +81,8 @@ private: Subroutine subroutine{begin, end}; 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; } @@ -187,7 +196,7 @@ private: class ShaderWriter { public: void AddLine(const std::string& text) { - ASSERT(scope >= 0); + DEBUG_ASSERT(scope >= 0); if (!text.empty()) { shader_source += std::string(static_cast(scope) * 4, ' '); } @@ -377,7 +386,7 @@ private: if (reg.empty() || dest_mask_num_components == 0) { 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 : ""); @@ -560,7 +569,7 @@ private: LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex); - DEBUG_ASSERT(false); + throw DecompileFail("Unhandled instruction"); break; } } @@ -604,6 +613,7 @@ private: LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex); + throw DecompileFail("Unhandled instruction"); } break; } @@ -757,6 +767,7 @@ private: LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex); + throw DecompileFail("Unhandled instruction"); break; } } @@ -862,7 +873,7 @@ private: --shader.scope; 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, - u32 main_offset, const RegGetter& inputreg_getter, - const RegGetter& outputreg_getter, bool sanitize_mul, bool is_gs) { +boost::optional DecompileProgram(const ProgramCode& program_code, + const SwizzleData& swizzle_data, u32 main_offset, + const RegGetter& inputreg_getter, + const RegGetter& outputreg_getter, bool sanitize_mul, + bool is_gs) { - auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines(); - GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, inputreg_getter, - outputreg_getter, sanitize_mul, is_gs); - return generator.GetShaderCode(); + try { + auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines(); + GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, + 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 diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index d1717147c..91a71ce13 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "common/common_types.h" #include "video_core/shader/shader.h" @@ -18,9 +19,11 @@ using RegGetter = std::function; std::string GetCommonDeclarations(); -std::string DecompileProgram(const ProgramCode& program_code, const SwizzleData& swizzle_data, - u32 main_offset, const RegGetter& inputreg_getter, - const RegGetter& outputreg_getter, bool sanitize_mul, bool is_gs); +boost::optional DecompileProgram(const ProgramCode& program_code, + const SwizzleData& swizzle_data, u32 main_offset, + const RegGetter& inputreg_getter, + const RegGetter& outputreg_getter, bool sanitize_mul, + bool is_gs); } // namespace Decompiler } // namespace Shader