mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Implement macro JIT
This commit is contained in:
		
							parent
							
								
									9d9ffe0f94
								
							
						
					
					
						commit
						b032ebdfee
					
				| @ -474,6 +474,7 @@ struct Values { | |||||||
|     bool reporting_services; |     bool reporting_services; | ||||||
|     bool quest_flag; |     bool quest_flag; | ||||||
|     bool disable_cpu_opt; |     bool disable_cpu_opt; | ||||||
|  |     bool disable_macro_jit; | ||||||
| 
 | 
 | ||||||
|     // BCAT
 |     // BCAT
 | ||||||
|     std::string bcat_backend; |     std::string bcat_backend; | ||||||
|  | |||||||
| @ -25,6 +25,12 @@ add_library(video_core STATIC | |||||||
|     engines/shader_bytecode.h |     engines/shader_bytecode.h | ||||||
|     engines/shader_header.h |     engines/shader_header.h | ||||||
|     engines/shader_type.h |     engines/shader_type.h | ||||||
|  |     macro/macro.cpp | ||||||
|  |     macro/macro.h | ||||||
|  |     macro/macro_interpreter.cpp | ||||||
|  |     macro/macro_interpreter.h | ||||||
|  |     macro/macro_jit_x64.cpp | ||||||
|  |     macro/macro_jit_x64.h | ||||||
|     fence_manager.h |     fence_manager.h | ||||||
|     gpu.cpp |     gpu.cpp | ||||||
|     gpu.h |     gpu.h | ||||||
| @ -36,8 +42,6 @@ add_library(video_core STATIC | |||||||
|     gpu_thread.h |     gpu_thread.h | ||||||
|     guest_driver.cpp |     guest_driver.cpp | ||||||
|     guest_driver.h |     guest_driver.h | ||||||
|     macro_interpreter.cpp |  | ||||||
|     macro_interpreter.h |  | ||||||
|     memory_manager.cpp |     memory_manager.cpp | ||||||
|     memory_manager.h |     memory_manager.h | ||||||
|     morton.cpp |     morton.cpp | ||||||
|  | |||||||
| @ -25,9 +25,8 @@ constexpr u32 MacroRegistersStart = 0xE00; | |||||||
| Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||||||
|                      MemoryManager& memory_manager) |                      MemoryManager& memory_manager) | ||||||
|     : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, |     : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, | ||||||
|       macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { |       macro_engine(GetMacroEngine(*this)), upload_state{memory_manager, regs.upload} { | ||||||
|     dirty.flags.flip(); |     dirty.flags.flip(); | ||||||
| 
 |  | ||||||
|     InitializeRegisterDefaults(); |     InitializeRegisterDefaults(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -116,7 +115,7 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||||||
|     mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; |     mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32>&& parameters) { | ||||||
|     // Reset the current macro.
 |     // Reset the current macro.
 | ||||||
|     executing_macro = 0; |     executing_macro = 0; | ||||||
| 
 | 
 | ||||||
| @ -125,7 +124,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3 | |||||||
|         ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); |         ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); | ||||||
| 
 | 
 | ||||||
|     // Execute the current macro.
 |     // Execute the current macro.
 | ||||||
|     macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); |     macro_engine->Execute(macro_positions[entry], std::move(parameters)); | ||||||
|     if (mme_draw.current_mode != MMEDrawMode::Undefined) { |     if (mme_draw.current_mode != MMEDrawMode::Undefined) { | ||||||
|         FlushMMEInlineDraw(); |         FlushMMEInlineDraw(); | ||||||
|     } |     } | ||||||
| @ -161,8 +160,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | |||||||
| 
 | 
 | ||||||
|         // Call the macro when there are no more parameters in the command buffer
 |         // Call the macro when there are no more parameters in the command buffer
 | ||||||
|         if (is_last_call) { |         if (is_last_call) { | ||||||
|             CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); |             CallMacroMethod(executing_macro, std::move(macro_params)); | ||||||
|             macro_params.clear(); |  | ||||||
|         } |         } | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @ -197,7 +195,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case MAXWELL3D_REG_INDEX(macros.data): { |     case MAXWELL3D_REG_INDEX(macros.data): { | ||||||
|         ProcessMacroUpload(arg); |         macro_engine->AddCode(regs.macros.upload_address, arg); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case MAXWELL3D_REG_INDEX(macros.bind): { |     case MAXWELL3D_REG_INDEX(macros.bind): { | ||||||
| @ -306,8 +304,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | |||||||
| 
 | 
 | ||||||
|         // Call the macro when there are no more parameters in the command buffer
 |         // Call the macro when there are no more parameters in the command buffer
 | ||||||
|         if (amount == methods_pending) { |         if (amount == methods_pending) { | ||||||
|             CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); |             CallMacroMethod(executing_macro, std::move(macro_params)); | ||||||
|             macro_params.clear(); |  | ||||||
|         } |         } | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @ -420,9 +417,7 @@ void Maxwell3D::FlushMMEInlineDraw() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Maxwell3D::ProcessMacroUpload(u32 data) { | void Maxwell3D::ProcessMacroUpload(u32 data) { | ||||||
|     ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), |     macro_engine->AddCode(regs.macros.upload_address++, data); | ||||||
|                "upload_address exceeded macro_memory size!"); |  | ||||||
|     macro_memory[regs.macros.upload_address++] = data; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Maxwell3D::ProcessMacroBind(u32 data) { | void Maxwell3D::ProcessMacroBind(u32 data) { | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ | |||||||
| #include "video_core/engines/engine_upload.h" | #include "video_core/engines/engine_upload.h" | ||||||
| #include "video_core/engines/shader_type.h" | #include "video_core/engines/shader_type.h" | ||||||
| #include "video_core/gpu.h" | #include "video_core/gpu.h" | ||||||
| #include "video_core/macro_interpreter.h" | #include "video_core/macro/macro.h" | ||||||
| #include "video_core/textures/texture.h" | #include "video_core/textures/texture.h" | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| @ -1411,15 +1411,6 @@ public: | |||||||
| 
 | 
 | ||||||
|     const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; |     const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||||||
| 
 | 
 | ||||||
|     /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
 |  | ||||||
|     /// we've seen used.
 |  | ||||||
|     using MacroMemory = std::array<u32, 0x40000>; |  | ||||||
| 
 |  | ||||||
|     /// Gets a reference to macro memory.
 |  | ||||||
|     const MacroMemory& GetMacroMemory() const { |  | ||||||
|         return macro_memory; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool ShouldExecute() const { |     bool ShouldExecute() const { | ||||||
|         return execute_on; |         return execute_on; | ||||||
|     } |     } | ||||||
| @ -1468,16 +1459,14 @@ private: | |||||||
| 
 | 
 | ||||||
|     std::array<bool, Regs::NUM_REGS> mme_inline{}; |     std::array<bool, Regs::NUM_REGS> mme_inline{}; | ||||||
| 
 | 
 | ||||||
|     /// Memory for macro code
 |  | ||||||
|     MacroMemory macro_memory; |  | ||||||
| 
 |  | ||||||
|     /// Macro method that is currently being executed / being fed parameters.
 |     /// Macro method that is currently being executed / being fed parameters.
 | ||||||
|     u32 executing_macro = 0; |     u32 executing_macro = 0; | ||||||
|     /// Parameters that have been submitted to the macro call so far.
 |     /// Parameters that have been submitted to the macro call so far.
 | ||||||
|     std::vector<u32> macro_params; |     std::vector<u32> macro_params; | ||||||
| 
 | 
 | ||||||
|     /// Interpreter for the macro codes uploaded to the GPU.
 |     /// Interpreter for the macro codes uploaded to the GPU.
 | ||||||
|     MacroInterpreter macro_interpreter; |     std::unique_ptr<MacroEngine> macro_engine; | ||||||
|  |     // MacroInterpreter macro_interpreter;
 | ||||||
| 
 | 
 | ||||||
|     static constexpr u32 null_cb_data = 0xFFFFFFFF; |     static constexpr u32 null_cb_data = 0xFFFFFFFF; | ||||||
|     struct { |     struct { | ||||||
| @ -1506,7 +1495,7 @@ private: | |||||||
|      * @param num_parameters Number of arguments |      * @param num_parameters Number of arguments | ||||||
|      * @param parameters Arguments to the method call |      * @param parameters Arguments to the method call | ||||||
|      */ |      */ | ||||||
|     void CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters); |     void CallMacroMethod(u32 method, std::vector<u32>&& parameters); | ||||||
| 
 | 
 | ||||||
|     /// Handles writes to the macro uploading register.
 |     /// Handles writes to the macro uploading register.
 | ||||||
|     void ProcessMacroUpload(u32 data); |     void ProcessMacroUpload(u32 data); | ||||||
|  | |||||||
							
								
								
									
										45
									
								
								src/video_core/macro/macro.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/video_core/macro/macro.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/settings.h" | ||||||
|  | #include "video_core/macro/macro.h" | ||||||
|  | #include "video_core/macro/macro_interpreter.h" | ||||||
|  | #include "video_core/macro/macro_jit_x64.h" | ||||||
|  | 
 | ||||||
|  | namespace Tegra { | ||||||
|  | 
 | ||||||
|  | void MacroEngine::AddCode(u32 method, u32 data) { | ||||||
|  |     uploaded_macro_code[method].push_back(data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroEngine::Execute(u32 method, std::vector<u32> parameters) { | ||||||
|  |     auto compiled_macro = macro_cache.find(method); | ||||||
|  |     if (compiled_macro != macro_cache.end()) { | ||||||
|  |         compiled_macro->second->Execute(parameters, method); | ||||||
|  |     } else { | ||||||
|  |         // Macro not compiled, check if it's uploaded and if so, compile it
 | ||||||
|  |         auto macro_code = uploaded_macro_code.find(method); | ||||||
|  |         if (macro_code == uploaded_macro_code.end()) { | ||||||
|  |             UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         macro_cache[method] = Compile(macro_code->second); | ||||||
|  |         macro_cache[method]->Execute(parameters, method); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d) { | ||||||
|  |     if (Settings::values.disable_macro_jit) { | ||||||
|  |         return std::make_unique<MacroInterpreter>(maxwell3d); | ||||||
|  |     } | ||||||
|  | #ifdef ARCHITECTURE_x86_64 | ||||||
|  |     return std::make_unique<MacroJITx64>(maxwell3d); | ||||||
|  | #else | ||||||
|  |     return std::make_unique<MacroInterpreter>(maxwell3d); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Tegra
 | ||||||
							
								
								
									
										128
									
								
								src/video_core/macro/macro.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/video_core/macro/macro.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <vector> | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace Tegra { | ||||||
|  | namespace Engines { | ||||||
|  | class Maxwell3D; | ||||||
|  | } | ||||||
|  | namespace Macro { | ||||||
|  | constexpr std::size_t NUM_MACRO_REGISTERS = 8; | ||||||
|  | enum class Operation : u32 { | ||||||
|  |     ALU = 0, | ||||||
|  |     AddImmediate = 1, | ||||||
|  |     ExtractInsert = 2, | ||||||
|  |     ExtractShiftLeftImmediate = 3, | ||||||
|  |     ExtractShiftLeftRegister = 4, | ||||||
|  |     Read = 5, | ||||||
|  |     Unused = 6, // This operation doesn't seem to be a valid encoding.
 | ||||||
|  |     Branch = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class ALUOperation : u32 { | ||||||
|  |     Add = 0, | ||||||
|  |     AddWithCarry = 1, | ||||||
|  |     Subtract = 2, | ||||||
|  |     SubtractWithBorrow = 3, | ||||||
|  |     // Operations 4-7 don't seem to be valid encodings.
 | ||||||
|  |     Xor = 8, | ||||||
|  |     Or = 9, | ||||||
|  |     And = 10, | ||||||
|  |     AndNot = 11, | ||||||
|  |     Nand = 12 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class ResultOperation : u32 { | ||||||
|  |     IgnoreAndFetch = 0, | ||||||
|  |     Move = 1, | ||||||
|  |     MoveAndSetMethod = 2, | ||||||
|  |     FetchAndSend = 3, | ||||||
|  |     MoveAndSend = 4, | ||||||
|  |     FetchAndSetMethod = 5, | ||||||
|  |     MoveAndSetMethodFetchAndSend = 6, | ||||||
|  |     MoveAndSetMethodSend = 7 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class BranchCondition : u32 { | ||||||
|  |     Zero = 0, | ||||||
|  |     NotZero = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | union Opcode { | ||||||
|  |     u32 raw; | ||||||
|  |     BitField<0, 3, Operation> operation; | ||||||
|  |     BitField<4, 3, ResultOperation> result_operation; | ||||||
|  |     BitField<4, 1, BranchCondition> branch_condition; | ||||||
|  |     // If set on a branch, then the branch doesn't have a delay slot.
 | ||||||
|  |     BitField<5, 1, u32> branch_annul; | ||||||
|  |     BitField<7, 1, u32> is_exit; | ||||||
|  |     BitField<8, 3, u32> dst; | ||||||
|  |     BitField<11, 3, u32> src_a; | ||||||
|  |     BitField<14, 3, u32> src_b; | ||||||
|  |     // The signed immediate overlaps the second source operand and the alu operation.
 | ||||||
|  |     BitField<14, 18, s32> immediate; | ||||||
|  | 
 | ||||||
|  |     BitField<17, 5, ALUOperation> alu_operation; | ||||||
|  | 
 | ||||||
|  |     // Bitfield instructions data
 | ||||||
|  |     BitField<17, 5, u32> bf_src_bit; | ||||||
|  |     BitField<22, 5, u32> bf_size; | ||||||
|  |     BitField<27, 5, u32> bf_dst_bit; | ||||||
|  | 
 | ||||||
|  |     u32 GetBitfieldMask() const { | ||||||
|  |         return (1 << bf_size) - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     s32 GetBranchTarget() const { | ||||||
|  |         return static_cast<s32>(immediate * sizeof(u32)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | union MethodAddress { | ||||||
|  |     u32 raw; | ||||||
|  |     BitField<0, 12, u32> address; | ||||||
|  |     BitField<12, 6, u32> increment; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Macro
 | ||||||
|  | 
 | ||||||
|  | class CachedMacro { | ||||||
|  | public: | ||||||
|  |     virtual ~CachedMacro() = default; | ||||||
|  |     /**
 | ||||||
|  |      * Executes the macro code with the specified input parameters. | ||||||
|  |      * @param code The macro byte code to execute | ||||||
|  |      * @param parameters The parameters of the macro | ||||||
|  |      */ | ||||||
|  |     virtual void Execute(std::vector<u32>& parameters, u32 method) = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class MacroEngine { | ||||||
|  | public: | ||||||
|  |     virtual ~MacroEngine() = default; | ||||||
|  | 
 | ||||||
|  |     // Store the uploaded macro code to compile them when they're called.
 | ||||||
|  |     void AddCode(u32 method, u32 data); | ||||||
|  | 
 | ||||||
|  |     // Compiles the macro if its not in the cache, and executes the compiled macro
 | ||||||
|  |     void Execute(u32 method, std::vector<u32> parameters); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unordered_map<u32, std::unique_ptr<CachedMacro>> macro_cache; | ||||||
|  |     std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); | ||||||
|  | 
 | ||||||
|  | } // namespace Tegra
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2018 yuzu Emulator Project
 | // Copyright 2020 yuzu Emulator Project
 | ||||||
| // 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.
 | ||||||
| 
 | 
 | ||||||
| @ -6,109 +6,46 @@ | |||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "video_core/engines/maxwell_3d.h" | #include "video_core/engines/maxwell_3d.h" | ||||||
| #include "video_core/macro_interpreter.h" | #include "video_core/macro/macro_interpreter.h" | ||||||
| 
 | 
 | ||||||
| MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); | MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); | ||||||
| 
 | 
 | ||||||
| namespace Tegra { | namespace Tegra { | ||||||
| namespace { |  | ||||||
| enum class Operation : u32 { |  | ||||||
|     ALU = 0, |  | ||||||
|     AddImmediate = 1, |  | ||||||
|     ExtractInsert = 2, |  | ||||||
|     ExtractShiftLeftImmediate = 3, |  | ||||||
|     ExtractShiftLeftRegister = 4, |  | ||||||
|     Read = 5, |  | ||||||
|     Unused = 6, // This operation doesn't seem to be a valid encoding.
 |  | ||||||
|     Branch = 7, |  | ||||||
| }; |  | ||||||
| } // Anonymous namespace
 |  | ||||||
| 
 |  | ||||||
| enum class MacroInterpreter::ALUOperation : u32 { |  | ||||||
|     Add = 0, |  | ||||||
|     AddWithCarry = 1, |  | ||||||
|     Subtract = 2, |  | ||||||
|     SubtractWithBorrow = 3, |  | ||||||
|     // Operations 4-7 don't seem to be valid encodings.
 |  | ||||||
|     Xor = 8, |  | ||||||
|     Or = 9, |  | ||||||
|     And = 10, |  | ||||||
|     AndNot = 11, |  | ||||||
|     Nand = 12 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum class MacroInterpreter::ResultOperation : u32 { |  | ||||||
|     IgnoreAndFetch = 0, |  | ||||||
|     Move = 1, |  | ||||||
|     MoveAndSetMethod = 2, |  | ||||||
|     FetchAndSend = 3, |  | ||||||
|     MoveAndSend = 4, |  | ||||||
|     FetchAndSetMethod = 5, |  | ||||||
|     MoveAndSetMethodFetchAndSend = 6, |  | ||||||
|     MoveAndSetMethodSend = 7 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum class MacroInterpreter::BranchCondition : u32 { |  | ||||||
|     Zero = 0, |  | ||||||
|     NotZero = 1, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| union MacroInterpreter::Opcode { |  | ||||||
|     u32 raw; |  | ||||||
|     BitField<0, 3, Operation> operation; |  | ||||||
|     BitField<4, 3, ResultOperation> result_operation; |  | ||||||
|     BitField<4, 1, BranchCondition> branch_condition; |  | ||||||
|     // If set on a branch, then the branch doesn't have a delay slot.
 |  | ||||||
|     BitField<5, 1, u32> branch_annul; |  | ||||||
|     BitField<7, 1, u32> is_exit; |  | ||||||
|     BitField<8, 3, u32> dst; |  | ||||||
|     BitField<11, 3, u32> src_a; |  | ||||||
|     BitField<14, 3, u32> src_b; |  | ||||||
|     // The signed immediate overlaps the second source operand and the alu operation.
 |  | ||||||
|     BitField<14, 18, s32> immediate; |  | ||||||
| 
 |  | ||||||
|     BitField<17, 5, ALUOperation> alu_operation; |  | ||||||
| 
 |  | ||||||
|     // Bitfield instructions data
 |  | ||||||
|     BitField<17, 5, u32> bf_src_bit; |  | ||||||
|     BitField<22, 5, u32> bf_size; |  | ||||||
|     BitField<27, 5, u32> bf_dst_bit; |  | ||||||
| 
 |  | ||||||
|     u32 GetBitfieldMask() const { |  | ||||||
|         return (1 << bf_size) - 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     s32 GetBranchTarget() const { |  | ||||||
|         return static_cast<s32>(immediate * sizeof(u32)); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::Execute(u32 offset, std::size_t num_parameters, const u32* parameters) { | std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { | ||||||
|  |     return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MacroInterpreterImpl::MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d, | ||||||
|  |                                            const std::vector<u32>& code) | ||||||
|  |     : maxwell3d(maxwell3d), code(code) {} | ||||||
|  | 
 | ||||||
|  | void MacroInterpreterImpl::Execute(std::vector<u32>& parameters, u32 method) { | ||||||
|     MICROPROFILE_SCOPE(MacroInterp); |     MICROPROFILE_SCOPE(MacroInterp); | ||||||
|     Reset(); |     Reset(); | ||||||
| 
 | 
 | ||||||
|     registers[1] = parameters[0]; |     registers[1] = parameters[0]; | ||||||
|  |     num_parameters = parameters.size(); | ||||||
| 
 | 
 | ||||||
|     if (num_parameters > parameters_capacity) { |     if (num_parameters > parameters_capacity) { | ||||||
|         parameters_capacity = num_parameters; |         parameters_capacity = num_parameters; | ||||||
|         this->parameters = std::make_unique<u32[]>(num_parameters); |         this->parameters = std::make_unique<u32[]>(num_parameters); | ||||||
|     } |     } | ||||||
|     std::memcpy(this->parameters.get(), parameters, num_parameters * sizeof(u32)); |     std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); | ||||||
|     this->num_parameters = num_parameters; |     this->num_parameters = num_parameters; | ||||||
| 
 | 
 | ||||||
|     // Execute the code until we hit an exit condition.
 |     // Execute the code until we hit an exit condition.
 | ||||||
|     bool keep_executing = true; |     bool keep_executing = true; | ||||||
|     while (keep_executing) { |     while (keep_executing) { | ||||||
|         keep_executing = Step(offset, false); |         keep_executing = Step(false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Assert the the macro used all the input parameters
 |     // Assert the the macro used all the input parameters
 | ||||||
|     ASSERT(next_parameter_index == num_parameters); |     ASSERT(next_parameter_index == num_parameters); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::Reset() { | void MacroInterpreterImpl::Reset() { | ||||||
|     registers = {}; |     registers = {}; | ||||||
|     pc = 0; |     pc = 0; | ||||||
|     delayed_pc = {}; |     delayed_pc = {}; | ||||||
| @ -120,10 +57,10 @@ void MacroInterpreter::Reset() { | |||||||
|     carry_flag = false; |     carry_flag = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | bool MacroInterpreterImpl::Step(bool is_delay_slot) { | ||||||
|     u32 base_address = pc; |     u32 base_address = pc; | ||||||
| 
 | 
 | ||||||
|     Opcode opcode = GetOpcode(offset); |     Macro::Opcode opcode = GetOpcode(); | ||||||
|     pc += 4; |     pc += 4; | ||||||
| 
 | 
 | ||||||
|     // Update the program counter if we were delayed
 |     // Update the program counter if we were delayed
 | ||||||
| @ -134,18 +71,18 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     switch (opcode.operation) { |     switch (opcode.operation) { | ||||||
|     case Operation::ALU: { |     case Macro::Operation::ALU: { | ||||||
|         u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), |         u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), | ||||||
|                                   GetRegister(opcode.src_b)); |                                   GetRegister(opcode.src_b)); | ||||||
|         ProcessResult(opcode.result_operation, opcode.dst, result); |         ProcessResult(opcode.result_operation, opcode.dst, result); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::AddImmediate: { |     case Macro::Operation::AddImmediate: { | ||||||
|         ProcessResult(opcode.result_operation, opcode.dst, |         ProcessResult(opcode.result_operation, opcode.dst, | ||||||
|                       GetRegister(opcode.src_a) + opcode.immediate); |                       GetRegister(opcode.src_a) + opcode.immediate); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::ExtractInsert: { |     case Macro::Operation::ExtractInsert: { | ||||||
|         u32 dst = GetRegister(opcode.src_a); |         u32 dst = GetRegister(opcode.src_a); | ||||||
|         u32 src = GetRegister(opcode.src_b); |         u32 src = GetRegister(opcode.src_b); | ||||||
| 
 | 
 | ||||||
| @ -155,7 +92,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
|         ProcessResult(opcode.result_operation, opcode.dst, dst); |         ProcessResult(opcode.result_operation, opcode.dst, dst); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::ExtractShiftLeftImmediate: { |     case Macro::Operation::ExtractShiftLeftImmediate: { | ||||||
|         u32 dst = GetRegister(opcode.src_a); |         u32 dst = GetRegister(opcode.src_a); | ||||||
|         u32 src = GetRegister(opcode.src_b); |         u32 src = GetRegister(opcode.src_b); | ||||||
| 
 | 
 | ||||||
| @ -164,7 +101,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
|         ProcessResult(opcode.result_operation, opcode.dst, result); |         ProcessResult(opcode.result_operation, opcode.dst, result); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::ExtractShiftLeftRegister: { |     case Macro::Operation::ExtractShiftLeftRegister: { | ||||||
|         u32 dst = GetRegister(opcode.src_a); |         u32 dst = GetRegister(opcode.src_a); | ||||||
|         u32 src = GetRegister(opcode.src_b); |         u32 src = GetRegister(opcode.src_b); | ||||||
| 
 | 
 | ||||||
| @ -173,12 +110,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
|         ProcessResult(opcode.result_operation, opcode.dst, result); |         ProcessResult(opcode.result_operation, opcode.dst, result); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::Read: { |     case Macro::Operation::Read: { | ||||||
|         u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate); |         u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate); | ||||||
|         ProcessResult(opcode.result_operation, opcode.dst, result); |         ProcessResult(opcode.result_operation, opcode.dst, result); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Operation::Branch: { |     case Macro::Operation::Branch: { | ||||||
|         ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); |         ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); | ||||||
|         u32 value = GetRegister(opcode.src_a); |         u32 value = GetRegister(opcode.src_a); | ||||||
|         bool taken = EvaluateBranchCondition(opcode.branch_condition, value); |         bool taken = EvaluateBranchCondition(opcode.branch_condition, value); | ||||||
| @ -191,7 +128,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
| 
 | 
 | ||||||
|             delayed_pc = base_address + opcode.GetBranchTarget(); |             delayed_pc = base_address + opcode.GetBranchTarget(); | ||||||
|             // Execute one more instruction due to the delay slot.
 |             // Execute one more instruction due to the delay slot.
 | ||||||
|             return Step(offset, true); |             return Step(true); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| @ -204,51 +141,44 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||||||
|     // cause an exit if it's executed inside a delay slot.
 |     // cause an exit if it's executed inside a delay slot.
 | ||||||
|     if (opcode.is_exit && !is_delay_slot) { |     if (opcode.is_exit && !is_delay_slot) { | ||||||
|         // Exit has a delay slot, execute the next instruction
 |         // Exit has a delay slot, execute the next instruction
 | ||||||
|         Step(offset, true); |         Step(true); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { | u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b) { | ||||||
|     const auto& macro_memory{maxwell3d.GetMacroMemory()}; |  | ||||||
|     ASSERT((pc % sizeof(u32)) == 0); |  | ||||||
|     ASSERT((pc + offset) < macro_memory.size() * sizeof(u32)); |  | ||||||
|     return {macro_memory[offset + pc / sizeof(u32)]}; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) { |  | ||||||
|     switch (operation) { |     switch (operation) { | ||||||
|     case ALUOperation::Add: { |     case Macro::ALUOperation::Add: { | ||||||
|         const u64 result{static_cast<u64>(src_a) + src_b}; |         const u64 result{static_cast<u64>(src_a) + src_b}; | ||||||
|         carry_flag = result > 0xffffffff; |         carry_flag = result > 0xffffffff; | ||||||
|         return static_cast<u32>(result); |         return static_cast<u32>(result); | ||||||
|     } |     } | ||||||
|     case ALUOperation::AddWithCarry: { |     case Macro::ALUOperation::AddWithCarry: { | ||||||
|         const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; |         const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; | ||||||
|         carry_flag = result > 0xffffffff; |         carry_flag = result > 0xffffffff; | ||||||
|         return static_cast<u32>(result); |         return static_cast<u32>(result); | ||||||
|     } |     } | ||||||
|     case ALUOperation::Subtract: { |     case Macro::ALUOperation::Subtract: { | ||||||
|         const u64 result{static_cast<u64>(src_a) - src_b}; |         const u64 result{static_cast<u64>(src_a) - src_b}; | ||||||
|         carry_flag = result < 0x100000000; |         carry_flag = result < 0x100000000; | ||||||
|         return static_cast<u32>(result); |         return static_cast<u32>(result); | ||||||
|     } |     } | ||||||
|     case ALUOperation::SubtractWithBorrow: { |     case Macro::ALUOperation::SubtractWithBorrow: { | ||||||
|         const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; |         const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; | ||||||
|         carry_flag = result < 0x100000000; |         carry_flag = result < 0x100000000; | ||||||
|         return static_cast<u32>(result); |         return static_cast<u32>(result); | ||||||
|     } |     } | ||||||
|     case ALUOperation::Xor: |     case Macro::ALUOperation::Xor: | ||||||
|         return src_a ^ src_b; |         return src_a ^ src_b; | ||||||
|     case ALUOperation::Or: |     case Macro::ALUOperation::Or: | ||||||
|         return src_a | src_b; |         return src_a | src_b; | ||||||
|     case ALUOperation::And: |     case Macro::ALUOperation::And: | ||||||
|         return src_a & src_b; |         return src_a & src_b; | ||||||
|     case ALUOperation::AndNot: |     case Macro::ALUOperation::AndNot: | ||||||
|         return src_a & ~src_b; |         return src_a & ~src_b; | ||||||
|     case ALUOperation::Nand: |     case Macro::ALUOperation::Nand: | ||||||
|         return ~(src_a & src_b); |         return ~(src_a & src_b); | ||||||
| 
 | 
 | ||||||
|     default: |     default: | ||||||
| @ -257,43 +187,43 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 result) { | void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result) { | ||||||
|     switch (operation) { |     switch (operation) { | ||||||
|     case ResultOperation::IgnoreAndFetch: |     case Macro::ResultOperation::IgnoreAndFetch: | ||||||
|         // Fetch parameter and ignore result.
 |         // Fetch parameter and ignore result.
 | ||||||
|         SetRegister(reg, FetchParameter()); |         SetRegister(reg, FetchParameter()); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::Move: |     case Macro::ResultOperation::Move: | ||||||
|         // Move result.
 |         // Move result.
 | ||||||
|         SetRegister(reg, result); |         SetRegister(reg, result); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::MoveAndSetMethod: |     case Macro::ResultOperation::MoveAndSetMethod: | ||||||
|         // Move result and use as Method Address.
 |         // Move result and use as Method Address.
 | ||||||
|         SetRegister(reg, result); |         SetRegister(reg, result); | ||||||
|         SetMethodAddress(result); |         SetMethodAddress(result); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::FetchAndSend: |     case Macro::ResultOperation::FetchAndSend: | ||||||
|         // Fetch parameter and send result.
 |         // Fetch parameter and send result.
 | ||||||
|         SetRegister(reg, FetchParameter()); |         SetRegister(reg, FetchParameter()); | ||||||
|         Send(result); |         Send(result); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::MoveAndSend: |     case Macro::ResultOperation::MoveAndSend: | ||||||
|         // Move and send result.
 |         // Move and send result.
 | ||||||
|         SetRegister(reg, result); |         SetRegister(reg, result); | ||||||
|         Send(result); |         Send(result); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::FetchAndSetMethod: |     case Macro::ResultOperation::FetchAndSetMethod: | ||||||
|         // Fetch parameter and use result as Method Address.
 |         // Fetch parameter and use result as Method Address.
 | ||||||
|         SetRegister(reg, FetchParameter()); |         SetRegister(reg, FetchParameter()); | ||||||
|         SetMethodAddress(result); |         SetMethodAddress(result); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::MoveAndSetMethodFetchAndSend: |     case Macro::ResultOperation::MoveAndSetMethodFetchAndSend: | ||||||
|         // Move result and use as Method Address, then fetch and send parameter.
 |         // Move result and use as Method Address, then fetch and send parameter.
 | ||||||
|         SetRegister(reg, result); |         SetRegister(reg, result); | ||||||
|         SetMethodAddress(result); |         SetMethodAddress(result); | ||||||
|         Send(FetchParameter()); |         Send(FetchParameter()); | ||||||
|         break; |         break; | ||||||
|     case ResultOperation::MoveAndSetMethodSend: |     case Macro::ResultOperation::MoveAndSetMethodSend: | ||||||
|         // Move result and use as Method Address, then send bits 12:17 of result.
 |         // Move result and use as Method Address, then send bits 12:17 of result.
 | ||||||
|         SetRegister(reg, result); |         SetRegister(reg, result); | ||||||
|         SetMethodAddress(result); |         SetMethodAddress(result); | ||||||
| @ -304,16 +234,28 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u32 MacroInterpreter::FetchParameter() { | bool MacroInterpreterImpl::EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const { | ||||||
|     ASSERT(next_parameter_index < num_parameters); |     switch (cond) { | ||||||
|     return parameters[next_parameter_index++]; |     case Macro::BranchCondition::Zero: | ||||||
|  |         return value == 0; | ||||||
|  |     case Macro::BranchCondition::NotZero: | ||||||
|  |         return value != 0; | ||||||
|  |     } | ||||||
|  |     UNREACHABLE(); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u32 MacroInterpreter::GetRegister(u32 register_id) const { | Macro::Opcode MacroInterpreterImpl::GetOpcode() const { | ||||||
|  |     ASSERT((pc % sizeof(u32)) == 0); | ||||||
|  |     ASSERT(pc < code.size() * sizeof(u32)); | ||||||
|  |     return {code[pc / sizeof(u32)]}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 MacroInterpreterImpl::GetRegister(u32 register_id) const { | ||||||
|     return registers.at(register_id); |     return registers.at(register_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::SetRegister(u32 register_id, u32 value) { | void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) { | ||||||
|     // Register 0 is hardwired as the zero register.
 |     // Register 0 is hardwired as the zero register.
 | ||||||
|     // Ensure no writes to it actually occur.
 |     // Ensure no writes to it actually occur.
 | ||||||
|     if (register_id == 0) { |     if (register_id == 0) { | ||||||
| @ -323,30 +265,24 @@ void MacroInterpreter::SetRegister(u32 register_id, u32 value) { | |||||||
|     registers.at(register_id) = value; |     registers.at(register_id) = value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::SetMethodAddress(u32 address) { | void MacroInterpreterImpl::SetMethodAddress(u32 address) { | ||||||
|     method_address.raw = address; |     method_address.raw = address; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MacroInterpreter::Send(u32 value) { | void MacroInterpreterImpl::Send(u32 value) { | ||||||
|     maxwell3d.CallMethodFromMME(method_address.address, value); |     maxwell3d.CallMethodFromMME(method_address.address, value); | ||||||
|     // Increment the method address by the method increment.
 |     // Increment the method address by the method increment.
 | ||||||
|     method_address.address.Assign(method_address.address.Value() + |     method_address.address.Assign(method_address.address.Value() + | ||||||
|                                   method_address.increment.Value()); |                                   method_address.increment.Value()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u32 MacroInterpreter::Read(u32 method) const { | u32 MacroInterpreterImpl::Read(u32 method) const { | ||||||
|     return maxwell3d.GetRegisterValue(method); |     return maxwell3d.GetRegisterValue(method); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) const { | u32 MacroInterpreterImpl::FetchParameter() { | ||||||
|     switch (cond) { |     ASSERT(next_parameter_index < num_parameters); | ||||||
|     case BranchCondition::Zero: |     return parameters[next_parameter_index++]; | ||||||
|         return value == 0; |  | ||||||
|     case BranchCondition::NotZero: |  | ||||||
|         return value != 0; |  | ||||||
|     } |  | ||||||
|     UNREACHABLE(); |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Tegra
 | } // namespace Tegra
 | ||||||
| @ -1,44 +1,37 @@ | |||||||
| // Copyright 2018 yuzu Emulator Project
 | // Copyright 2020 yuzu Emulator Project
 | ||||||
| // 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.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| #include <array> | #include <array> | ||||||
| #include <optional> | #include <optional> | ||||||
| 
 | #include <vector> | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "video_core/macro/macro.h" | ||||||
| 
 | 
 | ||||||
| namespace Tegra { | namespace Tegra { | ||||||
| namespace Engines { | namespace Engines { | ||||||
| class Maxwell3D; | class Maxwell3D; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class MacroInterpreter final { | class MacroInterpreter final : public MacroEngine { | ||||||
| public: | public: | ||||||
|     explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d); |     explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d); | ||||||
| 
 | 
 | ||||||
|     /**
 | protected: | ||||||
|      * Executes the macro code with the specified input parameters. |     std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override; | ||||||
|      * @param offset Offset to start execution at. |  | ||||||
|      * @param parameters The parameters of the macro. |  | ||||||
|      */ |  | ||||||
|     void Execute(u32 offset, std::size_t num_parameters, const u32* parameters); |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     enum class ALUOperation : u32; |     Engines::Maxwell3D& maxwell3d; | ||||||
|     enum class BranchCondition : u32; | }; | ||||||
|     enum class ResultOperation : u32; |  | ||||||
| 
 | 
 | ||||||
|     union Opcode; | class MacroInterpreterImpl : public CachedMacro { | ||||||
| 
 | public: | ||||||
|     union MethodAddress { |     MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code); | ||||||
|         u32 raw; |     void Execute(std::vector<u32>& parameters, u32 method) override; | ||||||
|         BitField<0, 12, u32> address; |  | ||||||
|         BitField<12, 6, u32> increment; |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|     /// Resets the execution engine state, zeroing registers, etc.
 |     /// Resets the execution engine state, zeroing registers, etc.
 | ||||||
|     void Reset(); |     void Reset(); | ||||||
| 
 | 
 | ||||||
| @ -49,20 +42,20 @@ private: | |||||||
|      * @param is_delay_slot Whether the current step is being executed due to a delay slot in a |      * @param is_delay_slot Whether the current step is being executed due to a delay slot in a | ||||||
|      * previous instruction. |      * previous instruction. | ||||||
|      */ |      */ | ||||||
|     bool Step(u32 offset, bool is_delay_slot); |     bool Step(bool is_delay_slot); | ||||||
| 
 | 
 | ||||||
|     /// Calculates the result of an ALU operation. src_a OP src_b;
 |     /// Calculates the result of an ALU operation. src_a OP src_b;
 | ||||||
|     u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b); |     u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); | ||||||
| 
 | 
 | ||||||
|     /// Performs the result operation on the input result and stores it in the specified register
 |     /// Performs the result operation on the input result and stores it in the specified register
 | ||||||
|     /// (if necessary).
 |     /// (if necessary).
 | ||||||
|     void ProcessResult(ResultOperation operation, u32 reg, u32 result); |     void ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result); | ||||||
| 
 | 
 | ||||||
|     /// Evaluates the branch condition and returns whether the branch should be taken or not.
 |     /// Evaluates the branch condition and returns whether the branch should be taken or not.
 | ||||||
|     bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; |     bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; | ||||||
| 
 | 
 | ||||||
|     /// Reads an opcode at the current program counter location.
 |     /// Reads an opcode at the current program counter location.
 | ||||||
|     Opcode GetOpcode(u32 offset) const; |     Macro::Opcode GetOpcode() const; | ||||||
| 
 | 
 | ||||||
|     /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
 |     /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
 | ||||||
|     u32 GetRegister(u32 register_id) const; |     u32 GetRegister(u32 register_id) const; | ||||||
| @ -89,13 +82,11 @@ private: | |||||||
|     /// Program counter to execute at after the delay slot is executed.
 |     /// Program counter to execute at after the delay slot is executed.
 | ||||||
|     std::optional<u32> delayed_pc; |     std::optional<u32> delayed_pc; | ||||||
| 
 | 
 | ||||||
|     static constexpr std::size_t NumMacroRegisters = 8; |  | ||||||
| 
 |  | ||||||
|     /// General purpose macro registers.
 |     /// General purpose macro registers.
 | ||||||
|     std::array<u32, NumMacroRegisters> registers = {}; |     std::array<u32, Macro::NUM_MACRO_REGISTERS> registers = {}; | ||||||
| 
 | 
 | ||||||
|     /// Method address to use for the next Send instruction.
 |     /// Method address to use for the next Send instruction.
 | ||||||
|     MethodAddress method_address = {}; |     Macro::MethodAddress method_address = {}; | ||||||
| 
 | 
 | ||||||
|     /// Input parameters of the current macro.
 |     /// Input parameters of the current macro.
 | ||||||
|     std::unique_ptr<u32[]> parameters; |     std::unique_ptr<u32[]> parameters; | ||||||
| @ -105,5 +96,7 @@ private: | |||||||
|     u32 next_parameter_index = 0; |     u32 next_parameter_index = 0; | ||||||
| 
 | 
 | ||||||
|     bool carry_flag = false; |     bool carry_flag = false; | ||||||
|  |     const std::vector<u32>& code; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
| } // namespace Tegra
 | } // namespace Tegra
 | ||||||
							
								
								
									
										633
									
								
								src/video_core/macro/macro_jit_x64.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								src/video_core/macro/macro_jit_x64.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,633 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/microprofile.h" | ||||||
|  | #include "common/x64/xbyak_util.h" | ||||||
|  | #include "video_core/engines/maxwell_3d.h" | ||||||
|  | #include "video_core/macro/macro_interpreter.h" | ||||||
|  | #include "video_core/macro/macro_jit_x64.h" | ||||||
|  | 
 | ||||||
|  | MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255, 47)); | ||||||
|  | MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0)); | ||||||
|  | 
 | ||||||
|  | namespace Tegra { | ||||||
|  | using JitFunction = void (MacroJITx64Impl::*)(Macro::Opcode opcode); | ||||||
|  | const std::array<JitFunction, 8> InstructionTable{ | ||||||
|  |     &MacroJITx64Impl::Compile_ALU, | ||||||
|  |     &MacroJITx64Impl::Compile_AddImmediate, | ||||||
|  |     &MacroJITx64Impl::Compile_ExtractInsert, | ||||||
|  |     &MacroJITx64Impl::Compile_ExtractShiftLeftImmediate, | ||||||
|  |     &MacroJITx64Impl::Compile_ExtractShiftLeftRegister, | ||||||
|  |     &MacroJITx64Impl::Compile_Read, | ||||||
|  |     nullptr, | ||||||
|  |     &MacroJITx64Impl::Compile_Branch, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const Xbyak::Reg64 PARAMETERS = Xbyak::util::r9; | ||||||
|  | static const Xbyak::Reg64 REGISTERS = Xbyak::util::r10; | ||||||
|  | static const Xbyak::Reg64 STATE = Xbyak::util::r11; | ||||||
|  | static const Xbyak::Reg64 NEXT_PARAMETER = Xbyak::util::r12; | ||||||
|  | static const Xbyak::Reg32 RESULT = Xbyak::util::r13d; | ||||||
|  | static const Xbyak::Reg64 RESULT_64 = Xbyak::util::r13; | ||||||
|  | static const Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d; | ||||||
|  | static const Xbyak::Reg64 METHOD_ADDRESS_64 = Xbyak::util::r14; | ||||||
|  | static const Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15; | ||||||
|  | 
 | ||||||
|  | static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ | ||||||
|  |     PARAMETERS, | ||||||
|  |     REGISTERS, | ||||||
|  |     STATE, | ||||||
|  |     NEXT_PARAMETER, | ||||||
|  |     RESULT, | ||||||
|  |     METHOD_ADDRESS, | ||||||
|  |     BRANCH_HOLDER, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { | ||||||
|  |     return std::make_unique<MacroJITx64Impl>(maxwell3d, code); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MacroJITx64Impl::MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code) | ||||||
|  |     : Xbyak::CodeGenerator(MAX_CODE_SIZE), code(code), maxwell3d(maxwell3d) { | ||||||
|  |     Compile(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MacroJITx64Impl::~MacroJITx64Impl() = default; | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Execute(std::vector<u32>& parameters, u32 method) { | ||||||
|  |     MICROPROFILE_SCOPE(MacroJitExecute); | ||||||
|  |     ASSERT_OR_EXECUTE(program != nullptr, { return; }); | ||||||
|  |     JITState state{}; | ||||||
|  |     state.maxwell3d = &maxwell3d; | ||||||
|  |     state.registers = {}; | ||||||
|  |     state.parameters = parameters.data(); | ||||||
|  |     program(&state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { | ||||||
|  |     const bool is_a_zero = opcode.src_a == 0; | ||||||
|  |     const bool is_b_zero = opcode.src_b == 0; | ||||||
|  |     const bool valid_operation = !is_a_zero && !is_b_zero; | ||||||
|  |     const bool is_move_operation = !is_a_zero && is_b_zero; | ||||||
|  |     const bool has_zero_register = is_a_zero || is_b_zero; | ||||||
|  | 
 | ||||||
|  |     Xbyak::Reg64 src_a; | ||||||
|  |     Xbyak::Reg32 src_b; | ||||||
|  | 
 | ||||||
|  |     if (!optimizer.zero_reg_skip) { | ||||||
|  |         src_a = Compile_GetRegister(opcode.src_a, RESULT_64); | ||||||
|  |         src_b = Compile_GetRegister(opcode.src_b, ebx); | ||||||
|  |     } else { | ||||||
|  |         if (!is_a_zero) { | ||||||
|  |             src_a = Compile_GetRegister(opcode.src_a, RESULT_64); | ||||||
|  |         } | ||||||
|  |         if (!is_b_zero) { | ||||||
|  |             src_b = Compile_GetRegister(opcode.src_b, ebx); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     Xbyak::Label skip_carry{}; | ||||||
|  | 
 | ||||||
|  |     bool has_emitted = false; | ||||||
|  | 
 | ||||||
|  |     switch (opcode.alu_operation) { | ||||||
|  |     case Macro::ALUOperation::Add: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (valid_operation) { | ||||||
|  |                 add(src_a, src_b); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             add(src_a, src_b); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!optimizer.can_skip_carry) { | ||||||
|  |             setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::AddWithCarry: | ||||||
|  |         bt(dword[STATE + offsetof(JITState, carry_flag)], 0); | ||||||
|  |         adc(src_a, src_b); | ||||||
|  |         setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::Subtract: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (valid_operation) { | ||||||
|  |                 sub(src_a, src_b); | ||||||
|  |                 has_emitted = true; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             sub(src_a, src_b); | ||||||
|  |             has_emitted = true; | ||||||
|  |         } | ||||||
|  |         if (!optimizer.can_skip_carry && has_emitted) { | ||||||
|  |             setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::SubtractWithBorrow: | ||||||
|  |         bt(dword[STATE + offsetof(JITState, carry_flag)], 0); | ||||||
|  |         sbb(src_a, src_b); | ||||||
|  |         setc(byte[STATE + offsetof(JITState, carry_flag)]); | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::Xor: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (valid_operation) { | ||||||
|  |                 xor_(src_a, src_b); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             xor_(src_a, src_b); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::Or: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (valid_operation) { | ||||||
|  |                 or_(src_a, src_b); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             or_(src_a, src_b); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::And: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (!has_zero_register) { | ||||||
|  |                 and_(src_a, src_b); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             and_(src_a, src_b); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::AndNot: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (!is_a_zero) { | ||||||
|  |                 not_(src_b); | ||||||
|  |                 and_(src_a, src_b); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             not_(src_b); | ||||||
|  |             and_(src_a, src_b); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Macro::ALUOperation::Nand: | ||||||
|  |         if (optimizer.zero_reg_skip) { | ||||||
|  |             if (!is_a_zero) { | ||||||
|  |                 and_(src_a, src_b); | ||||||
|  |                 not_(src_a); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             and_(src_a, src_b); | ||||||
|  |             not_(src_a); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", | ||||||
|  |                           static_cast<std::size_t>(opcode.alu_operation.Value())); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) { | ||||||
|  |     if (optimizer.skip_dummy_addimmediate) { | ||||||
|  |         // Games tend to use this as an exit instruction placeholder. It's to encode an instruction
 | ||||||
|  |         // without doing anything. In our case we can just not emit anything.
 | ||||||
|  |         if (opcode.result_operation == Macro::ResultOperation::Move && opcode.dst == 0) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Check for redundant moves
 | ||||||
|  |     if (optimizer.optimize_for_method_move && | ||||||
|  |         opcode.result_operation == Macro::ResultOperation::MoveAndSetMethod) { | ||||||
|  |         if (next_opcode.has_value()) { | ||||||
|  |             const auto next = *next_opcode; | ||||||
|  |             if (next.result_operation == Macro::ResultOperation::MoveAndSetMethod) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (optimizer.zero_reg_skip && opcode.src_a == 0) { | ||||||
|  |         if (opcode.immediate == 0) { | ||||||
|  |             xor_(RESULT, RESULT); | ||||||
|  |         } else { | ||||||
|  |             mov(RESULT, opcode.immediate); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         auto result = Compile_GetRegister(opcode.src_a, RESULT); | ||||||
|  |         if (opcode.immediate > 2) { | ||||||
|  |             add(result, opcode.immediate); | ||||||
|  |         } else if (opcode.immediate == 1) { | ||||||
|  |             inc(result); | ||||||
|  |         } else if (opcode.immediate < 0) { | ||||||
|  |             sub(result, opcode.immediate * -1); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) { | ||||||
|  |     auto dst = Compile_GetRegister(opcode.src_a, RESULT); | ||||||
|  |     auto src = Compile_GetRegister(opcode.src_b, eax); | ||||||
|  | 
 | ||||||
|  |     if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) { | ||||||
|  |         shr(src, opcode.bf_src_bit); | ||||||
|  |     } else if (opcode.bf_src_bit == 31) { | ||||||
|  |         xor_(src, src); | ||||||
|  |     } | ||||||
|  |     // Don't bother masking the whole register since we're using a 32 bit register
 | ||||||
|  |     if (opcode.bf_size != 31 && opcode.bf_size != 0) { | ||||||
|  |         and_(src, opcode.GetBitfieldMask()); | ||||||
|  |     } else if (opcode.bf_size == 0) { | ||||||
|  |         xor_(src, src); | ||||||
|  |     } | ||||||
|  |     if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) { | ||||||
|  |         shl(src, opcode.bf_dst_bit); | ||||||
|  |     } else if (opcode.bf_dst_bit == 31) { | ||||||
|  |         xor_(src, src); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit); | ||||||
|  |     if (mask != 0xffffffff) { | ||||||
|  |         and_(dst, mask); | ||||||
|  |     } | ||||||
|  |     or_(dst, src); | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) { | ||||||
|  |     auto dst = Compile_GetRegister(opcode.src_a, eax); | ||||||
|  |     auto src = Compile_GetRegister(opcode.src_b, RESULT); | ||||||
|  | 
 | ||||||
|  |     shr(src, al); | ||||||
|  |     if (opcode.bf_size != 0 && opcode.bf_size != 31) { | ||||||
|  |         and_(src, opcode.GetBitfieldMask()); | ||||||
|  |     } else if (opcode.bf_size == 0) { | ||||||
|  |         xor_(src, src); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) { | ||||||
|  |         shl(src, opcode.bf_dst_bit); | ||||||
|  |     } else if (opcode.bf_dst_bit == 31) { | ||||||
|  |         xor_(src, src); | ||||||
|  |     } | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) { | ||||||
|  |     auto dst = Compile_GetRegister(opcode.src_a, eax); | ||||||
|  |     auto src = Compile_GetRegister(opcode.src_b, RESULT); | ||||||
|  | 
 | ||||||
|  |     if (opcode.bf_src_bit != 0) { | ||||||
|  |         shr(src, opcode.bf_src_bit); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (opcode.bf_size != 31) { | ||||||
|  |         and_(src, opcode.GetBitfieldMask()); | ||||||
|  |     } | ||||||
|  |     shl(src, al); | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u32 Read(Engines::Maxwell3D* maxwell3d, u32 method) { | ||||||
|  |     return maxwell3d->GetRegisterValue(method); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { | ||||||
|  |     maxwell3d->CallMethodFromMME(method_address.address, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { | ||||||
|  |     if (optimizer.zero_reg_skip && opcode.src_a == 0) { | ||||||
|  |         if (opcode.immediate == 0) { | ||||||
|  |             xor_(RESULT, RESULT); | ||||||
|  |         } else { | ||||||
|  |             mov(RESULT, opcode.immediate); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         auto result = Compile_GetRegister(opcode.src_a, RESULT); | ||||||
|  |         if (opcode.immediate > 2) { | ||||||
|  |             add(result, opcode.immediate); | ||||||
|  |         } else if (opcode.immediate == 1) { | ||||||
|  |             inc(result); | ||||||
|  |         } else if (opcode.immediate < 0) { | ||||||
|  |             sub(result, opcode.immediate * -1); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0); | ||||||
|  |     mov(Common::X64::ABI_PARAM1, qword[STATE]); | ||||||
|  |     mov(Common::X64::ABI_PARAM2, RESULT); | ||||||
|  |     Common::X64::CallFarFunction(*this, &Read); | ||||||
|  |     Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0); | ||||||
|  |     mov(RESULT, Common::X64::ABI_RETURN.cvt32()); | ||||||
|  |     Compile_ProcessResult(opcode.result_operation, opcode.dst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { | ||||||
|  |     Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0); | ||||||
|  |     mov(Common::X64::ABI_PARAM1, qword[STATE]); | ||||||
|  |     mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS); | ||||||
|  |     mov(Common::X64::ABI_PARAM3, value); | ||||||
|  |     Common::X64::CallFarFunction(*this, &Send); | ||||||
|  |     Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, PersistentCallerSavedRegs(), 0); | ||||||
|  | 
 | ||||||
|  |     Xbyak::Label dont_process{}; | ||||||
|  |     // Get increment
 | ||||||
|  |     test(METHOD_ADDRESS, 0x3f000); | ||||||
|  |     // If zero, method address doesn't update
 | ||||||
|  |     je(dont_process); | ||||||
|  | 
 | ||||||
|  |     mov(ecx, METHOD_ADDRESS); | ||||||
|  |     and_(METHOD_ADDRESS, 0xfff); | ||||||
|  |     shr(ecx, 12); | ||||||
|  |     and_(ecx, 0x3f); | ||||||
|  |     lea(eax, ptr[rcx + METHOD_ADDRESS_64]); | ||||||
|  |     sal(ecx, 12); | ||||||
|  |     or_(eax, ecx); | ||||||
|  | 
 | ||||||
|  |     mov(METHOD_ADDRESS, eax); | ||||||
|  | 
 | ||||||
|  |     L(dont_process); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { | ||||||
|  |     ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); | ||||||
|  |     const s32 jump_address = | ||||||
|  |         static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32)); | ||||||
|  | 
 | ||||||
|  |     Xbyak::Label end; | ||||||
|  |     auto value = Compile_GetRegister(opcode.src_a, eax); | ||||||
|  |     test(value, value); | ||||||
|  |     if (optimizer.has_delayed_pc) { | ||||||
|  |         switch (opcode.branch_condition) { | ||||||
|  |         case Macro::BranchCondition::Zero: | ||||||
|  |             jne(end, T_NEAR); | ||||||
|  |             break; | ||||||
|  |         case Macro::BranchCondition::NotZero: | ||||||
|  |             je(end, T_NEAR); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (opcode.branch_annul) { | ||||||
|  |             xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |             jmp(labels[jump_address], T_NEAR); | ||||||
|  |         } else { | ||||||
|  |             Xbyak::Label handle_post_exit{}; | ||||||
|  |             Xbyak::Label skip{}; | ||||||
|  |             jmp(skip, T_NEAR); | ||||||
|  |             if (opcode.is_exit) { | ||||||
|  |                 L(handle_post_exit); | ||||||
|  |                 // Execute 1 instruction
 | ||||||
|  |                 mov(BRANCH_HOLDER, end_of_code); | ||||||
|  |                 // Jump to next instruction to skip delay slot check
 | ||||||
|  |                 jmp(labels[jump_address], T_NEAR); | ||||||
|  |             } else { | ||||||
|  |                 L(handle_post_exit); | ||||||
|  |                 xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |                 jmp(labels[jump_address], T_NEAR); | ||||||
|  |             } | ||||||
|  |             L(skip); | ||||||
|  |             mov(BRANCH_HOLDER, handle_post_exit); | ||||||
|  |             jmp(delay_skip[pc], T_NEAR); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         switch (opcode.branch_condition) { | ||||||
|  |         case Macro::BranchCondition::Zero: | ||||||
|  |             je(labels[jump_address], T_NEAR); | ||||||
|  |             break; | ||||||
|  |         case Macro::BranchCondition::NotZero: | ||||||
|  |             jne(labels[jump_address], T_NEAR); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     L(end); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() { | ||||||
|  |     optimizer.can_skip_carry = true; | ||||||
|  |     optimizer.has_delayed_pc = false; | ||||||
|  |     for (auto raw_op : code) { | ||||||
|  |         Macro::Opcode op{}; | ||||||
|  |         op.raw = raw_op; | ||||||
|  | 
 | ||||||
|  |         if (op.operation == Macro::Operation::ALU) { | ||||||
|  |             // Scan for any ALU operations which actually use the carry flag, if they don't exist in
 | ||||||
|  |             // our current code we can skip emitting the carry flag handling operations
 | ||||||
|  |             if (op.alu_operation == Macro::ALUOperation::AddWithCarry || | ||||||
|  |                 op.alu_operation == Macro::ALUOperation::SubtractWithBorrow) { | ||||||
|  |                 optimizer.can_skip_carry = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (op.operation == Macro::Operation::Branch) { | ||||||
|  |             if (!op.branch_annul) { | ||||||
|  |                 optimizer.has_delayed_pc = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile() { | ||||||
|  |     MICROPROFILE_SCOPE(MacroJitCompile); | ||||||
|  |     bool keep_executing = true; | ||||||
|  |     labels.fill(Xbyak::Label()); | ||||||
|  | 
 | ||||||
|  |     Common::X64::ABI_PushRegistersAndAdjustStackGPS(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); | ||||||
|  |     // JIT state
 | ||||||
|  |     mov(STATE, Common::X64::ABI_PARAM1); | ||||||
|  |     mov(PARAMETERS, qword[Common::X64::ABI_PARAM1 + | ||||||
|  |                           static_cast<Xbyak::uint32>(offsetof(JITState, parameters))]); | ||||||
|  |     mov(REGISTERS, Common::X64::ABI_PARAM1); | ||||||
|  |     add(REGISTERS, static_cast<Xbyak::uint32>(offsetof(JITState, registers))); | ||||||
|  |     xor_(RESULT, RESULT); | ||||||
|  |     xor_(METHOD_ADDRESS, METHOD_ADDRESS); | ||||||
|  |     xor_(NEXT_PARAMETER, NEXT_PARAMETER); | ||||||
|  |     xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  | 
 | ||||||
|  |     mov(dword[REGISTERS + 4], Compile_FetchParameter()); | ||||||
|  | 
 | ||||||
|  |     // Track get register for zero registers and mark it as no-op
 | ||||||
|  |     optimizer.zero_reg_skip = true; | ||||||
|  | 
 | ||||||
|  |     // AddImmediate tends to be used as a NOP instruction, if we detect this we can
 | ||||||
|  |     // completely skip the entire code path and no emit anything
 | ||||||
|  |     optimizer.skip_dummy_addimmediate = true; | ||||||
|  | 
 | ||||||
|  |     // SMO tends to emit a lot of unnecessary method moves, we can mitigate this by only emitting
 | ||||||
|  |     // one if our register isn't "dirty"
 | ||||||
|  |     optimizer.optimize_for_method_move = true; | ||||||
|  | 
 | ||||||
|  |     // Check to see if we can skip emitting certain instructions
 | ||||||
|  |     Optimizer_ScanFlags(); | ||||||
|  | 
 | ||||||
|  |     const u32 op_count = static_cast<u32>(code.size()); | ||||||
|  |     for (u32 i = 0; i < op_count; i++) { | ||||||
|  |         if (i < op_count - 1) { | ||||||
|  |             pc = i + 1; | ||||||
|  |             next_opcode = GetOpCode(); | ||||||
|  |         } else { | ||||||
|  |             next_opcode = {}; | ||||||
|  |         } | ||||||
|  |         pc = i; | ||||||
|  |         Compile_NextInstruction(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     L(end_of_code); | ||||||
|  | 
 | ||||||
|  |     Common::X64::ABI_PopRegistersAndAdjustStackGPS(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); | ||||||
|  |     ret(); | ||||||
|  |     ready(); | ||||||
|  |     program = getCode<ProgramType>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MacroJITx64Impl::Compile_NextInstruction() { | ||||||
|  |     const auto opcode = GetOpCode(); | ||||||
|  |     if (labels[pc].getAddress()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     L(labels[pc]); | ||||||
|  | 
 | ||||||
|  |     const std::size_t op = static_cast<std::size_t>(opcode.operation.Value()); | ||||||
|  | 
 | ||||||
|  |     if (InstructionTable[op] == nullptr) { | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented opcode {}", op); | ||||||
|  |     } else { | ||||||
|  |         ((*this).*InstructionTable[op])(opcode); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (optimizer.has_delayed_pc) { | ||||||
|  |         if (opcode.is_exit) { | ||||||
|  |             mov(rax, end_of_code); | ||||||
|  |             test(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |             cmove(BRANCH_HOLDER, rax); | ||||||
|  |             // Jump to next instruction to skip delay slot check
 | ||||||
|  |             je(labels[pc + 1], T_NEAR); | ||||||
|  |         } else { | ||||||
|  |             // TODO(ogniK): Optimize delay slot branching
 | ||||||
|  |             Xbyak::Label no_delay_slot{}; | ||||||
|  |             test(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |             je(no_delay_slot, T_NEAR); | ||||||
|  |             mov(rax, BRANCH_HOLDER); | ||||||
|  |             xor_(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |             jmp(rax); | ||||||
|  |             L(no_delay_slot); | ||||||
|  |         } | ||||||
|  |         L(delay_skip[pc]); | ||||||
|  |         if (opcode.is_exit) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         test(BRANCH_HOLDER, BRANCH_HOLDER); | ||||||
|  |         jne(end_of_code, T_NEAR); | ||||||
|  |         if (opcode.is_exit) { | ||||||
|  |             inc(BRANCH_HOLDER); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Xbyak::Reg32 Tegra::MacroJITx64Impl::Compile_FetchParameter() { | ||||||
|  |     mov(eax, dword[PARAMETERS + NEXT_PARAMETER * sizeof(u32)]); | ||||||
|  |     inc(NEXT_PARAMETER); | ||||||
|  |     return eax; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) { | ||||||
|  |     if (index == 0) { | ||||||
|  |         // Register 0 is always zero
 | ||||||
|  |         xor_(dst, dst); | ||||||
|  |     } else { | ||||||
|  |         mov(dst, dword[REGISTERS + index * sizeof(u32)]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return dst; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Xbyak::Reg64 Tegra::MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg64 dst) { | ||||||
|  |     if (index == 0) { | ||||||
|  |         // Register 0 is always zero
 | ||||||
|  |         xor_(dst, dst); | ||||||
|  |     } else { | ||||||
|  |         mov(dst, dword[REGISTERS + index * sizeof(u32)]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return dst; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Tegra::MacroJITx64Impl::Compile_WriteCarry(Xbyak::Reg64 dst) { | ||||||
|  |     Xbyak::Label zero{}, end{}; | ||||||
|  |     xor_(ecx, ecx); | ||||||
|  |     shr(dst, 32); | ||||||
|  |     setne(cl); | ||||||
|  |     mov(dword[STATE + offsetof(JITState, carry_flag)], ecx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) { | ||||||
|  |     auto SetRegister = [=](u32 reg, Xbyak::Reg32 result) { | ||||||
|  |         // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
 | ||||||
|  |         // register.
 | ||||||
|  |         if (reg == 0) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         mov(dword[REGISTERS + reg * sizeof(u32)], result); | ||||||
|  |     }; | ||||||
|  |     auto SetMethodAddress = [=](Xbyak::Reg32 reg) { mov(METHOD_ADDRESS, reg); }; | ||||||
|  | 
 | ||||||
|  |     switch (operation) { | ||||||
|  |     case Macro::ResultOperation::IgnoreAndFetch: | ||||||
|  |         SetRegister(reg, Compile_FetchParameter()); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::Move: | ||||||
|  |         SetRegister(reg, RESULT); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::MoveAndSetMethod: | ||||||
|  |         SetRegister(reg, RESULT); | ||||||
|  |         SetMethodAddress(RESULT); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::FetchAndSend: | ||||||
|  |         // Fetch parameter and send result.
 | ||||||
|  |         SetRegister(reg, Compile_FetchParameter()); | ||||||
|  |         Compile_Send(RESULT); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::MoveAndSend: | ||||||
|  |         // Move and send result.
 | ||||||
|  |         SetRegister(reg, RESULT); | ||||||
|  |         Compile_Send(RESULT); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::FetchAndSetMethod: | ||||||
|  |         // Fetch parameter and use result as Method Address.
 | ||||||
|  |         SetRegister(reg, Compile_FetchParameter()); | ||||||
|  |         SetMethodAddress(RESULT); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::MoveAndSetMethodFetchAndSend: | ||||||
|  |         // Move result and use as Method Address, then fetch and send parameter.
 | ||||||
|  |         SetRegister(reg, RESULT); | ||||||
|  |         SetMethodAddress(RESULT); | ||||||
|  |         Compile_Send(Compile_FetchParameter()); | ||||||
|  |         break; | ||||||
|  |     case Macro::ResultOperation::MoveAndSetMethodSend: | ||||||
|  |         // Move result and use as Method Address, then send bits 12:17 of result.
 | ||||||
|  |         SetRegister(reg, RESULT); | ||||||
|  |         SetMethodAddress(RESULT); | ||||||
|  |         shr(RESULT, 12); | ||||||
|  |         and_(RESULT, 0b111111); | ||||||
|  |         Compile_Send(RESULT); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented macro operation {}", static_cast<std::size_t>(operation)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Macro::Opcode MacroJITx64Impl::GetOpCode() const { | ||||||
|  |     ASSERT(pc < code.size()); | ||||||
|  |     return {code[pc]}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::bitset<32> MacroJITx64Impl::PersistentCallerSavedRegs() const { | ||||||
|  |     return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Tegra
 | ||||||
							
								
								
									
										98
									
								
								src/video_core/macro/macro_jit_x64.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/video_core/macro/macro_jit_x64.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <bitset> | ||||||
|  | #include <xbyak.h> | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/x64/xbyak_abi.h" | ||||||
|  | #include "video_core/macro/macro.h" | ||||||
|  | 
 | ||||||
|  | namespace Tegra { | ||||||
|  | namespace Engines { | ||||||
|  | class Maxwell3D; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// MAX_CODE_SIZE is arbitrarily chosen based on current booting games
 | ||||||
|  | constexpr size_t MAX_CODE_SIZE = 0x10000; | ||||||
|  | 
 | ||||||
|  | class MacroJITx64 final : public MacroEngine { | ||||||
|  | public: | ||||||
|  |     explicit MacroJITx64(Engines::Maxwell3D& maxwell3d); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Engines::Maxwell3D& maxwell3d; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class MacroJITx64Impl : public Xbyak::CodeGenerator, public CachedMacro { | ||||||
|  | public: | ||||||
|  |     MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code); | ||||||
|  |     ~MacroJITx64Impl(); | ||||||
|  |     void Execute(std::vector<u32>& parameters, u32 method) override; | ||||||
|  | 
 | ||||||
|  |     void Compile_ALU(Macro::Opcode opcode); | ||||||
|  |     void Compile_AddImmediate(Macro::Opcode opcode); | ||||||
|  |     void Compile_ExtractInsert(Macro::Opcode opcode); | ||||||
|  |     void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); | ||||||
|  |     void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); | ||||||
|  |     void Compile_Read(Macro::Opcode opcode); | ||||||
|  |     void Compile_Branch(Macro::Opcode opcode); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void Optimizer_ScanFlags(); | ||||||
|  | 
 | ||||||
|  |     void Compile(); | ||||||
|  |     bool Compile_NextInstruction(); | ||||||
|  | 
 | ||||||
|  |     Xbyak::Reg32 Compile_FetchParameter(); | ||||||
|  |     Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); | ||||||
|  |     Xbyak::Reg64 Compile_GetRegister(u32 index, Xbyak::Reg64 dst); | ||||||
|  |     void Compile_WriteCarry(Xbyak::Reg64 dst); | ||||||
|  | 
 | ||||||
|  |     void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); | ||||||
|  |     void Compile_Send(Xbyak::Reg32 value); | ||||||
|  | 
 | ||||||
|  |     Macro::Opcode GetOpCode() const; | ||||||
|  |     std::bitset<32> PersistentCallerSavedRegs() const; | ||||||
|  | 
 | ||||||
|  |     struct JITState { | ||||||
|  |         Engines::Maxwell3D* maxwell3d{}; | ||||||
|  |         std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{}; | ||||||
|  |         u32* parameters{}; | ||||||
|  |         u32 carry_flag{}; | ||||||
|  |     }; | ||||||
|  |     static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0"); | ||||||
|  |     using ProgramType = void (*)(JITState*); | ||||||
|  | 
 | ||||||
|  |     struct OptimizerState { | ||||||
|  |         bool can_skip_carry{}; | ||||||
|  |         bool has_delayed_pc{}; | ||||||
|  |         bool zero_reg_skip{}; | ||||||
|  |         bool skip_dummy_addimmediate{}; | ||||||
|  |         bool optimize_for_method_move{}; | ||||||
|  |     }; | ||||||
|  |     OptimizerState optimizer{}; | ||||||
|  | 
 | ||||||
|  |     std::optional<Macro::Opcode> next_opcode{}; | ||||||
|  |     ProgramType program{nullptr}; | ||||||
|  | 
 | ||||||
|  |     std::array<Xbyak::Label, MAX_CODE_SIZE> labels; | ||||||
|  |     std::array<Xbyak::Label, MAX_CODE_SIZE> delay_skip{}; | ||||||
|  |     Xbyak::Label end_of_code{}; | ||||||
|  | 
 | ||||||
|  |     bool is_delay_slot{}; | ||||||
|  |     u32 pc{}; | ||||||
|  |     std::optional<u32> delayed_pc; | ||||||
|  | 
 | ||||||
|  |     const std::vector<u32>& code; | ||||||
|  |     Engines::Maxwell3D& maxwell3d; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Tegra
 | ||||||
| @ -533,6 +533,8 @@ void Config::ReadDebuggingValues() { | |||||||
|     Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); |     Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); | ||||||
|     Settings::values.disable_cpu_opt = |     Settings::values.disable_cpu_opt = | ||||||
|         ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); |         ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); | ||||||
|  |     Settings::values.disable_macro_jit = | ||||||
|  |         ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| } | } | ||||||
| @ -1011,6 +1013,7 @@ void Config::SaveDebuggingValues() { | |||||||
|     WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); |     WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); | ||||||
|     WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); |     WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); | ||||||
|     WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); |     WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); | ||||||
|  |     WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); | ||||||
| 
 | 
 | ||||||
|     qt_config->endGroup(); |     qt_config->endGroup(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -39,6 +39,8 @@ void ConfigureDebug::SetConfiguration() { | |||||||
|     ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); |     ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); | ||||||
|     ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |     ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||||||
|     ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); |     ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); | ||||||
|  |     ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||||||
|  |     ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureDebug::ApplyConfiguration() { | void ConfigureDebug::ApplyConfiguration() { | ||||||
| @ -51,6 +53,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||||||
|     Settings::values.quest_flag = ui->quest_flag->isChecked(); |     Settings::values.quest_flag = ui->quest_flag->isChecked(); | ||||||
|     Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); |     Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); | ||||||
|     Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); |     Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | ||||||
|  |     Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); | ||||||
|     Debugger::ToggleConsole(); |     Debugger::ToggleConsole(); | ||||||
|     Log::Filter filter; |     Log::Filter filter; | ||||||
|     filter.ParseFilterString(Settings::values.log_filter); |     filter.ParseFilterString(Settings::values.log_filter); | ||||||
|  | |||||||
| @ -148,6 +148,19 @@ | |||||||
|         </property> |         </property> | ||||||
|        </widget> |        </widget> | ||||||
|       </item> |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QCheckBox" name="disable_macro_jit"> | ||||||
|  |         <property name="enabled"> | ||||||
|  |          <bool>true</bool> | ||||||
|  |         </property> | ||||||
|  |         <property name="whatsThis"> | ||||||
|  |          <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Disable Macro JIT</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|      </layout> |      </layout> | ||||||
|     </widget> |     </widget> | ||||||
|    </item> |    </item> | ||||||
|  | |||||||
| @ -432,6 +432,8 @@ void Config::ReadValues() { | |||||||
|     Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); |     Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); | ||||||
|     Settings::values.disable_cpu_opt = |     Settings::values.disable_cpu_opt = | ||||||
|         sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false); |         sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false); | ||||||
|  |     Settings::values.disable_macro_jit = | ||||||
|  |         sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false); | ||||||
| 
 | 
 | ||||||
|     const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); |     const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||||||
|     std::stringstream ss(title_list); |     std::stringstream ss(title_list); | ||||||
|  | |||||||
| @ -291,6 +291,8 @@ quest_flag = | |||||||
| # Determines whether or not JIT CPU optimizations are enabled | # Determines whether or not JIT CPU optimizations are enabled | ||||||
| # false: Optimizations Enabled, true: Optimizations Disabled | # false: Optimizations Enabled, true: Optimizations Disabled | ||||||
| disable_cpu_opt = | disable_cpu_opt = | ||||||
|  | # Enables/Disables the macro JIT compiler | ||||||
|  | disable_macro_jit=false | ||||||
| 
 | 
 | ||||||
| [WebService] | [WebService] | ||||||
| # Whether or not to enable telemetry | # Whether or not to enable telemetry | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 David Marcec
						David Marcec