shader: Initial recompiler work
This commit is contained in:
		| @@ -142,6 +142,7 @@ add_subdirectory(core) | |||||||
| add_subdirectory(audio_core) | add_subdirectory(audio_core) | ||||||
| add_subdirectory(video_core) | add_subdirectory(video_core) | ||||||
| add_subdirectory(input_common) | add_subdirectory(input_common) | ||||||
|  | add_subdirectory(shader_recompiler) | ||||||
| add_subdirectory(tests) | add_subdirectory(tests) | ||||||
|  |  | ||||||
| if (ENABLE_SDL2) | if (ENABLE_SDL2) | ||||||
|   | |||||||
							
								
								
									
										86
									
								
								src/shader_recompiler/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/shader_recompiler/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | add_executable(shader_recompiler | ||||||
|  |     environment.h | ||||||
|  |     exception.h | ||||||
|  |     file_environment.cpp | ||||||
|  |     file_environment.h | ||||||
|  |     frontend/ir/attribute.cpp | ||||||
|  |     frontend/ir/attribute.h | ||||||
|  |     frontend/ir/basic_block.cpp | ||||||
|  |     frontend/ir/basic_block.h | ||||||
|  |     frontend/ir/condition.cpp | ||||||
|  |     frontend/ir/condition.h | ||||||
|  |     frontend/ir/flow_test.cpp | ||||||
|  |     frontend/ir/flow_test.h | ||||||
|  |     frontend/ir/ir_emitter.cpp | ||||||
|  |     frontend/ir/ir_emitter.h | ||||||
|  |     frontend/ir/microinstruction.cpp | ||||||
|  |     frontend/ir/microinstruction.h | ||||||
|  |     frontend/ir/opcode.cpp | ||||||
|  |     frontend/ir/opcode.h | ||||||
|  |     frontend/ir/opcode.inc | ||||||
|  |     frontend/ir/pred.h | ||||||
|  |     frontend/ir/reg.h | ||||||
|  |     frontend/ir/type.cpp | ||||||
|  |     frontend/ir/type.h | ||||||
|  |     frontend/ir/value.cpp | ||||||
|  |     frontend/ir/value.h | ||||||
|  |     frontend/maxwell/control_flow.cpp | ||||||
|  |     frontend/maxwell/control_flow.h | ||||||
|  |     frontend/maxwell/decode.cpp | ||||||
|  |     frontend/maxwell/decode.h | ||||||
|  |     frontend/maxwell/instruction.h | ||||||
|  |     frontend/maxwell/location.h | ||||||
|  |     frontend/maxwell/maxwell.inc | ||||||
|  |     frontend/maxwell/opcode.cpp | ||||||
|  |     frontend/maxwell/opcode.h | ||||||
|  |     frontend/maxwell/program.cpp | ||||||
|  |     frontend/maxwell/program.h | ||||||
|  |     frontend/maxwell/termination_code.cpp | ||||||
|  |     frontend/maxwell/termination_code.h | ||||||
|  |     frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp | ||||||
|  |     frontend/maxwell/translate/impl/floating_point_multi_function.cpp | ||||||
|  |     frontend/maxwell/translate/impl/impl.cpp | ||||||
|  |     frontend/maxwell/translate/impl/impl.h | ||||||
|  |     frontend/maxwell/translate/impl/load_store_attribute.cpp | ||||||
|  |     frontend/maxwell/translate/impl/load_store_memory.cpp | ||||||
|  |     frontend/maxwell/translate/impl/not_implemented.cpp | ||||||
|  |     frontend/maxwell/translate/impl/register_move.cpp | ||||||
|  |     frontend/maxwell/translate/translate.cpp | ||||||
|  |     frontend/maxwell/translate/translate.h | ||||||
|  |     ir_opt/dead_code_elimination_pass.cpp | ||||||
|  |     ir_opt/get_set_elimination_pass.cpp | ||||||
|  |     ir_opt/identity_removal_pass.cpp | ||||||
|  |     ir_opt/passes.h | ||||||
|  |     ir_opt/verification_pass.cpp | ||||||
|  |     main.cpp | ||||||
|  | ) | ||||||
|  | target_link_libraries(shader_recompiler PRIVATE fmt::fmt) | ||||||
|  |  | ||||||
|  | if (MSVC) | ||||||
|  |     target_compile_options(shader_recompiler PRIVATE | ||||||
|  |         /W4 | ||||||
|  |         /WX | ||||||
|  |         /we4018 # 'expression' : signed/unsigned mismatch | ||||||
|  |         /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) | ||||||
|  |         /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch | ||||||
|  |         /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | ||||||
|  |         /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data | ||||||
|  |         /we4305 # 'context' : truncation from 'type1' to 'type2' | ||||||
|  |         /we4800 # Implicit conversion from 'type' to bool. Possible information loss | ||||||
|  |         /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. | ||||||
|  |     ) | ||||||
|  | else() | ||||||
|  |     target_compile_options(shader_recompiler PRIVATE | ||||||
|  |         -Werror | ||||||
|  |         -Werror=conversion | ||||||
|  |         -Werror=ignored-qualifiers | ||||||
|  |         -Werror=implicit-fallthrough | ||||||
|  |         -Werror=shadow | ||||||
|  |         -Werror=sign-compare | ||||||
|  |         $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||||||
|  |         $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||||||
|  |         -Werror=unused-variable | ||||||
|  |     ) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | create_target_directory_groups(shader_recompiler) | ||||||
							
								
								
									
										14
									
								
								src/shader_recompiler/environment.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/shader_recompiler/environment.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | namespace Shader { | ||||||
|  |  | ||||||
|  | class Environment { | ||||||
|  | public: | ||||||
|  |     virtual ~Environment() = default; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] virtual u64 ReadInstruction(u32 address) const = 0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader | ||||||
							
								
								
									
										42
									
								
								src/shader_recompiler/exception.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/shader_recompiler/exception.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stdexcept> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | namespace Shader { | ||||||
|  |  | ||||||
|  | class LogicError : public std::logic_error { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     LogicError(const char* message, Args&&... args) | ||||||
|  |         : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class RuntimeError : public std::runtime_error { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     RuntimeError(const char* message, Args&&... args) | ||||||
|  |         : std::runtime_error{fmt::format(message, std::forward<Args>(args)...)} {} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class NotImplementedException : public std::logic_error { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     NotImplementedException(const char* message, Args&&... args) | ||||||
|  |         : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class InvalidArgument : public std::invalid_argument { | ||||||
|  | public: | ||||||
|  |     template <typename... Args> | ||||||
|  |     InvalidArgument(const char* message, Args&&... args) | ||||||
|  |         : std::invalid_argument{fmt::format(message, std::forward<Args>(args)...)} {} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader | ||||||
							
								
								
									
										42
									
								
								src/shader_recompiler/file_environment.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/shader_recompiler/file_environment.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #include <cstdio> | ||||||
|  |  | ||||||
|  | #include "exception.h" | ||||||
|  | #include "file_environment.h" | ||||||
|  |  | ||||||
|  | namespace Shader { | ||||||
|  |  | ||||||
|  | FileEnvironment::FileEnvironment(const char* path) { | ||||||
|  |     std::FILE* const file{std::fopen(path, "rb")}; | ||||||
|  |     if (!file) { | ||||||
|  |         throw RuntimeError("Failed to open file='{}'", path); | ||||||
|  |     } | ||||||
|  |     std::fseek(file, 0, SEEK_END); | ||||||
|  |     const long size{std::ftell(file)}; | ||||||
|  |     std::rewind(file); | ||||||
|  |     if (size % 8 != 0) { | ||||||
|  |         std::fclose(file); | ||||||
|  |         throw RuntimeError("File size={} is not aligned to 8", size); | ||||||
|  |     } | ||||||
|  |     // TODO: Use a unique_ptr to avoid zero-initializing this | ||||||
|  |     const size_t num_inst{static_cast<size_t>(size) / 8}; | ||||||
|  |     data.resize(num_inst); | ||||||
|  |     if (std::fread(data.data(), 8, num_inst, file) != num_inst) { | ||||||
|  |         std::fclose(file); | ||||||
|  |         throw RuntimeError("Failed to read instructions={} from file='{}'", num_inst, path); | ||||||
|  |     } | ||||||
|  |     std::fclose(file); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | FileEnvironment::~FileEnvironment() = default; | ||||||
|  |  | ||||||
|  | u64 FileEnvironment::ReadInstruction(u32 offset) const { | ||||||
|  |     if (offset % 8 != 0) { | ||||||
|  |         throw InvalidArgument("offset={} is not aligned to 8", offset); | ||||||
|  |     } | ||||||
|  |     if (offset / 8 >= static_cast<u32>(data.size())) { | ||||||
|  |         throw InvalidArgument("offset={} is out of bounds", offset); | ||||||
|  |     } | ||||||
|  |     return data[offset / 8]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader | ||||||
							
								
								
									
										21
									
								
								src/shader_recompiler/file_environment.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/shader_recompiler/file_environment.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "environment.h" | ||||||
|  |  | ||||||
|  | namespace Shader { | ||||||
|  |  | ||||||
|  | class FileEnvironment final : public Environment { | ||||||
|  | public: | ||||||
|  |     explicit FileEnvironment(const char* path); | ||||||
|  |     ~FileEnvironment() override; | ||||||
|  |  | ||||||
|  |     u64 ReadInstruction(u32 offset) const override; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     std::vector<u64> data; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader | ||||||
							
								
								
									
										447
									
								
								src/shader_recompiler/frontend/ir/attribute.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								src/shader_recompiler/frontend/ir/attribute.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,447 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/attribute.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | bool IsGeneric(Attribute attribute) noexcept { | ||||||
|  |     return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int GenericAttributeIndex(Attribute attribute) { | ||||||
|  |     if (!IsGeneric(attribute)) { | ||||||
|  |         throw InvalidArgument("Attribute is not generic {}", attribute); | ||||||
|  |     } | ||||||
|  |     return (static_cast<int>(attribute) - static_cast<int>(Attribute::Generic0X)) / 4; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string NameOf(Attribute attribute) { | ||||||
|  |     switch (attribute) { | ||||||
|  |     case Attribute::PrimitiveId: | ||||||
|  |         return "PrimitiveId"; | ||||||
|  |     case Attribute::Layer: | ||||||
|  |         return "Layer"; | ||||||
|  |     case Attribute::ViewportIndex: | ||||||
|  |         return "ViewportIndex"; | ||||||
|  |     case Attribute::PointSize: | ||||||
|  |         return "PointSize"; | ||||||
|  |     case Attribute::PositionX: | ||||||
|  |         return "Position.X"; | ||||||
|  |     case Attribute::PositionY: | ||||||
|  |         return "Position.Y"; | ||||||
|  |     case Attribute::PositionZ: | ||||||
|  |         return "Position.Z"; | ||||||
|  |     case Attribute::PositionW: | ||||||
|  |         return "Position.W"; | ||||||
|  |     case Attribute::Generic0X: | ||||||
|  |         return "Generic[0].X"; | ||||||
|  |     case Attribute::Generic0Y: | ||||||
|  |         return "Generic[0].Y"; | ||||||
|  |     case Attribute::Generic0Z: | ||||||
|  |         return "Generic[0].Z"; | ||||||
|  |     case Attribute::Generic0W: | ||||||
|  |         return "Generic[0].W"; | ||||||
|  |     case Attribute::Generic1X: | ||||||
|  |         return "Generic[1].X"; | ||||||
|  |     case Attribute::Generic1Y: | ||||||
|  |         return "Generic[1].Y"; | ||||||
|  |     case Attribute::Generic1Z: | ||||||
|  |         return "Generic[1].Z"; | ||||||
|  |     case Attribute::Generic1W: | ||||||
|  |         return "Generic[1].W"; | ||||||
|  |     case Attribute::Generic2X: | ||||||
|  |         return "Generic[2].X"; | ||||||
|  |     case Attribute::Generic2Y: | ||||||
|  |         return "Generic[2].Y"; | ||||||
|  |     case Attribute::Generic2Z: | ||||||
|  |         return "Generic[2].Z"; | ||||||
|  |     case Attribute::Generic2W: | ||||||
|  |         return "Generic[2].W"; | ||||||
|  |     case Attribute::Generic3X: | ||||||
|  |         return "Generic[3].X"; | ||||||
|  |     case Attribute::Generic3Y: | ||||||
|  |         return "Generic[3].Y"; | ||||||
|  |     case Attribute::Generic3Z: | ||||||
|  |         return "Generic[3].Z"; | ||||||
|  |     case Attribute::Generic3W: | ||||||
|  |         return "Generic[3].W"; | ||||||
|  |     case Attribute::Generic4X: | ||||||
|  |         return "Generic[4].X"; | ||||||
|  |     case Attribute::Generic4Y: | ||||||
|  |         return "Generic[4].Y"; | ||||||
|  |     case Attribute::Generic4Z: | ||||||
|  |         return "Generic[4].Z"; | ||||||
|  |     case Attribute::Generic4W: | ||||||
|  |         return "Generic[4].W"; | ||||||
|  |     case Attribute::Generic5X: | ||||||
|  |         return "Generic[5].X"; | ||||||
|  |     case Attribute::Generic5Y: | ||||||
|  |         return "Generic[5].Y"; | ||||||
|  |     case Attribute::Generic5Z: | ||||||
|  |         return "Generic[5].Z"; | ||||||
|  |     case Attribute::Generic5W: | ||||||
|  |         return "Generic[5].W"; | ||||||
|  |     case Attribute::Generic6X: | ||||||
|  |         return "Generic[6].X"; | ||||||
|  |     case Attribute::Generic6Y: | ||||||
|  |         return "Generic[6].Y"; | ||||||
|  |     case Attribute::Generic6Z: | ||||||
|  |         return "Generic[6].Z"; | ||||||
|  |     case Attribute::Generic6W: | ||||||
|  |         return "Generic[6].W"; | ||||||
|  |     case Attribute::Generic7X: | ||||||
|  |         return "Generic[7].X"; | ||||||
|  |     case Attribute::Generic7Y: | ||||||
|  |         return "Generic[7].Y"; | ||||||
|  |     case Attribute::Generic7Z: | ||||||
|  |         return "Generic[7].Z"; | ||||||
|  |     case Attribute::Generic7W: | ||||||
|  |         return "Generic[7].W"; | ||||||
|  |     case Attribute::Generic8X: | ||||||
|  |         return "Generic[8].X"; | ||||||
|  |     case Attribute::Generic8Y: | ||||||
|  |         return "Generic[8].Y"; | ||||||
|  |     case Attribute::Generic8Z: | ||||||
|  |         return "Generic[8].Z"; | ||||||
|  |     case Attribute::Generic8W: | ||||||
|  |         return "Generic[8].W"; | ||||||
|  |     case Attribute::Generic9X: | ||||||
|  |         return "Generic[9].X"; | ||||||
|  |     case Attribute::Generic9Y: | ||||||
|  |         return "Generic[9].Y"; | ||||||
|  |     case Attribute::Generic9Z: | ||||||
|  |         return "Generic[9].Z"; | ||||||
|  |     case Attribute::Generic9W: | ||||||
|  |         return "Generic[9].W"; | ||||||
|  |     case Attribute::Generic10X: | ||||||
|  |         return "Generic[10].X"; | ||||||
|  |     case Attribute::Generic10Y: | ||||||
|  |         return "Generic[10].Y"; | ||||||
|  |     case Attribute::Generic10Z: | ||||||
|  |         return "Generic[10].Z"; | ||||||
|  |     case Attribute::Generic10W: | ||||||
|  |         return "Generic[10].W"; | ||||||
|  |     case Attribute::Generic11X: | ||||||
|  |         return "Generic[11].X"; | ||||||
|  |     case Attribute::Generic11Y: | ||||||
|  |         return "Generic[11].Y"; | ||||||
|  |     case Attribute::Generic11Z: | ||||||
|  |         return "Generic[11].Z"; | ||||||
|  |     case Attribute::Generic11W: | ||||||
|  |         return "Generic[11].W"; | ||||||
|  |     case Attribute::Generic12X: | ||||||
|  |         return "Generic[12].X"; | ||||||
|  |     case Attribute::Generic12Y: | ||||||
|  |         return "Generic[12].Y"; | ||||||
|  |     case Attribute::Generic12Z: | ||||||
|  |         return "Generic[12].Z"; | ||||||
|  |     case Attribute::Generic12W: | ||||||
|  |         return "Generic[12].W"; | ||||||
|  |     case Attribute::Generic13X: | ||||||
|  |         return "Generic[13].X"; | ||||||
|  |     case Attribute::Generic13Y: | ||||||
|  |         return "Generic[13].Y"; | ||||||
|  |     case Attribute::Generic13Z: | ||||||
|  |         return "Generic[13].Z"; | ||||||
|  |     case Attribute::Generic13W: | ||||||
|  |         return "Generic[13].W"; | ||||||
|  |     case Attribute::Generic14X: | ||||||
|  |         return "Generic[14].X"; | ||||||
|  |     case Attribute::Generic14Y: | ||||||
|  |         return "Generic[14].Y"; | ||||||
|  |     case Attribute::Generic14Z: | ||||||
|  |         return "Generic[14].Z"; | ||||||
|  |     case Attribute::Generic14W: | ||||||
|  |         return "Generic[14].W"; | ||||||
|  |     case Attribute::Generic15X: | ||||||
|  |         return "Generic[15].X"; | ||||||
|  |     case Attribute::Generic15Y: | ||||||
|  |         return "Generic[15].Y"; | ||||||
|  |     case Attribute::Generic15Z: | ||||||
|  |         return "Generic[15].Z"; | ||||||
|  |     case Attribute::Generic15W: | ||||||
|  |         return "Generic[15].W"; | ||||||
|  |     case Attribute::Generic16X: | ||||||
|  |         return "Generic[16].X"; | ||||||
|  |     case Attribute::Generic16Y: | ||||||
|  |         return "Generic[16].Y"; | ||||||
|  |     case Attribute::Generic16Z: | ||||||
|  |         return "Generic[16].Z"; | ||||||
|  |     case Attribute::Generic16W: | ||||||
|  |         return "Generic[16].W"; | ||||||
|  |     case Attribute::Generic17X: | ||||||
|  |         return "Generic[17].X"; | ||||||
|  |     case Attribute::Generic17Y: | ||||||
|  |         return "Generic[17].Y"; | ||||||
|  |     case Attribute::Generic17Z: | ||||||
|  |         return "Generic[17].Z"; | ||||||
|  |     case Attribute::Generic17W: | ||||||
|  |         return "Generic[17].W"; | ||||||
|  |     case Attribute::Generic18X: | ||||||
|  |         return "Generic[18].X"; | ||||||
|  |     case Attribute::Generic18Y: | ||||||
|  |         return "Generic[18].Y"; | ||||||
|  |     case Attribute::Generic18Z: | ||||||
|  |         return "Generic[18].Z"; | ||||||
|  |     case Attribute::Generic18W: | ||||||
|  |         return "Generic[18].W"; | ||||||
|  |     case Attribute::Generic19X: | ||||||
|  |         return "Generic[19].X"; | ||||||
|  |     case Attribute::Generic19Y: | ||||||
|  |         return "Generic[19].Y"; | ||||||
|  |     case Attribute::Generic19Z: | ||||||
|  |         return "Generic[19].Z"; | ||||||
|  |     case Attribute::Generic19W: | ||||||
|  |         return "Generic[19].W"; | ||||||
|  |     case Attribute::Generic20X: | ||||||
|  |         return "Generic[20].X"; | ||||||
|  |     case Attribute::Generic20Y: | ||||||
|  |         return "Generic[20].Y"; | ||||||
|  |     case Attribute::Generic20Z: | ||||||
|  |         return "Generic[20].Z"; | ||||||
|  |     case Attribute::Generic20W: | ||||||
|  |         return "Generic[20].W"; | ||||||
|  |     case Attribute::Generic21X: | ||||||
|  |         return "Generic[21].X"; | ||||||
|  |     case Attribute::Generic21Y: | ||||||
|  |         return "Generic[21].Y"; | ||||||
|  |     case Attribute::Generic21Z: | ||||||
|  |         return "Generic[21].Z"; | ||||||
|  |     case Attribute::Generic21W: | ||||||
|  |         return "Generic[21].W"; | ||||||
|  |     case Attribute::Generic22X: | ||||||
|  |         return "Generic[22].X"; | ||||||
|  |     case Attribute::Generic22Y: | ||||||
|  |         return "Generic[22].Y"; | ||||||
|  |     case Attribute::Generic22Z: | ||||||
|  |         return "Generic[22].Z"; | ||||||
|  |     case Attribute::Generic22W: | ||||||
|  |         return "Generic[22].W"; | ||||||
|  |     case Attribute::Generic23X: | ||||||
|  |         return "Generic[23].X"; | ||||||
|  |     case Attribute::Generic23Y: | ||||||
|  |         return "Generic[23].Y"; | ||||||
|  |     case Attribute::Generic23Z: | ||||||
|  |         return "Generic[23].Z"; | ||||||
|  |     case Attribute::Generic23W: | ||||||
|  |         return "Generic[23].W"; | ||||||
|  |     case Attribute::Generic24X: | ||||||
|  |         return "Generic[24].X"; | ||||||
|  |     case Attribute::Generic24Y: | ||||||
|  |         return "Generic[24].Y"; | ||||||
|  |     case Attribute::Generic24Z: | ||||||
|  |         return "Generic[24].Z"; | ||||||
|  |     case Attribute::Generic24W: | ||||||
|  |         return "Generic[24].W"; | ||||||
|  |     case Attribute::Generic25X: | ||||||
|  |         return "Generic[25].X"; | ||||||
|  |     case Attribute::Generic25Y: | ||||||
|  |         return "Generic[25].Y"; | ||||||
|  |     case Attribute::Generic25Z: | ||||||
|  |         return "Generic[25].Z"; | ||||||
|  |     case Attribute::Generic25W: | ||||||
|  |         return "Generic[25].W"; | ||||||
|  |     case Attribute::Generic26X: | ||||||
|  |         return "Generic[26].X"; | ||||||
|  |     case Attribute::Generic26Y: | ||||||
|  |         return "Generic[26].Y"; | ||||||
|  |     case Attribute::Generic26Z: | ||||||
|  |         return "Generic[26].Z"; | ||||||
|  |     case Attribute::Generic26W: | ||||||
|  |         return "Generic[26].W"; | ||||||
|  |     case Attribute::Generic27X: | ||||||
|  |         return "Generic[27].X"; | ||||||
|  |     case Attribute::Generic27Y: | ||||||
|  |         return "Generic[27].Y"; | ||||||
|  |     case Attribute::Generic27Z: | ||||||
|  |         return "Generic[27].Z"; | ||||||
|  |     case Attribute::Generic27W: | ||||||
|  |         return "Generic[27].W"; | ||||||
|  |     case Attribute::Generic28X: | ||||||
|  |         return "Generic[28].X"; | ||||||
|  |     case Attribute::Generic28Y: | ||||||
|  |         return "Generic[28].Y"; | ||||||
|  |     case Attribute::Generic28Z: | ||||||
|  |         return "Generic[28].Z"; | ||||||
|  |     case Attribute::Generic28W: | ||||||
|  |         return "Generic[28].W"; | ||||||
|  |     case Attribute::Generic29X: | ||||||
|  |         return "Generic[29].X"; | ||||||
|  |     case Attribute::Generic29Y: | ||||||
|  |         return "Generic[29].Y"; | ||||||
|  |     case Attribute::Generic29Z: | ||||||
|  |         return "Generic[29].Z"; | ||||||
|  |     case Attribute::Generic29W: | ||||||
|  |         return "Generic[29].W"; | ||||||
|  |     case Attribute::Generic30X: | ||||||
|  |         return "Generic[30].X"; | ||||||
|  |     case Attribute::Generic30Y: | ||||||
|  |         return "Generic[30].Y"; | ||||||
|  |     case Attribute::Generic30Z: | ||||||
|  |         return "Generic[30].Z"; | ||||||
|  |     case Attribute::Generic30W: | ||||||
|  |         return "Generic[30].W"; | ||||||
|  |     case Attribute::Generic31X: | ||||||
|  |         return "Generic[31].X"; | ||||||
|  |     case Attribute::Generic31Y: | ||||||
|  |         return "Generic[31].Y"; | ||||||
|  |     case Attribute::Generic31Z: | ||||||
|  |         return "Generic[31].Z"; | ||||||
|  |     case Attribute::Generic31W: | ||||||
|  |         return "Generic[31].W"; | ||||||
|  |     case Attribute::ColorFrontDiffuseR: | ||||||
|  |         return "ColorFrontDiffuse.R"; | ||||||
|  |     case Attribute::ColorFrontDiffuseG: | ||||||
|  |         return "ColorFrontDiffuse.G"; | ||||||
|  |     case Attribute::ColorFrontDiffuseB: | ||||||
|  |         return "ColorFrontDiffuse.B"; | ||||||
|  |     case Attribute::ColorFrontDiffuseA: | ||||||
|  |         return "ColorFrontDiffuse.A"; | ||||||
|  |     case Attribute::ColorFrontSpecularR: | ||||||
|  |         return "ColorFrontSpecular.R"; | ||||||
|  |     case Attribute::ColorFrontSpecularG: | ||||||
|  |         return "ColorFrontSpecular.G"; | ||||||
|  |     case Attribute::ColorFrontSpecularB: | ||||||
|  |         return "ColorFrontSpecular.B"; | ||||||
|  |     case Attribute::ColorFrontSpecularA: | ||||||
|  |         return "ColorFrontSpecular.A"; | ||||||
|  |     case Attribute::ColorBackDiffuseR: | ||||||
|  |         return "ColorBackDiffuse.R"; | ||||||
|  |     case Attribute::ColorBackDiffuseG: | ||||||
|  |         return "ColorBackDiffuse.G"; | ||||||
|  |     case Attribute::ColorBackDiffuseB: | ||||||
|  |         return "ColorBackDiffuse.B"; | ||||||
|  |     case Attribute::ColorBackDiffuseA: | ||||||
|  |         return "ColorBackDiffuse.A"; | ||||||
|  |     case Attribute::ColorBackSpecularR: | ||||||
|  |         return "ColorBackSpecular.R"; | ||||||
|  |     case Attribute::ColorBackSpecularG: | ||||||
|  |         return "ColorBackSpecular.G"; | ||||||
|  |     case Attribute::ColorBackSpecularB: | ||||||
|  |         return "ColorBackSpecular.B"; | ||||||
|  |     case Attribute::ColorBackSpecularA: | ||||||
|  |         return "ColorBackSpecular.A"; | ||||||
|  |     case Attribute::ClipDistance0: | ||||||
|  |         return "ClipDistance[0]"; | ||||||
|  |     case Attribute::ClipDistance1: | ||||||
|  |         return "ClipDistance[1]"; | ||||||
|  |     case Attribute::ClipDistance2: | ||||||
|  |         return "ClipDistance[2]"; | ||||||
|  |     case Attribute::ClipDistance3: | ||||||
|  |         return "ClipDistance[3]"; | ||||||
|  |     case Attribute::ClipDistance4: | ||||||
|  |         return "ClipDistance[4]"; | ||||||
|  |     case Attribute::ClipDistance5: | ||||||
|  |         return "ClipDistance[5]"; | ||||||
|  |     case Attribute::ClipDistance6: | ||||||
|  |         return "ClipDistance[6]"; | ||||||
|  |     case Attribute::ClipDistance7: | ||||||
|  |         return "ClipDistance[7]"; | ||||||
|  |     case Attribute::PointSpriteS: | ||||||
|  |         return "PointSprite.S"; | ||||||
|  |     case Attribute::PointSpriteT: | ||||||
|  |         return "PointSprite.T"; | ||||||
|  |     case Attribute::FogCoordinate: | ||||||
|  |         return "FogCoordinate"; | ||||||
|  |     case Attribute::TessellationEvaluationPointU: | ||||||
|  |         return "TessellationEvaluationPoint.U"; | ||||||
|  |     case Attribute::TessellationEvaluationPointV: | ||||||
|  |         return "TessellationEvaluationPoint.V"; | ||||||
|  |     case Attribute::InstanceId: | ||||||
|  |         return "InstanceId"; | ||||||
|  |     case Attribute::VertexId: | ||||||
|  |         return "VertexId"; | ||||||
|  |     case Attribute::FixedFncTexture0S: | ||||||
|  |         return "FixedFncTexture[0].S"; | ||||||
|  |     case Attribute::FixedFncTexture0T: | ||||||
|  |         return "FixedFncTexture[0].T"; | ||||||
|  |     case Attribute::FixedFncTexture0R: | ||||||
|  |         return "FixedFncTexture[0].R"; | ||||||
|  |     case Attribute::FixedFncTexture0Q: | ||||||
|  |         return "FixedFncTexture[0].Q"; | ||||||
|  |     case Attribute::FixedFncTexture1S: | ||||||
|  |         return "FixedFncTexture[1].S"; | ||||||
|  |     case Attribute::FixedFncTexture1T: | ||||||
|  |         return "FixedFncTexture[1].T"; | ||||||
|  |     case Attribute::FixedFncTexture1R: | ||||||
|  |         return "FixedFncTexture[1].R"; | ||||||
|  |     case Attribute::FixedFncTexture1Q: | ||||||
|  |         return "FixedFncTexture[1].Q"; | ||||||
|  |     case Attribute::FixedFncTexture2S: | ||||||
|  |         return "FixedFncTexture[2].S"; | ||||||
|  |     case Attribute::FixedFncTexture2T: | ||||||
|  |         return "FixedFncTexture[2].T"; | ||||||
|  |     case Attribute::FixedFncTexture2R: | ||||||
|  |         return "FixedFncTexture[2].R"; | ||||||
|  |     case Attribute::FixedFncTexture2Q: | ||||||
|  |         return "FixedFncTexture[2].Q"; | ||||||
|  |     case Attribute::FixedFncTexture3S: | ||||||
|  |         return "FixedFncTexture[3].S"; | ||||||
|  |     case Attribute::FixedFncTexture3T: | ||||||
|  |         return "FixedFncTexture[3].T"; | ||||||
|  |     case Attribute::FixedFncTexture3R: | ||||||
|  |         return "FixedFncTexture[3].R"; | ||||||
|  |     case Attribute::FixedFncTexture3Q: | ||||||
|  |         return "FixedFncTexture[3].Q"; | ||||||
|  |     case Attribute::FixedFncTexture4S: | ||||||
|  |         return "FixedFncTexture[4].S"; | ||||||
|  |     case Attribute::FixedFncTexture4T: | ||||||
|  |         return "FixedFncTexture[4].T"; | ||||||
|  |     case Attribute::FixedFncTexture4R: | ||||||
|  |         return "FixedFncTexture[4].R"; | ||||||
|  |     case Attribute::FixedFncTexture4Q: | ||||||
|  |         return "FixedFncTexture[4].Q"; | ||||||
|  |     case Attribute::FixedFncTexture5S: | ||||||
|  |         return "FixedFncTexture[5].S"; | ||||||
|  |     case Attribute::FixedFncTexture5T: | ||||||
|  |         return "FixedFncTexture[5].T"; | ||||||
|  |     case Attribute::FixedFncTexture5R: | ||||||
|  |         return "FixedFncTexture[5].R"; | ||||||
|  |     case Attribute::FixedFncTexture5Q: | ||||||
|  |         return "FixedFncTexture[5].Q"; | ||||||
|  |     case Attribute::FixedFncTexture6S: | ||||||
|  |         return "FixedFncTexture[6].S"; | ||||||
|  |     case Attribute::FixedFncTexture6T: | ||||||
|  |         return "FixedFncTexture[6].T"; | ||||||
|  |     case Attribute::FixedFncTexture6R: | ||||||
|  |         return "FixedFncTexture[6].R"; | ||||||
|  |     case Attribute::FixedFncTexture6Q: | ||||||
|  |         return "FixedFncTexture[6].Q"; | ||||||
|  |     case Attribute::FixedFncTexture7S: | ||||||
|  |         return "FixedFncTexture[7].S"; | ||||||
|  |     case Attribute::FixedFncTexture7T: | ||||||
|  |         return "FixedFncTexture[7].T"; | ||||||
|  |     case Attribute::FixedFncTexture7R: | ||||||
|  |         return "FixedFncTexture[7].R"; | ||||||
|  |     case Attribute::FixedFncTexture7Q: | ||||||
|  |         return "FixedFncTexture[7].Q"; | ||||||
|  |     case Attribute::FixedFncTexture8S: | ||||||
|  |         return "FixedFncTexture[8].S"; | ||||||
|  |     case Attribute::FixedFncTexture8T: | ||||||
|  |         return "FixedFncTexture[8].T"; | ||||||
|  |     case Attribute::FixedFncTexture8R: | ||||||
|  |         return "FixedFncTexture[8].R"; | ||||||
|  |     case Attribute::FixedFncTexture8Q: | ||||||
|  |         return "FixedFncTexture[8].Q"; | ||||||
|  |     case Attribute::FixedFncTexture9S: | ||||||
|  |         return "FixedFncTexture[9].S"; | ||||||
|  |     case Attribute::FixedFncTexture9T: | ||||||
|  |         return "FixedFncTexture[9].T"; | ||||||
|  |     case Attribute::FixedFncTexture9R: | ||||||
|  |         return "FixedFncTexture[9].R"; | ||||||
|  |     case Attribute::FixedFncTexture9Q: | ||||||
|  |         return "FixedFncTexture[9].Q"; | ||||||
|  |     case Attribute::ViewportMask: | ||||||
|  |         return "ViewportMask"; | ||||||
|  |     case Attribute::FrontFace: | ||||||
|  |         return "FrontFace"; | ||||||
|  |     } | ||||||
|  |     return fmt::format("<reserved attribute {}>", static_cast<int>(attribute)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										242
									
								
								src/shader_recompiler/frontend/ir/attribute.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								src/shader_recompiler/frontend/ir/attribute.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class Attribute : u64 { | ||||||
|  |     PrimitiveId = 24, | ||||||
|  |     Layer = 25, | ||||||
|  |     ViewportIndex = 26, | ||||||
|  |     PointSize = 27, | ||||||
|  |     PositionX = 28, | ||||||
|  |     PositionY = 29, | ||||||
|  |     PositionZ = 30, | ||||||
|  |     PositionW = 31, | ||||||
|  |     Generic0X = 32, | ||||||
|  |     Generic0Y = 33, | ||||||
|  |     Generic0Z = 34, | ||||||
|  |     Generic0W = 35, | ||||||
|  |     Generic1X = 36, | ||||||
|  |     Generic1Y = 37, | ||||||
|  |     Generic1Z = 38, | ||||||
|  |     Generic1W = 39, | ||||||
|  |     Generic2X = 40, | ||||||
|  |     Generic2Y = 41, | ||||||
|  |     Generic2Z = 42, | ||||||
|  |     Generic2W = 43, | ||||||
|  |     Generic3X = 44, | ||||||
|  |     Generic3Y = 45, | ||||||
|  |     Generic3Z = 46, | ||||||
|  |     Generic3W = 47, | ||||||
|  |     Generic4X = 48, | ||||||
|  |     Generic4Y = 49, | ||||||
|  |     Generic4Z = 50, | ||||||
|  |     Generic4W = 51, | ||||||
|  |     Generic5X = 52, | ||||||
|  |     Generic5Y = 53, | ||||||
|  |     Generic5Z = 54, | ||||||
|  |     Generic5W = 55, | ||||||
|  |     Generic6X = 56, | ||||||
|  |     Generic6Y = 57, | ||||||
|  |     Generic6Z = 58, | ||||||
|  |     Generic6W = 59, | ||||||
|  |     Generic7X = 60, | ||||||
|  |     Generic7Y = 61, | ||||||
|  |     Generic7Z = 62, | ||||||
|  |     Generic7W = 63, | ||||||
|  |     Generic8X = 64, | ||||||
|  |     Generic8Y = 65, | ||||||
|  |     Generic8Z = 66, | ||||||
|  |     Generic8W = 67, | ||||||
|  |     Generic9X = 68, | ||||||
|  |     Generic9Y = 69, | ||||||
|  |     Generic9Z = 70, | ||||||
|  |     Generic9W = 71, | ||||||
|  |     Generic10X = 72, | ||||||
|  |     Generic10Y = 73, | ||||||
|  |     Generic10Z = 74, | ||||||
|  |     Generic10W = 75, | ||||||
|  |     Generic11X = 76, | ||||||
|  |     Generic11Y = 77, | ||||||
|  |     Generic11Z = 78, | ||||||
|  |     Generic11W = 79, | ||||||
|  |     Generic12X = 80, | ||||||
|  |     Generic12Y = 81, | ||||||
|  |     Generic12Z = 82, | ||||||
|  |     Generic12W = 83, | ||||||
|  |     Generic13X = 84, | ||||||
|  |     Generic13Y = 85, | ||||||
|  |     Generic13Z = 86, | ||||||
|  |     Generic13W = 87, | ||||||
|  |     Generic14X = 88, | ||||||
|  |     Generic14Y = 89, | ||||||
|  |     Generic14Z = 90, | ||||||
|  |     Generic14W = 91, | ||||||
|  |     Generic15X = 92, | ||||||
|  |     Generic15Y = 93, | ||||||
|  |     Generic15Z = 94, | ||||||
|  |     Generic15W = 95, | ||||||
|  |     Generic16X = 96, | ||||||
|  |     Generic16Y = 97, | ||||||
|  |     Generic16Z = 98, | ||||||
|  |     Generic16W = 99, | ||||||
|  |     Generic17X = 100, | ||||||
|  |     Generic17Y = 101, | ||||||
|  |     Generic17Z = 102, | ||||||
|  |     Generic17W = 103, | ||||||
|  |     Generic18X = 104, | ||||||
|  |     Generic18Y = 105, | ||||||
|  |     Generic18Z = 106, | ||||||
|  |     Generic18W = 107, | ||||||
|  |     Generic19X = 108, | ||||||
|  |     Generic19Y = 109, | ||||||
|  |     Generic19Z = 110, | ||||||
|  |     Generic19W = 111, | ||||||
|  |     Generic20X = 112, | ||||||
|  |     Generic20Y = 113, | ||||||
|  |     Generic20Z = 114, | ||||||
|  |     Generic20W = 115, | ||||||
|  |     Generic21X = 116, | ||||||
|  |     Generic21Y = 117, | ||||||
|  |     Generic21Z = 118, | ||||||
|  |     Generic21W = 119, | ||||||
|  |     Generic22X = 120, | ||||||
|  |     Generic22Y = 121, | ||||||
|  |     Generic22Z = 122, | ||||||
|  |     Generic22W = 123, | ||||||
|  |     Generic23X = 124, | ||||||
|  |     Generic23Y = 125, | ||||||
|  |     Generic23Z = 126, | ||||||
|  |     Generic23W = 127, | ||||||
|  |     Generic24X = 128, | ||||||
|  |     Generic24Y = 129, | ||||||
|  |     Generic24Z = 130, | ||||||
|  |     Generic24W = 131, | ||||||
|  |     Generic25X = 132, | ||||||
|  |     Generic25Y = 133, | ||||||
|  |     Generic25Z = 134, | ||||||
|  |     Generic25W = 135, | ||||||
|  |     Generic26X = 136, | ||||||
|  |     Generic26Y = 137, | ||||||
|  |     Generic26Z = 138, | ||||||
|  |     Generic26W = 139, | ||||||
|  |     Generic27X = 140, | ||||||
|  |     Generic27Y = 141, | ||||||
|  |     Generic27Z = 142, | ||||||
|  |     Generic27W = 143, | ||||||
|  |     Generic28X = 144, | ||||||
|  |     Generic28Y = 145, | ||||||
|  |     Generic28Z = 146, | ||||||
|  |     Generic28W = 147, | ||||||
|  |     Generic29X = 148, | ||||||
|  |     Generic29Y = 149, | ||||||
|  |     Generic29Z = 150, | ||||||
|  |     Generic29W = 151, | ||||||
|  |     Generic30X = 152, | ||||||
|  |     Generic30Y = 153, | ||||||
|  |     Generic30Z = 154, | ||||||
|  |     Generic30W = 155, | ||||||
|  |     Generic31X = 156, | ||||||
|  |     Generic31Y = 157, | ||||||
|  |     Generic31Z = 158, | ||||||
|  |     Generic31W = 159, | ||||||
|  |     ColorFrontDiffuseR = 160, | ||||||
|  |     ColorFrontDiffuseG = 161, | ||||||
|  |     ColorFrontDiffuseB = 162, | ||||||
|  |     ColorFrontDiffuseA = 163, | ||||||
|  |     ColorFrontSpecularR = 164, | ||||||
|  |     ColorFrontSpecularG = 165, | ||||||
|  |     ColorFrontSpecularB = 166, | ||||||
|  |     ColorFrontSpecularA = 167, | ||||||
|  |     ColorBackDiffuseR = 168, | ||||||
|  |     ColorBackDiffuseG = 169, | ||||||
|  |     ColorBackDiffuseB = 170, | ||||||
|  |     ColorBackDiffuseA = 171, | ||||||
|  |     ColorBackSpecularR = 172, | ||||||
|  |     ColorBackSpecularG = 173, | ||||||
|  |     ColorBackSpecularB = 174, | ||||||
|  |     ColorBackSpecularA = 175, | ||||||
|  |     ClipDistance0 = 176, | ||||||
|  |     ClipDistance1 = 177, | ||||||
|  |     ClipDistance2 = 178, | ||||||
|  |     ClipDistance3 = 179, | ||||||
|  |     ClipDistance4 = 180, | ||||||
|  |     ClipDistance5 = 181, | ||||||
|  |     ClipDistance6 = 182, | ||||||
|  |     ClipDistance7 = 183, | ||||||
|  |     PointSpriteS = 184, | ||||||
|  |     PointSpriteT = 185, | ||||||
|  |     FogCoordinate = 186, | ||||||
|  |     TessellationEvaluationPointU = 188, | ||||||
|  |     TessellationEvaluationPointV = 189, | ||||||
|  |     InstanceId = 190, | ||||||
|  |     VertexId = 191, | ||||||
|  |     FixedFncTexture0S = 192, | ||||||
|  |     FixedFncTexture0T = 193, | ||||||
|  |     FixedFncTexture0R = 194, | ||||||
|  |     FixedFncTexture0Q = 195, | ||||||
|  |     FixedFncTexture1S = 196, | ||||||
|  |     FixedFncTexture1T = 197, | ||||||
|  |     FixedFncTexture1R = 198, | ||||||
|  |     FixedFncTexture1Q = 199, | ||||||
|  |     FixedFncTexture2S = 200, | ||||||
|  |     FixedFncTexture2T = 201, | ||||||
|  |     FixedFncTexture2R = 202, | ||||||
|  |     FixedFncTexture2Q = 203, | ||||||
|  |     FixedFncTexture3S = 204, | ||||||
|  |     FixedFncTexture3T = 205, | ||||||
|  |     FixedFncTexture3R = 206, | ||||||
|  |     FixedFncTexture3Q = 207, | ||||||
|  |     FixedFncTexture4S = 208, | ||||||
|  |     FixedFncTexture4T = 209, | ||||||
|  |     FixedFncTexture4R = 210, | ||||||
|  |     FixedFncTexture4Q = 211, | ||||||
|  |     FixedFncTexture5S = 212, | ||||||
|  |     FixedFncTexture5T = 213, | ||||||
|  |     FixedFncTexture5R = 214, | ||||||
|  |     FixedFncTexture5Q = 215, | ||||||
|  |     FixedFncTexture6S = 216, | ||||||
|  |     FixedFncTexture6T = 217, | ||||||
|  |     FixedFncTexture6R = 218, | ||||||
|  |     FixedFncTexture6Q = 219, | ||||||
|  |     FixedFncTexture7S = 220, | ||||||
|  |     FixedFncTexture7T = 221, | ||||||
|  |     FixedFncTexture7R = 222, | ||||||
|  |     FixedFncTexture7Q = 223, | ||||||
|  |     FixedFncTexture8S = 224, | ||||||
|  |     FixedFncTexture8T = 225, | ||||||
|  |     FixedFncTexture8R = 226, | ||||||
|  |     FixedFncTexture8Q = 227, | ||||||
|  |     FixedFncTexture9S = 228, | ||||||
|  |     FixedFncTexture9T = 229, | ||||||
|  |     FixedFncTexture9R = 230, | ||||||
|  |     FixedFncTexture9Q = 231, | ||||||
|  |     ViewportMask = 232, | ||||||
|  |     FrontFace = 255, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | [[nodiscard]] bool IsGeneric(Attribute attribute) noexcept; | ||||||
|  |  | ||||||
|  | [[nodiscard]] int GenericAttributeIndex(Attribute attribute); | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string NameOf(Attribute attribute); | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Attribute> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) { | ||||||
|  |         return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										142
									
								
								src/shader_recompiler/frontend/ir/basic_block.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/shader_recompiler/frontend/ir/basic_block.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <initializer_list> | ||||||
|  | #include <map> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
|  | #include "common/bit_cast.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/value.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | Block::Block(u32 begin, u32 end) : location_begin{begin}, location_end{end} {} | ||||||
|  |  | ||||||
|  | Block::~Block() = default; | ||||||
|  |  | ||||||
|  | void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) { | ||||||
|  |     PrependNewInst(end(), op, args); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, | ||||||
|  |                                       std::initializer_list<Value> args) { | ||||||
|  |     Inst* const inst{std::construct_at(instruction_alloc_pool.allocate(), op)}; | ||||||
|  |     const auto result_it{instructions.insert(insertion_point, *inst)}; | ||||||
|  |  | ||||||
|  |     if (inst->NumArgs() != args.size()) { | ||||||
|  |         throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op); | ||||||
|  |     } | ||||||
|  |     std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable { | ||||||
|  |         inst->SetArg(index, arg); | ||||||
|  |         ++index; | ||||||
|  |     }); | ||||||
|  |     return result_it; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u32 Block::LocationBegin() const noexcept { | ||||||
|  |     return location_begin; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u32 Block::LocationEnd() const noexcept { | ||||||
|  |     return location_end; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Block::InstructionList& Block::Instructions() noexcept { | ||||||
|  |     return instructions; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const Block::InstructionList& Block::Instructions() const noexcept { | ||||||
|  |     return instructions; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index, | ||||||
|  |                               const std::map<const Inst*, size_t>& inst_to_index, | ||||||
|  |                               const Value& arg) { | ||||||
|  |     if (arg.IsEmpty()) { | ||||||
|  |         return "<null>"; | ||||||
|  |     } | ||||||
|  |     if (arg.IsLabel()) { | ||||||
|  |         if (const auto it{block_to_index.find(arg.Label())}; it != block_to_index.end()) { | ||||||
|  |             return fmt::format("{{Block ${}}}", it->second); | ||||||
|  |         } | ||||||
|  |         return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(arg.Label())); | ||||||
|  |     } | ||||||
|  |     if (!arg.IsImmediate()) { | ||||||
|  |         if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) { | ||||||
|  |             return fmt::format("%{}", it->second); | ||||||
|  |         } | ||||||
|  |         return fmt::format("%<unknown inst {:016x}>", reinterpret_cast<u64>(arg.Inst())); | ||||||
|  |     } | ||||||
|  |     switch (arg.Type()) { | ||||||
|  |     case Type::U1: | ||||||
|  |         return fmt::format("#{}", arg.U1() ? '1' : '0'); | ||||||
|  |     case Type::U8: | ||||||
|  |         return fmt::format("#{}", arg.U8()); | ||||||
|  |     case Type::U16: | ||||||
|  |         return fmt::format("#{}", arg.U16()); | ||||||
|  |     case Type::U32: | ||||||
|  |         return fmt::format("#{}", arg.U32()); | ||||||
|  |     case Type::U64: | ||||||
|  |         return fmt::format("#{}", arg.U64()); | ||||||
|  |     case Type::Reg: | ||||||
|  |         return fmt::format("{}", arg.Reg()); | ||||||
|  |     case Type::Pred: | ||||||
|  |         return fmt::format("{}", arg.Pred()); | ||||||
|  |     case Type::Attribute: | ||||||
|  |         return fmt::format("{}", arg.Attribute()); | ||||||
|  |     default: | ||||||
|  |         return "<unknown immediate type>"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string DumpBlock(const Block& block) { | ||||||
|  |     size_t inst_index{0}; | ||||||
|  |     std::map<const Inst*, size_t> inst_to_index; | ||||||
|  |     return DumpBlock(block, {}, inst_to_index, inst_index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index, | ||||||
|  |                       std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) { | ||||||
|  |     std::string ret{"Block"}; | ||||||
|  |     if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) { | ||||||
|  |         ret += fmt::format(" ${}", it->second); | ||||||
|  |     } | ||||||
|  |     ret += fmt::format(": begin={:04x} end={:04x}\n", block.LocationBegin(), block.LocationEnd()); | ||||||
|  |  | ||||||
|  |     for (const Inst& inst : block) { | ||||||
|  |         const Opcode op{inst.Opcode()}; | ||||||
|  |         ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst)); | ||||||
|  |         if (TypeOf(op) != Type::Void) { | ||||||
|  |             ret += fmt::format("%{:<5} = {}", inst_index, op); | ||||||
|  |         } else { | ||||||
|  |             ret += fmt::format("         {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces | ||||||
|  |         } | ||||||
|  |         const size_t arg_count{NumArgsOf(op)}; | ||||||
|  |         for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { | ||||||
|  |             const Value arg{inst.Arg(arg_index)}; | ||||||
|  |             ret += arg_index != 0 ? ", " : " "; | ||||||
|  |             ret += ArgToIndex(block_to_index, inst_to_index, arg); | ||||||
|  |  | ||||||
|  |             const Type actual_type{arg.Type()}; | ||||||
|  |             const Type expected_type{ArgTypeOf(op, arg_index)}; | ||||||
|  |             if (!AreTypesCompatible(actual_type, expected_type)) { | ||||||
|  |                 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (TypeOf(op) != Type::Void) { | ||||||
|  |             ret += fmt::format(" (uses: {})\n", inst.UseCount()); | ||||||
|  |         } else { | ||||||
|  |             ret += '\n'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inst_to_index.emplace(&inst, inst_index); | ||||||
|  |         ++inst_index; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										134
									
								
								src/shader_recompiler/frontend/ir/basic_block.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/shader_recompiler/frontend/ir/basic_block.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <initializer_list> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | #include <boost/intrusive/list.hpp> | ||||||
|  | #include <boost/pool/pool_alloc.hpp> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | class Block { | ||||||
|  | public: | ||||||
|  |     using InstructionList = boost::intrusive::list<Inst>; | ||||||
|  |     using size_type = InstructionList::size_type; | ||||||
|  |     using iterator = InstructionList::iterator; | ||||||
|  |     using const_iterator = InstructionList::const_iterator; | ||||||
|  |     using reverse_iterator = InstructionList::reverse_iterator; | ||||||
|  |     using const_reverse_iterator = InstructionList::const_reverse_iterator; | ||||||
|  |  | ||||||
|  |     explicit Block(u32 begin, u32 end); | ||||||
|  |     ~Block(); | ||||||
|  |  | ||||||
|  |     Block(const Block&) = delete; | ||||||
|  |     Block& operator=(const Block&) = delete; | ||||||
|  |  | ||||||
|  |     Block(Block&&) = default; | ||||||
|  |     Block& operator=(Block&&) = default; | ||||||
|  |  | ||||||
|  |     /// Appends a new instruction to the end of this basic block. | ||||||
|  |     void AppendNewInst(Opcode op, std::initializer_list<Value> args); | ||||||
|  |  | ||||||
|  |     /// Prepends a new instruction to this basic block before the insertion point. | ||||||
|  |     iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args); | ||||||
|  |  | ||||||
|  |     /// Gets the starting location of this basic block. | ||||||
|  |     [[nodiscard]] u32 LocationBegin() const noexcept; | ||||||
|  |     /// Gets the end location for this basic block. | ||||||
|  |     [[nodiscard]] u32 LocationEnd() const noexcept; | ||||||
|  |  | ||||||
|  |     /// Gets a mutable reference to the instruction list for this basic block. | ||||||
|  |     InstructionList& Instructions() noexcept; | ||||||
|  |     /// Gets an immutable reference to the instruction list for this basic block. | ||||||
|  |     const InstructionList& Instructions() const noexcept; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] bool empty() const { | ||||||
|  |         return instructions.empty(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] size_type size() const { | ||||||
|  |         return instructions.size(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] Inst& front() { | ||||||
|  |         return instructions.front(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const Inst& front() const { | ||||||
|  |         return instructions.front(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] Inst& back() { | ||||||
|  |         return instructions.back(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const Inst& back() const { | ||||||
|  |         return instructions.back(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] iterator begin() { | ||||||
|  |         return instructions.begin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_iterator begin() const { | ||||||
|  |         return instructions.begin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] iterator end() { | ||||||
|  |         return instructions.end(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_iterator end() const { | ||||||
|  |         return instructions.end(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] reverse_iterator rbegin() { | ||||||
|  |         return instructions.rbegin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_reverse_iterator rbegin() const { | ||||||
|  |         return instructions.rbegin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] reverse_iterator rend() { | ||||||
|  |         return instructions.rend(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_reverse_iterator rend() const { | ||||||
|  |         return instructions.rend(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] const_iterator cbegin() const { | ||||||
|  |         return instructions.cbegin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_iterator cend() const { | ||||||
|  |         return instructions.cend(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] const_reverse_iterator crbegin() const { | ||||||
|  |         return instructions.crbegin(); | ||||||
|  |     } | ||||||
|  |     [[nodiscard]] const_reverse_iterator crend() const { | ||||||
|  |         return instructions.crend(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     /// Starting location of this block | ||||||
|  |     u32 location_begin; | ||||||
|  |     /// End location of this block | ||||||
|  |     u32 location_end; | ||||||
|  |  | ||||||
|  |     /// List of instructions in this block. | ||||||
|  |     InstructionList instructions; | ||||||
|  |  | ||||||
|  |     /// Memory pool for instruction list | ||||||
|  |     boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free, | ||||||
|  |                                boost::details::pool::null_mutex> | ||||||
|  |         instruction_alloc_pool; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string DumpBlock(const Block& block); | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string DumpBlock(const Block& block, | ||||||
|  |                                     const std::map<const Block*, size_t>& block_to_index, | ||||||
|  |                                     std::map<const Inst*, size_t>& inst_to_index, | ||||||
|  |                                     size_t& inst_index); | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										31
									
								
								src/shader_recompiler/frontend/ir/condition.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/shader_recompiler/frontend/ir/condition.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/condition.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | std::string NameOf(Condition condition) { | ||||||
|  |     std::string ret; | ||||||
|  |     if (condition.FlowTest() != FlowTest::T) { | ||||||
|  |         ret = fmt::to_string(condition.FlowTest()); | ||||||
|  |     } | ||||||
|  |     const auto [pred, negated]{condition.Pred()}; | ||||||
|  |     if (pred != Pred::PT || negated) { | ||||||
|  |         if (!ret.empty()) { | ||||||
|  |             ret += '&'; | ||||||
|  |         } | ||||||
|  |         if (negated) { | ||||||
|  |             ret += '!'; | ||||||
|  |         } | ||||||
|  |         ret += fmt::to_string(pred); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										60
									
								
								src/shader_recompiler/frontend/ir/condition.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/shader_recompiler/frontend/ir/condition.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <compare> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/flow_test.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/pred.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | class Condition { | ||||||
|  | public: | ||||||
|  |     Condition() noexcept = default; | ||||||
|  |  | ||||||
|  |     explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept | ||||||
|  |         : flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)}, | ||||||
|  |           pred_negated{pred_negated_ ? u8{1} : u8{0}} {} | ||||||
|  |  | ||||||
|  |     explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept | ||||||
|  |         : Condition(FlowTest::T, pred_, pred_negated_) {} | ||||||
|  |  | ||||||
|  |     Condition(bool value) : Condition(Pred::PT, !value) {} | ||||||
|  |  | ||||||
|  |     auto operator<=>(const Condition&) const noexcept = default; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] IR::FlowTest FlowTest() const noexcept { | ||||||
|  |         return static_cast<IR::FlowTest>(flow_test); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] std::pair<IR::Pred, bool> Pred() const noexcept { | ||||||
|  |         return {static_cast<IR::Pred>(pred), pred_negated != 0}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     u16 flow_test; | ||||||
|  |     u8 pred; | ||||||
|  |     u8 pred_negated; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | std::string NameOf(Condition condition); | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Condition> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Condition& cond, FormatContext& ctx) { | ||||||
|  |         return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										83
									
								
								src/shader_recompiler/frontend/ir/flow_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/shader_recompiler/frontend/ir/flow_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/flow_test.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | std::string NameOf(FlowTest flow_test) { | ||||||
|  |     switch (flow_test) { | ||||||
|  |     case FlowTest::F: | ||||||
|  |         return "F"; | ||||||
|  |     case FlowTest::LT: | ||||||
|  |         return "LT"; | ||||||
|  |     case FlowTest::EQ: | ||||||
|  |         return "EQ"; | ||||||
|  |     case FlowTest::LE: | ||||||
|  |         return "LE"; | ||||||
|  |     case FlowTest::GT: | ||||||
|  |         return "GT"; | ||||||
|  |     case FlowTest::NE: | ||||||
|  |         return "NE"; | ||||||
|  |     case FlowTest::GE: | ||||||
|  |         return "GE"; | ||||||
|  |     case FlowTest::NUM: | ||||||
|  |         return "NUM"; | ||||||
|  |     case FlowTest::NaN: | ||||||
|  |         return "NAN"; | ||||||
|  |     case FlowTest::LTU: | ||||||
|  |         return "LTU"; | ||||||
|  |     case FlowTest::EQU: | ||||||
|  |         return "EQU"; | ||||||
|  |     case FlowTest::LEU: | ||||||
|  |         return "LEU"; | ||||||
|  |     case FlowTest::GTU: | ||||||
|  |         return "GTU"; | ||||||
|  |     case FlowTest::NEU: | ||||||
|  |         return "NEU"; | ||||||
|  |     case FlowTest::GEU: | ||||||
|  |         return "GEU"; | ||||||
|  |     case FlowTest::T: | ||||||
|  |         return "T"; | ||||||
|  |     case FlowTest::OFF: | ||||||
|  |         return "OFF"; | ||||||
|  |     case FlowTest::LO: | ||||||
|  |         return "LO"; | ||||||
|  |     case FlowTest::SFF: | ||||||
|  |         return "SFF"; | ||||||
|  |     case FlowTest::LS: | ||||||
|  |         return "LS"; | ||||||
|  |     case FlowTest::HI: | ||||||
|  |         return "HI"; | ||||||
|  |     case FlowTest::SFT: | ||||||
|  |         return "SFT"; | ||||||
|  |     case FlowTest::HS: | ||||||
|  |         return "HS"; | ||||||
|  |     case FlowTest::OFT: | ||||||
|  |         return "OFT"; | ||||||
|  |     case FlowTest::CSM_TA: | ||||||
|  |         return "CSM_TA"; | ||||||
|  |     case FlowTest::CSM_TR: | ||||||
|  |         return "CSM_TR"; | ||||||
|  |     case FlowTest::CSM_MX: | ||||||
|  |         return "CSM_MX"; | ||||||
|  |     case FlowTest::FCSM_TA: | ||||||
|  |         return "FCSM_TA"; | ||||||
|  |     case FlowTest::FCSM_TR: | ||||||
|  |         return "FCSM_TR"; | ||||||
|  |     case FlowTest::FCSM_MX: | ||||||
|  |         return "FCSM_MX"; | ||||||
|  |     case FlowTest::RLE: | ||||||
|  |         return "RLE"; | ||||||
|  |     case FlowTest::RGT: | ||||||
|  |         return "RGT"; | ||||||
|  |     } | ||||||
|  |     return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										61
									
								
								src/shader_recompiler/frontend/ir/flow_test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/shader_recompiler/frontend/ir/flow_test.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class FlowTest { | ||||||
|  |     F, | ||||||
|  |     LT, | ||||||
|  |     EQ, | ||||||
|  |     LE, | ||||||
|  |     GT, | ||||||
|  |     NE, | ||||||
|  |     GE, | ||||||
|  |     NUM, | ||||||
|  |     NaN, | ||||||
|  |     LTU, | ||||||
|  |     EQU, | ||||||
|  |     LEU, | ||||||
|  |     GTU, | ||||||
|  |     NEU, | ||||||
|  |     GEU, | ||||||
|  |     T, | ||||||
|  |     OFF, | ||||||
|  |     LO, | ||||||
|  |     SFF, | ||||||
|  |     LS, | ||||||
|  |     HI, | ||||||
|  |     SFT, | ||||||
|  |     HS, | ||||||
|  |     OFT, | ||||||
|  |     CSM_TA, | ||||||
|  |     CSM_TR, | ||||||
|  |     CSM_MX, | ||||||
|  |     FCSM_TA, | ||||||
|  |     FCSM_TR, | ||||||
|  |     FCSM_MX, | ||||||
|  |     RLE, | ||||||
|  |     RGT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string NameOf(FlowTest flow_test); | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::FlowTest> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) { | ||||||
|  |         return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										533
									
								
								src/shader_recompiler/frontend/ir/ir_emitter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								src/shader_recompiler/frontend/ir/ir_emitter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,533 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_cast.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/value.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | [[noreturn]] static void ThrowInvalidType(Type type) { | ||||||
|  |     throw InvalidArgument("Invalid type {}", type); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::Imm1(bool value) const { | ||||||
|  |     return U1{Value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U8 IREmitter::Imm8(u8 value) const { | ||||||
|  |     return U8{Value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16 IREmitter::Imm16(u16 value) const { | ||||||
|  |     return U16{Value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::Imm32(u32 value) const { | ||||||
|  |     return U32{Value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::Imm32(s32 value) const { | ||||||
|  |     return U32{Value{static_cast<u32>(value)}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::Imm32(f32 value) const { | ||||||
|  |     return U32{Value{Common::BitCast<u32>(value)}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U64 IREmitter::Imm64(u64 value) const { | ||||||
|  |     return U64{Value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U64 IREmitter::Imm64(f64 value) const { | ||||||
|  |     return U64{Value{Common::BitCast<u64>(value)}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::Branch(IR::Block* label) { | ||||||
|  |     Inst(Opcode::Branch, label); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label) { | ||||||
|  |     Inst(Opcode::BranchConditional, cond, true_label, false_label); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::Exit() { | ||||||
|  |     Inst(Opcode::Exit); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::Return() { | ||||||
|  |     Inst(Opcode::Return); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::Unreachable() { | ||||||
|  |     Inst(Opcode::Unreachable); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::GetReg(IR::Reg reg) { | ||||||
|  |     return Inst<U32>(Opcode::GetRegister, reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetReg(IR::Reg reg, const U32& value) { | ||||||
|  |     Inst(Opcode::SetRegister, reg, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetPred(IR::Pred pred, bool is_negated) { | ||||||
|  |     const U1 value{Inst<U1>(Opcode::GetPred, pred)}; | ||||||
|  |     if (is_negated) { | ||||||
|  |         return Inst<U1>(Opcode::LogicalNot, value); | ||||||
|  |     } else { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetPred(IR::Pred pred, const U1& value) { | ||||||
|  |     Inst(Opcode::SetPred, pred, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) { | ||||||
|  |     return Inst<U32>(Opcode::GetCbuf, binding, byte_offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetZFlag() { | ||||||
|  |     return Inst<U1>(Opcode::GetZFlag); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetSFlag() { | ||||||
|  |     return Inst<U1>(Opcode::GetSFlag); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetCFlag() { | ||||||
|  |     return Inst<U1>(Opcode::GetCFlag); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetOFlag() { | ||||||
|  |     return Inst<U1>(Opcode::GetOFlag); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetZFlag(const U1& value) { | ||||||
|  |     Inst(Opcode::SetZFlag, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetSFlag(const U1& value) { | ||||||
|  |     Inst(Opcode::SetSFlag, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetCFlag(const U1& value) { | ||||||
|  |     Inst(Opcode::SetCFlag, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetOFlag(const U1& value) { | ||||||
|  |     Inst(Opcode::SetOFlag, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::GetAttribute(IR::Attribute attribute) { | ||||||
|  |     return Inst<U32>(Opcode::GetAttribute, attribute); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::SetAttribute(IR::Attribute attribute, const U32& value) { | ||||||
|  |     Inst(Opcode::SetAttribute, attribute, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobalU8(const U64& address, const U32& value) { | ||||||
|  |     Inst(Opcode::WriteGlobalU8, address, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobalS8(const U64& address, const U32& value) { | ||||||
|  |     Inst(Opcode::WriteGlobalS8, address, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobalU16(const U64& address, const U32& value) { | ||||||
|  |     Inst(Opcode::WriteGlobalU16, address, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobalS16(const U64& address, const U32& value) { | ||||||
|  |     Inst(Opcode::WriteGlobalS16, address, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobal32(const U64& address, const U32& value) { | ||||||
|  |     Inst(Opcode::WriteGlobal32, address, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobal64(const U64& address, const IR::Value& vector) { | ||||||
|  |     Inst(Opcode::WriteGlobal64, address, vector); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void IREmitter::WriteGlobal128(const U64& address, const IR::Value& vector) { | ||||||
|  |     Inst(Opcode::WriteGlobal128, address, vector); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetZeroFromOp(const Value& op) { | ||||||
|  |     return Inst<U1>(Opcode::GetZeroFromOp, op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetSignFromOp(const Value& op) { | ||||||
|  |     return Inst<U1>(Opcode::GetSignFromOp, op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetCarryFromOp(const Value& op) { | ||||||
|  |     return Inst<U1>(Opcode::GetCarryFromOp, op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::GetOverflowFromOp(const Value& op) { | ||||||
|  |     return Inst<U1>(Opcode::GetOverflowFromOp, op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPAdd(const U16U32U64& a, const U16U32U64& b) { | ||||||
|  |     if (a.Type() != a.Type()) { | ||||||
|  |         throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||||||
|  |     } | ||||||
|  |     switch (a.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPAdd16, a, b); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPAdd32, a, b); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPAdd64, a, b); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(a.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2) { | ||||||
|  |     if (e1.Type() != e2.Type()) { | ||||||
|  |         throw InvalidArgument("Incompatible types {} {}", e1.Type(), e2.Type()); | ||||||
|  |     } | ||||||
|  |     return Inst(Opcode::CompositeConstruct2, e1, e2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3) { | ||||||
|  |     if (e1.Type() != e2.Type() || e1.Type() != e3.Type()) { | ||||||
|  |         throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type()); | ||||||
|  |     } | ||||||
|  |     return Inst(Opcode::CompositeConstruct3, e1, e2, e3); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3, | ||||||
|  |                                     const UAny& e4) { | ||||||
|  |     if (e1.Type() != e2.Type() || e1.Type() != e3.Type() || e1.Type() != e4.Type()) { | ||||||
|  |         throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type(), | ||||||
|  |                               e4.Type()); | ||||||
|  |     } | ||||||
|  |     return Inst(Opcode::CompositeConstruct4, e1, e2, e3, e4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | UAny IREmitter::CompositeExtract(const Value& vector, size_t element) { | ||||||
|  |     if (element >= 4) { | ||||||
|  |         throw InvalidArgument("Out of bounds element {}", element); | ||||||
|  |     } | ||||||
|  |     return Inst<UAny>(Opcode::CompositeExtract, vector, Imm32(static_cast<u32>(element))); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U64 IREmitter::PackUint2x32(const Value& vector) { | ||||||
|  |     return Inst<U64>(Opcode::PackUint2x32, vector); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::UnpackUint2x32(const U64& value) { | ||||||
|  |     return Inst<Value>(Opcode::UnpackUint2x32, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::PackFloat2x16(const Value& vector) { | ||||||
|  |     return Inst<U32>(Opcode::PackFloat2x16, vector); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::UnpackFloat2x16(const U32& value) { | ||||||
|  |     return Inst<Value>(Opcode::UnpackFloat2x16, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U64 IREmitter::PackDouble2x32(const Value& vector) { | ||||||
|  |     return Inst<U64>(Opcode::PackDouble2x32, vector); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value IREmitter::UnpackDouble2x32(const U64& value) { | ||||||
|  |     return Inst<Value>(Opcode::UnpackDouble2x32, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPMul(const U16U32U64& a, const U16U32U64& b) { | ||||||
|  |     if (a.Type() != b.Type()) { | ||||||
|  |         throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||||||
|  |     } | ||||||
|  |     switch (a.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPMul16, a, b); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPMul32, a, b); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPMul64, a, b); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(a.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPAbs(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPAbs16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPAbs32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPAbs64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPNeg(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPNeg16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPNeg32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPNeg64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPAbsNeg(const U16U32U64& value, bool abs, bool neg) { | ||||||
|  |     U16U32U64 result{value}; | ||||||
|  |     if (abs) { | ||||||
|  |         result = FPAbs(value); | ||||||
|  |     } | ||||||
|  |     if (neg) { | ||||||
|  |         result = FPNeg(value); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::FPCosNotReduced(const U32& value) { | ||||||
|  |     return Inst<U32>(Opcode::FPCosNotReduced, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::FPExp2NotReduced(const U32& value) { | ||||||
|  |     return Inst<U32>(Opcode::FPExp2NotReduced, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::FPLog2(const U32& value) { | ||||||
|  |     return Inst<U32>(Opcode::FPLog2, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::FPRecip(const U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPRecip32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPRecip64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::FPRecipSqrt(const U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPRecipSqrt32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPRecipSqrt64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::FPSinNotReduced(const U32& value) { | ||||||
|  |     return Inst<U32>(Opcode::FPSinNotReduced, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32 IREmitter::FPSqrt(const U32& value) { | ||||||
|  |     return Inst<U32>(Opcode::FPSqrt, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPSaturate(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPSaturate16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPSaturate32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPSaturate64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPRoundEven(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPRoundEven16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPRoundEven32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPRoundEven64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPFloor(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPFloor16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPFloor32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPFloor64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPCeil(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPCeil16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPCeil32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPCeil64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U16U32U64 IREmitter::FPTrunc(const U16U32U64& value) { | ||||||
|  |     switch (value.Type()) { | ||||||
|  |     case Type::U16: | ||||||
|  |         return Inst<U16>(Opcode::FPTrunc16, value); | ||||||
|  |     case Type::U32: | ||||||
|  |         return Inst<U32>(Opcode::FPTrunc32, value); | ||||||
|  |     case Type::U64: | ||||||
|  |         return Inst<U64>(Opcode::FPTrunc64, value); | ||||||
|  |     default: | ||||||
|  |         ThrowInvalidType(value.Type()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::LogicalOr(const U1& a, const U1& b) { | ||||||
|  |     return Inst<U1>(Opcode::LogicalOr, a, b); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::LogicalAnd(const U1& a, const U1& b) { | ||||||
|  |     return Inst<U1>(Opcode::LogicalAnd, a, b); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U1 IREmitter::LogicalNot(const U1& value) { | ||||||
|  |     return Inst<U1>(Opcode::LogicalNot, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::ConvertFToS(size_t bitsize, const U16U32U64& value) { | ||||||
|  |     switch (bitsize) { | ||||||
|  |     case 16: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS16F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS16F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS16F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     case 32: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS32F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS32F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U32>(Opcode::ConvertS32F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     case 64: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U64>(Opcode::ConvertS64F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U64>(Opcode::ConvertS64F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U64>(Opcode::ConvertS64F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     default: | ||||||
|  |         throw InvalidArgument("Invalid destination bitsize {}", bitsize); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::ConvertFToU(size_t bitsize, const U16U32U64& value) { | ||||||
|  |     switch (bitsize) { | ||||||
|  |     case 16: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU16F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU16F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU16F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     case 32: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU32F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU32F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU32F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     case 64: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U16: | ||||||
|  |             return Inst<U64>(Opcode::ConvertU64F16, value); | ||||||
|  |         case Type::U32: | ||||||
|  |             return Inst<U64>(Opcode::ConvertU64F32, value); | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U64>(Opcode::ConvertU64F64, value); | ||||||
|  |         default: | ||||||
|  |             ThrowInvalidType(value.Type()); | ||||||
|  |         } | ||||||
|  |     default: | ||||||
|  |         throw InvalidArgument("Invalid destination bitsize {}", bitsize); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value) { | ||||||
|  |     if (is_signed) { | ||||||
|  |         return ConvertFToS(bitsize, value); | ||||||
|  |     } else { | ||||||
|  |         return ConvertFToU(bitsize, value); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | U32U64 IREmitter::ConvertU(size_t bitsize, const U32U64& value) { | ||||||
|  |     switch (bitsize) { | ||||||
|  |     case 32: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U32: | ||||||
|  |             // Nothing to do | ||||||
|  |             return value; | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U32>(Opcode::ConvertU32U64, value); | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 64: | ||||||
|  |         switch (value.Type()) { | ||||||
|  |         case Type::U32: | ||||||
|  |             // Nothing to do | ||||||
|  |             return value; | ||||||
|  |         case Type::U64: | ||||||
|  |             return Inst<U64>(Opcode::ConvertU64U32, value); | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     throw NotImplementedException("Conversion from {} to {} bits", value.Type(), bitsize); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										123
									
								
								src/shader_recompiler/frontend/ir/ir_emitter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/shader_recompiler/frontend/ir/ir_emitter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/attribute.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/value.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | class IREmitter { | ||||||
|  | public: | ||||||
|  |     explicit IREmitter(Block& block_) : block{block_}, insertion_point{block.end()} {} | ||||||
|  |  | ||||||
|  |     Block& block; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U1 Imm1(bool value) const; | ||||||
|  |     [[nodiscard]] U8 Imm8(u8 value) const; | ||||||
|  |     [[nodiscard]] U16 Imm16(u16 value) const; | ||||||
|  |     [[nodiscard]] U32 Imm32(u32 value) const; | ||||||
|  |     [[nodiscard]] U32 Imm32(s32 value) const; | ||||||
|  |     [[nodiscard]] U32 Imm32(f32 value) const; | ||||||
|  |     [[nodiscard]] U64 Imm64(u64 value) const; | ||||||
|  |     [[nodiscard]] U64 Imm64(f64 value) const; | ||||||
|  |  | ||||||
|  |     void Branch(IR::Block* label); | ||||||
|  |     void BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label); | ||||||
|  |     void Exit(); | ||||||
|  |     void Return(); | ||||||
|  |     void Unreachable(); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32 GetReg(IR::Reg reg); | ||||||
|  |     void SetReg(IR::Reg reg, const U32& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false); | ||||||
|  |     void SetPred(IR::Pred pred, const U1& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U1 GetZFlag(); | ||||||
|  |     [[nodiscard]] U1 GetSFlag(); | ||||||
|  |     [[nodiscard]] U1 GetCFlag(); | ||||||
|  |     [[nodiscard]] U1 GetOFlag(); | ||||||
|  |  | ||||||
|  |     void SetZFlag(const U1& value); | ||||||
|  |     void SetSFlag(const U1& value); | ||||||
|  |     void SetCFlag(const U1& value); | ||||||
|  |     void SetOFlag(const U1& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32 GetAttribute(IR::Attribute attribute); | ||||||
|  |     void SetAttribute(IR::Attribute attribute, const U32& value); | ||||||
|  |  | ||||||
|  |     void WriteGlobalU8(const U64& address, const U32& value); | ||||||
|  |     void WriteGlobalS8(const U64& address, const U32& value); | ||||||
|  |     void WriteGlobalU16(const U64& address, const U32& value); | ||||||
|  |     void WriteGlobalS16(const U64& address, const U32& value); | ||||||
|  |     void WriteGlobal32(const U64& address, const U32& value); | ||||||
|  |     void WriteGlobal64(const U64& address, const IR::Value& vector); | ||||||
|  |     void WriteGlobal128(const U64& address, const IR::Value& vector); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U1 GetZeroFromOp(const Value& op); | ||||||
|  |     [[nodiscard]] U1 GetSignFromOp(const Value& op); | ||||||
|  |     [[nodiscard]] U1 GetCarryFromOp(const Value& op); | ||||||
|  |     [[nodiscard]] U1 GetOverflowFromOp(const Value& op); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2); | ||||||
|  |     [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3); | ||||||
|  |     [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3, | ||||||
|  |                                            const UAny& e4); | ||||||
|  |     [[nodiscard]] UAny CompositeExtract(const Value& vector, size_t element); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U64 PackUint2x32(const Value& vector); | ||||||
|  |     [[nodiscard]] Value UnpackUint2x32(const U64& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32 PackFloat2x16(const Value& vector); | ||||||
|  |     [[nodiscard]] Value UnpackFloat2x16(const U32& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U64 PackDouble2x32(const Value& vector); | ||||||
|  |     [[nodiscard]] Value UnpackDouble2x32(const U64& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U16U32U64 FPAdd(const U16U32U64& a, const U16U32U64& b); | ||||||
|  |     [[nodiscard]] U16U32U64 FPMul(const U16U32U64& a, const U16U32U64& b); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U16U32U64 FPAbs(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPNeg(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPAbsNeg(const U16U32U64& value, bool abs, bool neg); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32 FPCosNotReduced(const U32& value); | ||||||
|  |     [[nodiscard]] U32 FPExp2NotReduced(const U32& value); | ||||||
|  |     [[nodiscard]] U32 FPLog2(const U32& value); | ||||||
|  |     [[nodiscard]] U32U64 FPRecip(const U32U64& value); | ||||||
|  |     [[nodiscard]] U32U64 FPRecipSqrt(const U32U64& value); | ||||||
|  |     [[nodiscard]] U32 FPSinNotReduced(const U32& value); | ||||||
|  |     [[nodiscard]] U32 FPSqrt(const U32& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPSaturate(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPRoundEven(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPFloor(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPCeil(const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U16U32U64 FPTrunc(const U16U32U64& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U1 LogicalOr(const U1& a, const U1& b); | ||||||
|  |     [[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b); | ||||||
|  |     [[nodiscard]] U1 LogicalNot(const U1& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const U16U32U64& value); | ||||||
|  |     [[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] U32U64 ConvertU(size_t bitsize, const U32U64& value); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     IR::Block::iterator insertion_point; | ||||||
|  |  | ||||||
|  |     template <typename T = Value, typename... Args> | ||||||
|  |     T Inst(Opcode op, Args... args) { | ||||||
|  |         auto it{block.PrependNewInst(insertion_point, op, {Value{args}...})}; | ||||||
|  |         return T{Value{&*it}}; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										189
									
								
								src/shader_recompiler/frontend/ir/microinstruction.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/shader_recompiler/frontend/ir/microinstruction.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/type.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | static void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) { | ||||||
|  |     if (inst && inst->Opcode() != opcode) { | ||||||
|  |         throw LogicError("Invalid pseudo-instruction"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) { | ||||||
|  |     if (dest_inst) { | ||||||
|  |         throw LogicError("Only one of each type of pseudo-op allowed"); | ||||||
|  |     } | ||||||
|  |     dest_inst = pseudo_inst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) { | ||||||
|  |     if (inst->Opcode() != expected_opcode) { | ||||||
|  |         throw LogicError("Undoing use of invalid pseudo-op"); | ||||||
|  |     } | ||||||
|  |     inst = nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Inst::MayHaveSideEffects() const noexcept { | ||||||
|  |     switch (op) { | ||||||
|  |     case Opcode::SetAttribute: | ||||||
|  |     case Opcode::SetAttributeIndexed: | ||||||
|  |     case Opcode::WriteGlobalU8: | ||||||
|  |     case Opcode::WriteGlobalS8: | ||||||
|  |     case Opcode::WriteGlobalU16: | ||||||
|  |     case Opcode::WriteGlobalS16: | ||||||
|  |     case Opcode::WriteGlobal32: | ||||||
|  |     case Opcode::WriteGlobal64: | ||||||
|  |     case Opcode::WriteGlobal128: | ||||||
|  |         return true; | ||||||
|  |     default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Inst::IsPseudoInstruction() const noexcept { | ||||||
|  |     switch (op) { | ||||||
|  |     case Opcode::GetZeroFromOp: | ||||||
|  |     case Opcode::GetSignFromOp: | ||||||
|  |     case Opcode::GetCarryFromOp: | ||||||
|  |     case Opcode::GetOverflowFromOp: | ||||||
|  |     case Opcode::GetZSCOFromOp: | ||||||
|  |         return true; | ||||||
|  |     default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Inst::HasAssociatedPseudoOperation() const noexcept { | ||||||
|  |     return zero_inst || sign_inst || carry_inst || overflow_inst || zsco_inst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) { | ||||||
|  |     // This is faster than doing a search through the block. | ||||||
|  |     switch (opcode) { | ||||||
|  |     case Opcode::GetZeroFromOp: | ||||||
|  |         CheckPseudoInstruction(zero_inst, Opcode::GetZeroFromOp); | ||||||
|  |         return zero_inst; | ||||||
|  |     case Opcode::GetSignFromOp: | ||||||
|  |         CheckPseudoInstruction(sign_inst, Opcode::GetSignFromOp); | ||||||
|  |         return sign_inst; | ||||||
|  |     case Opcode::GetCarryFromOp: | ||||||
|  |         CheckPseudoInstruction(carry_inst, Opcode::GetCarryFromOp); | ||||||
|  |         return carry_inst; | ||||||
|  |     case Opcode::GetOverflowFromOp: | ||||||
|  |         CheckPseudoInstruction(overflow_inst, Opcode::GetOverflowFromOp); | ||||||
|  |         return overflow_inst; | ||||||
|  |     case Opcode::GetZSCOFromOp: | ||||||
|  |         CheckPseudoInstruction(zsco_inst, Opcode::GetZSCOFromOp); | ||||||
|  |         return zsco_inst; | ||||||
|  |     default: | ||||||
|  |         throw InvalidArgument("{} is not a pseudo-instruction", opcode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | size_t Inst::NumArgs() const { | ||||||
|  |     return NumArgsOf(op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Type Inst::Type() const { | ||||||
|  |     return TypeOf(op); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Value Inst::Arg(size_t index) const { | ||||||
|  |     if (index >= NumArgsOf(op)) { | ||||||
|  |         throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | ||||||
|  |     } | ||||||
|  |     return args[index]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::SetArg(size_t index, Value value) { | ||||||
|  |     if (index >= NumArgsOf(op)) { | ||||||
|  |         throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | ||||||
|  |     } | ||||||
|  |     if (!args[index].IsImmediate()) { | ||||||
|  |         UndoUse(args[index]); | ||||||
|  |     } | ||||||
|  |     if (!value.IsImmediate()) { | ||||||
|  |         Use(value); | ||||||
|  |     } | ||||||
|  |     args[index] = value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::Invalidate() { | ||||||
|  |     ClearArgs(); | ||||||
|  |     op = Opcode::Void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::ClearArgs() { | ||||||
|  |     for (auto& value : args) { | ||||||
|  |         if (!value.IsImmediate()) { | ||||||
|  |             UndoUse(value); | ||||||
|  |         } | ||||||
|  |         value = {}; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::ReplaceUsesWith(Value replacement) { | ||||||
|  |     Invalidate(); | ||||||
|  |  | ||||||
|  |     op = Opcode::Identity; | ||||||
|  |  | ||||||
|  |     if (!replacement.IsImmediate()) { | ||||||
|  |         Use(replacement); | ||||||
|  |     } | ||||||
|  |     args[0] = replacement; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::Use(const Value& value) { | ||||||
|  |     ++value.Inst()->use_count; | ||||||
|  |  | ||||||
|  |     switch (op) { | ||||||
|  |     case Opcode::GetZeroFromOp: | ||||||
|  |         SetPseudoInstruction(value.Inst()->zero_inst, this); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetSignFromOp: | ||||||
|  |         SetPseudoInstruction(value.Inst()->sign_inst, this); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetCarryFromOp: | ||||||
|  |         SetPseudoInstruction(value.Inst()->carry_inst, this); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetOverflowFromOp: | ||||||
|  |         SetPseudoInstruction(value.Inst()->overflow_inst, this); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetZSCOFromOp: | ||||||
|  |         SetPseudoInstruction(value.Inst()->zsco_inst, this); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Inst::UndoUse(const Value& value) { | ||||||
|  |     --value.Inst()->use_count; | ||||||
|  |  | ||||||
|  |     switch (op) { | ||||||
|  |     case Opcode::GetZeroFromOp: | ||||||
|  |         RemovePseudoInstruction(value.Inst()->zero_inst, Opcode::GetZeroFromOp); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetSignFromOp: | ||||||
|  |         RemovePseudoInstruction(value.Inst()->sign_inst, Opcode::GetSignFromOp); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetCarryFromOp: | ||||||
|  |         RemovePseudoInstruction(value.Inst()->carry_inst, Opcode::GetCarryFromOp); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetOverflowFromOp: | ||||||
|  |         RemovePseudoInstruction(value.Inst()->overflow_inst, Opcode::GetOverflowFromOp); | ||||||
|  |         break; | ||||||
|  |     case Opcode::GetZSCOFromOp: | ||||||
|  |         RemovePseudoInstruction(value.Inst()->zsco_inst, Opcode::GetZSCOFromOp); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										82
									
								
								src/shader_recompiler/frontend/ir/microinstruction.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/shader_recompiler/frontend/ir/microinstruction.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
|  | #include <boost/intrusive/list.hpp> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/type.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/value.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | constexpr size_t MAX_ARG_COUNT = 4; | ||||||
|  |  | ||||||
|  | class Inst : public boost::intrusive::list_base_hook<> { | ||||||
|  | public: | ||||||
|  |     explicit Inst(Opcode op_) noexcept : op(op_) {} | ||||||
|  |  | ||||||
|  |     /// Get the number of uses this instruction has. | ||||||
|  |     [[nodiscard]] int UseCount() const noexcept { | ||||||
|  |         return use_count; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Determines whether this instruction has uses or not. | ||||||
|  |     [[nodiscard]] bool HasUses() const noexcept { | ||||||
|  |         return use_count > 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Get the opcode this microinstruction represents. | ||||||
|  |     [[nodiscard]] IR::Opcode Opcode() const noexcept { | ||||||
|  |         return op; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Determines whether or not this instruction may have side effects. | ||||||
|  |     [[nodiscard]] bool MayHaveSideEffects() const noexcept; | ||||||
|  |  | ||||||
|  |     /// Determines whether or not this instruction is a pseudo-instruction. | ||||||
|  |     /// Pseudo-instructions depend on their parent instructions for their semantics. | ||||||
|  |     [[nodiscard]] bool IsPseudoInstruction() const noexcept; | ||||||
|  |  | ||||||
|  |     /// Determines if there is a pseudo-operation associated with this instruction. | ||||||
|  |     [[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept; | ||||||
|  |     /// Gets a pseudo-operation associated with this instruction | ||||||
|  |     [[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode); | ||||||
|  |  | ||||||
|  |     /// Get the number of arguments this instruction has. | ||||||
|  |     [[nodiscard]] size_t NumArgs() const; | ||||||
|  |  | ||||||
|  |     /// Get the type this instruction returns. | ||||||
|  |     [[nodiscard]] IR::Type Type() const; | ||||||
|  |  | ||||||
|  |     /// Get the value of a given argument index. | ||||||
|  |     [[nodiscard]] Value Arg(size_t index) const; | ||||||
|  |     /// Set the value of a given argument index. | ||||||
|  |     void SetArg(size_t index, Value value); | ||||||
|  |  | ||||||
|  |     void Invalidate(); | ||||||
|  |     void ClearArgs(); | ||||||
|  |  | ||||||
|  |     void ReplaceUsesWith(Value replacement); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     void Use(const Value& value); | ||||||
|  |     void UndoUse(const Value& value); | ||||||
|  |  | ||||||
|  |     IR::Opcode op{}; | ||||||
|  |     int use_count{}; | ||||||
|  |     std::array<Value, MAX_ARG_COUNT> args{}; | ||||||
|  |     Inst* zero_inst{}; | ||||||
|  |     Inst* sign_inst{}; | ||||||
|  |     Inst* carry_inst{}; | ||||||
|  |     Inst* overflow_inst{}; | ||||||
|  |     Inst* zsco_inst{}; | ||||||
|  |     u64 flags{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										67
									
								
								src/shader_recompiler/frontend/ir/opcode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/shader_recompiler/frontend/ir/opcode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <string_view> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/opcode.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  | namespace { | ||||||
|  | struct OpcodeMeta { | ||||||
|  |     std::string_view name; | ||||||
|  |     Type type; | ||||||
|  |     std::array<Type, 4> arg_types; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | using enum Type; | ||||||
|  |  | ||||||
|  | constexpr std::array META_TABLE{ | ||||||
|  | #define OPCODE(name_token, type_token, ...)                                                        \ | ||||||
|  |     OpcodeMeta{                                                                                    \ | ||||||
|  |         .name{#name_token},                                                                        \ | ||||||
|  |         .type{type_token},                                                                         \ | ||||||
|  |         .arg_types{__VA_ARGS__},                                                                   \ | ||||||
|  |     }, | ||||||
|  | #include "opcode.inc" | ||||||
|  | #undef OPCODE | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void ValidateOpcode(Opcode op) { | ||||||
|  |     const size_t raw{static_cast<size_t>(op)}; | ||||||
|  |     if (raw >= META_TABLE.size()) { | ||||||
|  |         throw InvalidArgument("Invalid opcode with raw value {}", raw); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | Type TypeOf(Opcode op) { | ||||||
|  |     ValidateOpcode(op); | ||||||
|  |     return META_TABLE[static_cast<size_t>(op)].type; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | size_t NumArgsOf(Opcode op) { | ||||||
|  |     ValidateOpcode(op); | ||||||
|  |     const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types}; | ||||||
|  |     const auto distance{std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void))}; | ||||||
|  |     return static_cast<size_t>(distance); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Type ArgTypeOf(Opcode op, size_t arg_index) { | ||||||
|  |     ValidateOpcode(op); | ||||||
|  |     const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types}; | ||||||
|  |     if (arg_index >= arg_types.size() || arg_types[arg_index] == Type::Void) { | ||||||
|  |         throw InvalidArgument("Out of bounds argument"); | ||||||
|  |     } | ||||||
|  |     return arg_types[arg_index]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string_view NameOf(Opcode op) { | ||||||
|  |     ValidateOpcode(op); | ||||||
|  |     return META_TABLE[static_cast<size_t>(op)].name; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										44
									
								
								src/shader_recompiler/frontend/ir/opcode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/shader_recompiler/frontend/ir/opcode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string_view> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/type.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class Opcode { | ||||||
|  | #define OPCODE(name, ...) name, | ||||||
|  | #include "opcode.inc" | ||||||
|  | #undef OPCODE | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /// Get return type of an opcode | ||||||
|  | [[nodiscard]] Type TypeOf(Opcode op); | ||||||
|  |  | ||||||
|  | /// Get the number of arguments an opcode accepts | ||||||
|  | [[nodiscard]] size_t NumArgsOf(Opcode op); | ||||||
|  |  | ||||||
|  | /// Get the required type of an argument of an opcode | ||||||
|  | [[nodiscard]] Type ArgTypeOf(Opcode op, size_t arg_index); | ||||||
|  |  | ||||||
|  | /// Get the name of an opcode | ||||||
|  | [[nodiscard]] std::string_view NameOf(Opcode op); | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Opcode> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Opcode& op, FormatContext& ctx) { | ||||||
|  |         return format_to(ctx.out(), "{}", Shader::IR::NameOf(op)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										142
									
								
								src/shader_recompiler/frontend/ir/opcode.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/shader_recompiler/frontend/ir/opcode.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | //     opcode name,                                         return type,    arg1 type,      arg2 type,      arg3 type,      arg4 type,      ... | ||||||
|  | OPCODE(Void,                                                Void,                                                                           ) | ||||||
|  | OPCODE(Identity,                                            Opaque,         Opaque,                                                         ) | ||||||
|  |  | ||||||
|  | // Control flow | ||||||
|  | OPCODE(Branch,                                              Void,           Label,                                                          ) | ||||||
|  | OPCODE(BranchConditional,                                   Void,           U1,             Label,          Label,                          ) | ||||||
|  | OPCODE(Exit,                                                Void,                                                                           ) | ||||||
|  | OPCODE(Return,                                              Void,                                                                           ) | ||||||
|  | OPCODE(Unreachable,                                         Void,                                                                           ) | ||||||
|  |  | ||||||
|  | // Context getters/setters | ||||||
|  | OPCODE(GetRegister,                                         U32,            Reg,                                                            ) | ||||||
|  | OPCODE(SetRegister,                                         Void,           Reg,            U32,                                            ) | ||||||
|  | OPCODE(GetPred,                                             U1,             Pred,                                                           ) | ||||||
|  | OPCODE(SetPred,                                             Void,           Pred,           U1,                                             ) | ||||||
|  | OPCODE(GetCbuf,                                             U32,            U32,            U32,                                            ) | ||||||
|  | OPCODE(GetAttribute,                                        U32,            Attribute,                                                      ) | ||||||
|  | OPCODE(SetAttribute,                                        U32,            Attribute,                                                      ) | ||||||
|  | OPCODE(GetAttributeIndexed,                                 U32,            U32,                                                            ) | ||||||
|  | OPCODE(SetAttributeIndexed,                                 U32,            U32,                                                            ) | ||||||
|  | OPCODE(GetZSCORaw,                                          U32,                                                                            ) | ||||||
|  | OPCODE(SetZSCORaw,                                          Void,           U32,                                                            ) | ||||||
|  | OPCODE(SetZSCO,                                             Void,           ZSCO,                                                           ) | ||||||
|  | OPCODE(GetZFlag,                                            U1,             Void,                                                           ) | ||||||
|  | OPCODE(GetSFlag,                                            U1,             Void,                                                           ) | ||||||
|  | OPCODE(GetCFlag,                                            U1,             Void,                                                           ) | ||||||
|  | OPCODE(GetOFlag,                                            U1,             Void,                                                           ) | ||||||
|  | OPCODE(SetZFlag,                                            Void,           U1,                                                             ) | ||||||
|  | OPCODE(SetSFlag,                                            Void,           U1,                                                             ) | ||||||
|  | OPCODE(SetCFlag,                                            Void,           U1,                                                             ) | ||||||
|  | OPCODE(SetOFlag,                                            Void,           U1,                                                             ) | ||||||
|  |  | ||||||
|  | // Memory operations | ||||||
|  | OPCODE(WriteGlobalU8,                                       Void,           U64,            U32,                                            ) | ||||||
|  | OPCODE(WriteGlobalS8,                                       Void,           U64,            U32,                                            ) | ||||||
|  | OPCODE(WriteGlobalU16,                                      Void,           U64,            U32,                                            ) | ||||||
|  | OPCODE(WriteGlobalS16,                                      Void,           U64,            U32,                                            ) | ||||||
|  | OPCODE(WriteGlobal32,                                       Void,           U64,            U32,                                            ) | ||||||
|  | OPCODE(WriteGlobal64,                                       Void,           U64,            Opaque,                                         ) | ||||||
|  | OPCODE(WriteGlobal128,                                      Void,           U64,            Opaque,                                         ) | ||||||
|  |  | ||||||
|  | // Vector utility | ||||||
|  | OPCODE(CompositeConstruct2,                                 Opaque,         Opaque,         Opaque,                                         ) | ||||||
|  | OPCODE(CompositeConstruct3,                                 Opaque,         Opaque,         Opaque,         Opaque,                         ) | ||||||
|  | OPCODE(CompositeConstruct4,                                 Opaque,         Opaque,         Opaque,         Opaque,         Opaque,         ) | ||||||
|  | OPCODE(CompositeExtract,                                    Opaque,         Opaque,         U32,                                            ) | ||||||
|  |  | ||||||
|  | // Bitwise conversions | ||||||
|  | OPCODE(PackUint2x32,                                        U64,            Opaque,                                                         ) | ||||||
|  | OPCODE(UnpackUint2x32,                                      Opaque,         U64,                                                            ) | ||||||
|  | OPCODE(PackFloat2x16,                                       U32,            Opaque,                                                         ) | ||||||
|  | OPCODE(UnpackFloat2x16,                                     Opaque,         U32,                                                            ) | ||||||
|  | OPCODE(PackDouble2x32,                                      U64,            Opaque,                                                         ) | ||||||
|  | OPCODE(UnpackDouble2x32,                                    Opaque,         U64,                                                            ) | ||||||
|  |  | ||||||
|  | // Pseudo-operation, handled specially at final emit | ||||||
|  | OPCODE(GetZeroFromOp,                                       U1,             Opaque,                                                         ) | ||||||
|  | OPCODE(GetSignFromOp,                                       U1,             Opaque,                                                         ) | ||||||
|  | OPCODE(GetCarryFromOp,                                      U1,             Opaque,                                                         ) | ||||||
|  | OPCODE(GetOverflowFromOp,                                   U1,             Opaque,                                                         ) | ||||||
|  | OPCODE(GetZSCOFromOp,                                       ZSCO,           Opaque,                                                         ) | ||||||
|  |  | ||||||
|  | // Floating-point operations | ||||||
|  | OPCODE(FPAbs16,                                             U16,            U16                                                             ) | ||||||
|  | OPCODE(FPAbs32,                                             U32,            U32                                                             ) | ||||||
|  | OPCODE(FPAbs64,                                             U64,            U64                                                             ) | ||||||
|  | OPCODE(FPAdd16,                                             U16,            U16,            U16                                             ) | ||||||
|  | OPCODE(FPAdd32,                                             U32,            U32,            U32                                             ) | ||||||
|  | OPCODE(FPAdd64,                                             U64,            U64,            U64                                             ) | ||||||
|  | OPCODE(FPFma16,                                             U16,            U16,            U16                                             ) | ||||||
|  | OPCODE(FPFma32,                                             U32,            U32,            U32                                             ) | ||||||
|  | OPCODE(FPFma64,                                             U64,            U64,            U64                                             ) | ||||||
|  | OPCODE(FPMax32,                                             U32,            U32,            U32                                             ) | ||||||
|  | OPCODE(FPMax64,                                             U64,            U64,            U64                                             ) | ||||||
|  | OPCODE(FPMin32,                                             U32,            U32,            U32                                             ) | ||||||
|  | OPCODE(FPMin64,                                             U64,            U64,            U64                                             ) | ||||||
|  | OPCODE(FPMul16,                                             U16,            U16,            U16                                             ) | ||||||
|  | OPCODE(FPMul32,                                             U32,            U32,            U32                                             ) | ||||||
|  | OPCODE(FPMul64,                                             U64,            U64,            U64                                             ) | ||||||
|  | OPCODE(FPNeg16,                                             U16,            U16                                                             ) | ||||||
|  | OPCODE(FPNeg32,                                             U32,            U32                                                             ) | ||||||
|  | OPCODE(FPNeg64,                                             U64,            U64                                                             ) | ||||||
|  | OPCODE(FPRecip32,                                           U32,            U32                                                             ) | ||||||
|  | OPCODE(FPRecip64,                                           U64,            U64                                                             ) | ||||||
|  | OPCODE(FPRecipSqrt32,                                       U32,            U32                                                             ) | ||||||
|  | OPCODE(FPRecipSqrt64,                                       U64,            U64                                                             ) | ||||||
|  | OPCODE(FPSqrt,                                              U32,            U32                                                             ) | ||||||
|  | OPCODE(FPSin,                                               U32,            U32                                                             ) | ||||||
|  | OPCODE(FPSinNotReduced,                                     U32,            U32                                                             ) | ||||||
|  | OPCODE(FPExp2,                                              U32,            U32                                                             ) | ||||||
|  | OPCODE(FPExp2NotReduced,                                    U32,            U32                                                             ) | ||||||
|  | OPCODE(FPCos,                                               U32,            U32                                                             ) | ||||||
|  | OPCODE(FPCosNotReduced,                                     U32,            U32                                                             ) | ||||||
|  | OPCODE(FPLog2,                                              U32,            U32                                                             ) | ||||||
|  | OPCODE(FPSaturate16,                                        U16,            U16                                                             ) | ||||||
|  | OPCODE(FPSaturate32,                                        U32,            U32                                                             ) | ||||||
|  | OPCODE(FPSaturate64,                                        U64,            U64                                                             ) | ||||||
|  | OPCODE(FPRoundEven16,                                       U16,            U16                                                             ) | ||||||
|  | OPCODE(FPRoundEven32,                                       U32,            U32                                                             ) | ||||||
|  | OPCODE(FPRoundEven64,                                       U64,            U64                                                             ) | ||||||
|  | OPCODE(FPFloor16,                                           U16,            U16                                                             ) | ||||||
|  | OPCODE(FPFloor32,                                           U32,            U32                                                             ) | ||||||
|  | OPCODE(FPFloor64,                                           U64,            U64                                                             ) | ||||||
|  | OPCODE(FPCeil16,                                            U16,            U16                                                             ) | ||||||
|  | OPCODE(FPCeil32,                                            U32,            U32                                                             ) | ||||||
|  | OPCODE(FPCeil64,                                            U64,            U64                                                             ) | ||||||
|  | OPCODE(FPTrunc16,                                           U16,            U16                                                             ) | ||||||
|  | OPCODE(FPTrunc32,                                           U32,            U32                                                             ) | ||||||
|  | OPCODE(FPTrunc64,                                           U64,            U64                                                             ) | ||||||
|  |  | ||||||
|  | // Logical operations | ||||||
|  | OPCODE(LogicalOr,                                           U1,             U1,             U1,                                             ) | ||||||
|  | OPCODE(LogicalAnd,                                          U1,             U1,             U1,                                             ) | ||||||
|  | OPCODE(LogicalNot,                                          U1,             U1,                                                             ) | ||||||
|  |  | ||||||
|  | // Conversion operations | ||||||
|  | OPCODE(ConvertS16F16,                                       U32,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertS16F32,                                       U32,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertS16F64,                                       U32,            U64,                                                            ) | ||||||
|  | OPCODE(ConvertS32F16,                                       U32,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertS32F32,                                       U32,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertS32F64,                                       U32,            U64,                                                            ) | ||||||
|  | OPCODE(ConvertS64F16,                                       U64,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertS64F32,                                       U64,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertS64F64,                                       U64,            U64,                                                            ) | ||||||
|  | OPCODE(ConvertU16F16,                                       U32,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertU16F32,                                       U32,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertU16F64,                                       U32,            U64,                                                            ) | ||||||
|  | OPCODE(ConvertU32F16,                                       U32,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertU32F32,                                       U32,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertU32F64,                                       U32,            U64,                                                            ) | ||||||
|  | OPCODE(ConvertU64F16,                                       U64,            U16,                                                            ) | ||||||
|  | OPCODE(ConvertU64F32,                                       U64,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertU64F64,                                       U64,            U64,                                                            ) | ||||||
|  |  | ||||||
|  | OPCODE(ConvertU64U32,                                       U64,            U32,                                                            ) | ||||||
|  | OPCODE(ConvertU32U64,                                       U32,            U64,                                                            ) | ||||||
							
								
								
									
										28
									
								
								src/shader_recompiler/frontend/ir/pred.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/shader_recompiler/frontend/ir/pred.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Pred> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Pred& pred, FormatContext& ctx) { | ||||||
|  |         if (pred == Shader::IR::Pred::PT) { | ||||||
|  |             return fmt::format_to(ctx.out(), "PT"); | ||||||
|  |         } else { | ||||||
|  |             return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										314
									
								
								src/shader_recompiler/frontend/ir/reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								src/shader_recompiler/frontend/ir/reg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,314 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class Reg : u64 { | ||||||
|  |     R0, | ||||||
|  |     R1, | ||||||
|  |     R2, | ||||||
|  |     R3, | ||||||
|  |     R4, | ||||||
|  |     R5, | ||||||
|  |     R6, | ||||||
|  |     R7, | ||||||
|  |     R8, | ||||||
|  |     R9, | ||||||
|  |     R10, | ||||||
|  |     R11, | ||||||
|  |     R12, | ||||||
|  |     R13, | ||||||
|  |     R14, | ||||||
|  |     R15, | ||||||
|  |     R16, | ||||||
|  |     R17, | ||||||
|  |     R18, | ||||||
|  |     R19, | ||||||
|  |     R20, | ||||||
|  |     R21, | ||||||
|  |     R22, | ||||||
|  |     R23, | ||||||
|  |     R24, | ||||||
|  |     R25, | ||||||
|  |     R26, | ||||||
|  |     R27, | ||||||
|  |     R28, | ||||||
|  |     R29, | ||||||
|  |     R30, | ||||||
|  |     R31, | ||||||
|  |     R32, | ||||||
|  |     R33, | ||||||
|  |     R34, | ||||||
|  |     R35, | ||||||
|  |     R36, | ||||||
|  |     R37, | ||||||
|  |     R38, | ||||||
|  |     R39, | ||||||
|  |     R40, | ||||||
|  |     R41, | ||||||
|  |     R42, | ||||||
|  |     R43, | ||||||
|  |     R44, | ||||||
|  |     R45, | ||||||
|  |     R46, | ||||||
|  |     R47, | ||||||
|  |     R48, | ||||||
|  |     R49, | ||||||
|  |     R50, | ||||||
|  |     R51, | ||||||
|  |     R52, | ||||||
|  |     R53, | ||||||
|  |     R54, | ||||||
|  |     R55, | ||||||
|  |     R56, | ||||||
|  |     R57, | ||||||
|  |     R58, | ||||||
|  |     R59, | ||||||
|  |     R60, | ||||||
|  |     R61, | ||||||
|  |     R62, | ||||||
|  |     R63, | ||||||
|  |     R64, | ||||||
|  |     R65, | ||||||
|  |     R66, | ||||||
|  |     R67, | ||||||
|  |     R68, | ||||||
|  |     R69, | ||||||
|  |     R70, | ||||||
|  |     R71, | ||||||
|  |     R72, | ||||||
|  |     R73, | ||||||
|  |     R74, | ||||||
|  |     R75, | ||||||
|  |     R76, | ||||||
|  |     R77, | ||||||
|  |     R78, | ||||||
|  |     R79, | ||||||
|  |     R80, | ||||||
|  |     R81, | ||||||
|  |     R82, | ||||||
|  |     R83, | ||||||
|  |     R84, | ||||||
|  |     R85, | ||||||
|  |     R86, | ||||||
|  |     R87, | ||||||
|  |     R88, | ||||||
|  |     R89, | ||||||
|  |     R90, | ||||||
|  |     R91, | ||||||
|  |     R92, | ||||||
|  |     R93, | ||||||
|  |     R94, | ||||||
|  |     R95, | ||||||
|  |     R96, | ||||||
|  |     R97, | ||||||
|  |     R98, | ||||||
|  |     R99, | ||||||
|  |     R100, | ||||||
|  |     R101, | ||||||
|  |     R102, | ||||||
|  |     R103, | ||||||
|  |     R104, | ||||||
|  |     R105, | ||||||
|  |     R106, | ||||||
|  |     R107, | ||||||
|  |     R108, | ||||||
|  |     R109, | ||||||
|  |     R110, | ||||||
|  |     R111, | ||||||
|  |     R112, | ||||||
|  |     R113, | ||||||
|  |     R114, | ||||||
|  |     R115, | ||||||
|  |     R116, | ||||||
|  |     R117, | ||||||
|  |     R118, | ||||||
|  |     R119, | ||||||
|  |     R120, | ||||||
|  |     R121, | ||||||
|  |     R122, | ||||||
|  |     R123, | ||||||
|  |     R124, | ||||||
|  |     R125, | ||||||
|  |     R126, | ||||||
|  |     R127, | ||||||
|  |     R128, | ||||||
|  |     R129, | ||||||
|  |     R130, | ||||||
|  |     R131, | ||||||
|  |     R132, | ||||||
|  |     R133, | ||||||
|  |     R134, | ||||||
|  |     R135, | ||||||
|  |     R136, | ||||||
|  |     R137, | ||||||
|  |     R138, | ||||||
|  |     R139, | ||||||
|  |     R140, | ||||||
|  |     R141, | ||||||
|  |     R142, | ||||||
|  |     R143, | ||||||
|  |     R144, | ||||||
|  |     R145, | ||||||
|  |     R146, | ||||||
|  |     R147, | ||||||
|  |     R148, | ||||||
|  |     R149, | ||||||
|  |     R150, | ||||||
|  |     R151, | ||||||
|  |     R152, | ||||||
|  |     R153, | ||||||
|  |     R154, | ||||||
|  |     R155, | ||||||
|  |     R156, | ||||||
|  |     R157, | ||||||
|  |     R158, | ||||||
|  |     R159, | ||||||
|  |     R160, | ||||||
|  |     R161, | ||||||
|  |     R162, | ||||||
|  |     R163, | ||||||
|  |     R164, | ||||||
|  |     R165, | ||||||
|  |     R166, | ||||||
|  |     R167, | ||||||
|  |     R168, | ||||||
|  |     R169, | ||||||
|  |     R170, | ||||||
|  |     R171, | ||||||
|  |     R172, | ||||||
|  |     R173, | ||||||
|  |     R174, | ||||||
|  |     R175, | ||||||
|  |     R176, | ||||||
|  |     R177, | ||||||
|  |     R178, | ||||||
|  |     R179, | ||||||
|  |     R180, | ||||||
|  |     R181, | ||||||
|  |     R182, | ||||||
|  |     R183, | ||||||
|  |     R184, | ||||||
|  |     R185, | ||||||
|  |     R186, | ||||||
|  |     R187, | ||||||
|  |     R188, | ||||||
|  |     R189, | ||||||
|  |     R190, | ||||||
|  |     R191, | ||||||
|  |     R192, | ||||||
|  |     R193, | ||||||
|  |     R194, | ||||||
|  |     R195, | ||||||
|  |     R196, | ||||||
|  |     R197, | ||||||
|  |     R198, | ||||||
|  |     R199, | ||||||
|  |     R200, | ||||||
|  |     R201, | ||||||
|  |     R202, | ||||||
|  |     R203, | ||||||
|  |     R204, | ||||||
|  |     R205, | ||||||
|  |     R206, | ||||||
|  |     R207, | ||||||
|  |     R208, | ||||||
|  |     R209, | ||||||
|  |     R210, | ||||||
|  |     R211, | ||||||
|  |     R212, | ||||||
|  |     R213, | ||||||
|  |     R214, | ||||||
|  |     R215, | ||||||
|  |     R216, | ||||||
|  |     R217, | ||||||
|  |     R218, | ||||||
|  |     R219, | ||||||
|  |     R220, | ||||||
|  |     R221, | ||||||
|  |     R222, | ||||||
|  |     R223, | ||||||
|  |     R224, | ||||||
|  |     R225, | ||||||
|  |     R226, | ||||||
|  |     R227, | ||||||
|  |     R228, | ||||||
|  |     R229, | ||||||
|  |     R230, | ||||||
|  |     R231, | ||||||
|  |     R232, | ||||||
|  |     R233, | ||||||
|  |     R234, | ||||||
|  |     R235, | ||||||
|  |     R236, | ||||||
|  |     R237, | ||||||
|  |     R238, | ||||||
|  |     R239, | ||||||
|  |     R240, | ||||||
|  |     R241, | ||||||
|  |     R242, | ||||||
|  |     R243, | ||||||
|  |     R244, | ||||||
|  |     R245, | ||||||
|  |     R246, | ||||||
|  |     R247, | ||||||
|  |     R248, | ||||||
|  |     R249, | ||||||
|  |     R250, | ||||||
|  |     R251, | ||||||
|  |     R252, | ||||||
|  |     R253, | ||||||
|  |     R254, | ||||||
|  |     RZ, | ||||||
|  | }; | ||||||
|  | static_assert(static_cast<int>(Reg::RZ) == 255); | ||||||
|  |  | ||||||
|  | [[nodiscard]] constexpr Reg operator+(Reg reg, int num) { | ||||||
|  |     if (reg == Reg::RZ) { | ||||||
|  |         // Adding or subtracting registers from RZ yields RZ | ||||||
|  |         return Reg::RZ; | ||||||
|  |     } | ||||||
|  |     const int result{static_cast<int>(reg) + num}; | ||||||
|  |     if (result >= static_cast<int>(Reg::RZ)) { | ||||||
|  |         throw LogicError("Overflow on register arithmetic"); | ||||||
|  |     } | ||||||
|  |     if (result < 0) { | ||||||
|  |         throw LogicError("Underflow on register arithmetic"); | ||||||
|  |     } | ||||||
|  |     return static_cast<Reg>(result); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | [[nodiscard]] constexpr Reg operator-(Reg reg, int num) { | ||||||
|  |     return reg + (-num); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | [[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) { | ||||||
|  |     return (static_cast<size_t>(reg) / align) * align == static_cast<size_t>(reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Reg> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Reg& reg, FormatContext& ctx) { | ||||||
|  |         if (reg == Shader::IR::Reg::RZ) { | ||||||
|  |             return fmt::format_to(ctx.out(), "RZ"); | ||||||
|  |         } else if (static_cast<int>(reg) >= 0 && static_cast<int>(reg) < 255) { | ||||||
|  |             return fmt::format_to(ctx.out(), "R{}", static_cast<int>(reg)); | ||||||
|  |         } else { | ||||||
|  |             throw Shader::LogicError("Invalid register with raw value {}", static_cast<int>(reg)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										36
									
								
								src/shader_recompiler/frontend/ir/type.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/shader_recompiler/frontend/ir/type.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/type.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | std::string NameOf(Type type) { | ||||||
|  |     static constexpr std::array names{ | ||||||
|  |         "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", "U64", "ZSCO", | ||||||
|  |     }; | ||||||
|  |     const size_t bits{static_cast<size_t>(type)}; | ||||||
|  |     if (bits == 0) { | ||||||
|  |         return "Void"; | ||||||
|  |     } | ||||||
|  |     std::string result; | ||||||
|  |     for (size_t i = 0; i < names.size(); i++) { | ||||||
|  |         if ((bits & (size_t{1} << i)) != 0) { | ||||||
|  |             if (!result.empty()) { | ||||||
|  |                 result += '|'; | ||||||
|  |             } | ||||||
|  |             result += names[i]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AreTypesCompatible(Type lhs, Type rhs) noexcept { | ||||||
|  |     return lhs == rhs || lhs == Type::Opaque || rhs == Type::Opaque; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										47
									
								
								src/shader_recompiler/frontend/ir/type.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/shader_recompiler/frontend/ir/type.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/common_funcs.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | enum class Type { | ||||||
|  |     Void = 0, | ||||||
|  |     Opaque = 1 << 0, | ||||||
|  |     Label = 1 << 1, | ||||||
|  |     Reg = 1 << 2, | ||||||
|  |     Pred = 1 << 3, | ||||||
|  |     Attribute = 1 << 4, | ||||||
|  |     U1 = 1 << 5, | ||||||
|  |     U8 = 1 << 6, | ||||||
|  |     U16 = 1 << 7, | ||||||
|  |     U32 = 1 << 8, | ||||||
|  |     U64 = 1 << 9, | ||||||
|  |     ZSCO = 1 << 10, | ||||||
|  | }; | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(Type) | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string NameOf(Type type); | ||||||
|  |  | ||||||
|  | [[nodiscard]] bool AreTypesCompatible(Type lhs, Type rhs) noexcept; | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::IR::Type> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::IR::Type& type, FormatContext& ctx) { | ||||||
|  |         return fmt::format_to(ctx.out(), "{}", NameOf(type)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										124
									
								
								src/shader_recompiler/frontend/ir/value.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/shader_recompiler/frontend/ir/value.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/value.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(IR::Block* value) noexcept : type{Type::Label}, label{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} | ||||||
|  |  | ||||||
|  | Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} | ||||||
|  |  | ||||||
|  | bool Value::IsIdentity() const noexcept { | ||||||
|  |     return type == Type::Opaque && inst->Opcode() == Opcode::Identity; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Value::IsEmpty() const noexcept { | ||||||
|  |     return type == Type::Void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Value::IsImmediate() const noexcept { | ||||||
|  |     if (IsIdentity()) { | ||||||
|  |         return inst->Arg(0).IsImmediate(); | ||||||
|  |     } | ||||||
|  |     return type != Type::Opaque; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Value::IsLabel() const noexcept { | ||||||
|  |     return type == Type::Label; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Type Value::Type() const noexcept { | ||||||
|  |     if (IsIdentity()) { | ||||||
|  |         return inst->Arg(0).Type(); | ||||||
|  |     } | ||||||
|  |     if (type == Type::Opaque) { | ||||||
|  |         return inst->Type(); | ||||||
|  |     } | ||||||
|  |     return type; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Inst* Value::Inst() const { | ||||||
|  |     ValidateAccess(Type::Opaque); | ||||||
|  |     return inst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Block* Value::Label() const { | ||||||
|  |     ValidateAccess(Type::Label); | ||||||
|  |     return label; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Inst* Value::InstRecursive() const { | ||||||
|  |     ValidateAccess(Type::Opaque); | ||||||
|  |     if (IsIdentity()) { | ||||||
|  |         return inst->Arg(0).InstRecursive(); | ||||||
|  |     } | ||||||
|  |     return inst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Reg Value::Reg() const { | ||||||
|  |     ValidateAccess(Type::Reg); | ||||||
|  |     return reg; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Pred Value::Pred() const { | ||||||
|  |     ValidateAccess(Type::Pred); | ||||||
|  |     return pred; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Attribute Value::Attribute() const { | ||||||
|  |     ValidateAccess(Type::Attribute); | ||||||
|  |     return attribute; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Value::U1() const { | ||||||
|  |     ValidateAccess(Type::U1); | ||||||
|  |     return imm_u1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u8 Value::U8() const { | ||||||
|  |     ValidateAccess(Type::U8); | ||||||
|  |     return imm_u8; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u16 Value::U16() const { | ||||||
|  |     ValidateAccess(Type::U16); | ||||||
|  |     return imm_u16; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u32 Value::U32() const { | ||||||
|  |     ValidateAccess(Type::U32); | ||||||
|  |     return imm_u32; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u64 Value::U64() const { | ||||||
|  |     ValidateAccess(Type::U64); | ||||||
|  |     return imm_u64; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Value::ValidateAccess(IR::Type expected) const { | ||||||
|  |     if (type != expected) { | ||||||
|  |         throw LogicError("Reading {} out of {}", expected, type); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										98
									
								
								src/shader_recompiler/frontend/ir/value.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/shader_recompiler/frontend/ir/value.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/attribute.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/pred.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/reg.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/type.h" | ||||||
|  |  | ||||||
|  | namespace Shader::IR { | ||||||
|  |  | ||||||
|  | class Block; | ||||||
|  | class Inst; | ||||||
|  |  | ||||||
|  | class Value { | ||||||
|  | public: | ||||||
|  |     Value() noexcept : type{IR::Type::Void}, inst{nullptr} {} | ||||||
|  |     explicit Value(IR::Inst* value) noexcept; | ||||||
|  |     explicit Value(IR::Block* value) noexcept; | ||||||
|  |     explicit Value(IR::Reg value) noexcept; | ||||||
|  |     explicit Value(IR::Pred value) noexcept; | ||||||
|  |     explicit Value(IR::Attribute value) noexcept; | ||||||
|  |     explicit Value(bool value) noexcept; | ||||||
|  |     explicit Value(u8 value) noexcept; | ||||||
|  |     explicit Value(u16 value) noexcept; | ||||||
|  |     explicit Value(u32 value) noexcept; | ||||||
|  |     explicit Value(u64 value) noexcept; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] bool IsIdentity() const noexcept; | ||||||
|  |     [[nodiscard]] bool IsEmpty() const noexcept; | ||||||
|  |     [[nodiscard]] bool IsImmediate() const noexcept; | ||||||
|  |     [[nodiscard]] bool IsLabel() const noexcept; | ||||||
|  |     [[nodiscard]] IR::Type Type() const noexcept; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] IR::Inst* Inst() const; | ||||||
|  |     [[nodiscard]] IR::Block* Label() const; | ||||||
|  |     [[nodiscard]] IR::Inst* InstRecursive() const; | ||||||
|  |     [[nodiscard]] IR::Reg Reg() const; | ||||||
|  |     [[nodiscard]] IR::Pred Pred() const; | ||||||
|  |     [[nodiscard]] IR::Attribute Attribute() const; | ||||||
|  |     [[nodiscard]] bool U1() const; | ||||||
|  |     [[nodiscard]] u8 U8() const; | ||||||
|  |     [[nodiscard]] u16 U16() const; | ||||||
|  |     [[nodiscard]] u32 U32() const; | ||||||
|  |     [[nodiscard]] u64 U64() const; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     void ValidateAccess(IR::Type expected) const; | ||||||
|  |  | ||||||
|  |     IR::Type type; | ||||||
|  |     union { | ||||||
|  |         IR::Inst* inst; | ||||||
|  |         IR::Block* label; | ||||||
|  |         IR::Reg reg; | ||||||
|  |         IR::Pred pred; | ||||||
|  |         IR::Attribute attribute; | ||||||
|  |         bool imm_u1; | ||||||
|  |         u8 imm_u8; | ||||||
|  |         u16 imm_u16; | ||||||
|  |         u32 imm_u32; | ||||||
|  |         u64 imm_u64; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <IR::Type type_> | ||||||
|  | class TypedValue : public Value { | ||||||
|  | public: | ||||||
|  |     TypedValue() = default; | ||||||
|  |  | ||||||
|  |     template <IR::Type other_type> | ||||||
|  |     requires((other_type & type_) != IR::Type::Void) explicit(false) | ||||||
|  |         TypedValue(const TypedValue<other_type>& value) | ||||||
|  |         : Value(value) {} | ||||||
|  |  | ||||||
|  |     explicit TypedValue(const Value& value) : Value(value) { | ||||||
|  |         if ((value.Type() & type_) == IR::Type::Void) { | ||||||
|  |             throw InvalidArgument("Incompatible types {} and {}", type_, value.Type()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     explicit TypedValue(IR::Inst* inst) : TypedValue(Value(inst)) {} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | using U1 = TypedValue<Type::U1>; | ||||||
|  | using U8 = TypedValue<Type::U8>; | ||||||
|  | using U16 = TypedValue<Type::U16>; | ||||||
|  | using U32 = TypedValue<Type::U32>; | ||||||
|  | using U64 = TypedValue<Type::U64>; | ||||||
|  | using U32U64 = TypedValue<Type::U32 | Type::U64>; | ||||||
|  | using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>; | ||||||
|  | using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>; | ||||||
|  | using ZSCO = TypedValue<Type::ZSCO>; | ||||||
|  |  | ||||||
|  | } // namespace Shader::IR | ||||||
							
								
								
									
										531
									
								
								src/shader_recompiler/frontend/maxwell/control_flow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										531
									
								
								src/shader_recompiler/frontend/maxwell/control_flow.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,531 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <optional> | ||||||
|  | #include <ranges> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/decode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/location.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell::Flow { | ||||||
|  |  | ||||||
|  | static u32 BranchOffset(Location pc, Instruction inst) { | ||||||
|  |     return pc.Offset() + inst.branch.Offset() + 8; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) { | ||||||
|  |     if (pc <= block.begin || pc >= block.end) { | ||||||
|  |         throw InvalidArgument("Invalid address to split={}", pc); | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         Block{ | ||||||
|  |             .begin{block.begin}, | ||||||
|  |             .end{pc}, | ||||||
|  |             .end_class{EndClass::Branch}, | ||||||
|  |             .id{block.id}, | ||||||
|  |             .stack{block.stack}, | ||||||
|  |             .cond{true}, | ||||||
|  |             .branch_true{new_id}, | ||||||
|  |             .branch_false{UNREACHABLE_BLOCK_ID}, | ||||||
|  |         }, | ||||||
|  |         Block{ | ||||||
|  |             .begin{pc}, | ||||||
|  |             .end{block.end}, | ||||||
|  |             .end_class{block.end_class}, | ||||||
|  |             .id{new_id}, | ||||||
|  |             .stack{std::move(block.stack)}, | ||||||
|  |             .cond{block.cond}, | ||||||
|  |             .branch_true{block.branch_true}, | ||||||
|  |             .branch_false{block.branch_false}, | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Token OpcodeToken(Opcode opcode) { | ||||||
|  |     switch (opcode) { | ||||||
|  |     case Opcode::PBK: | ||||||
|  |     case Opcode::BRK: | ||||||
|  |         return Token::PBK; | ||||||
|  |     case Opcode::PCNT: | ||||||
|  |     case Opcode::CONT: | ||||||
|  |         return Token::PBK; | ||||||
|  |     case Opcode::PEXIT: | ||||||
|  |     case Opcode::EXIT: | ||||||
|  |         return Token::PEXIT; | ||||||
|  |     case Opcode::PLONGJMP: | ||||||
|  |     case Opcode::LONGJMP: | ||||||
|  |         return Token::PLONGJMP; | ||||||
|  |     case Opcode::PRET: | ||||||
|  |     case Opcode::RET: | ||||||
|  |     case Opcode::CAL: | ||||||
|  |         return Token::PRET; | ||||||
|  |     case Opcode::SSY: | ||||||
|  |     case Opcode::SYNC: | ||||||
|  |         return Token::SSY; | ||||||
|  |     default: | ||||||
|  |         throw InvalidArgument("{}", opcode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool IsAbsoluteJump(Opcode opcode) { | ||||||
|  |     switch (opcode) { | ||||||
|  |     case Opcode::JCAL: | ||||||
|  |     case Opcode::JMP: | ||||||
|  |     case Opcode::JMX: | ||||||
|  |         return true; | ||||||
|  |     default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool HasFlowTest(Opcode opcode) { | ||||||
|  |     switch (opcode) { | ||||||
|  |     case Opcode::BRA: | ||||||
|  |     case Opcode::BRX: | ||||||
|  |     case Opcode::EXIT: | ||||||
|  |     case Opcode::JMP: | ||||||
|  |     case Opcode::JMX: | ||||||
|  |     case Opcode::BRK: | ||||||
|  |     case Opcode::CONT: | ||||||
|  |     case Opcode::LONGJMP: | ||||||
|  |     case Opcode::RET: | ||||||
|  |     case Opcode::SYNC: | ||||||
|  |         return true; | ||||||
|  |     case Opcode::CAL: | ||||||
|  |     case Opcode::JCAL: | ||||||
|  |         return false; | ||||||
|  |     default: | ||||||
|  |         throw InvalidArgument("Invalid branch {}", opcode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static std::string Name(const Block& block) { | ||||||
|  |     if (block.begin.IsVirtual()) { | ||||||
|  |         return fmt::format("\"Virtual {}\"", block.id); | ||||||
|  |     } else { | ||||||
|  |         return fmt::format("\"{}\"", block.begin); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Stack::Push(Token token, Location target) { | ||||||
|  |     entries.push_back({ | ||||||
|  |         .token{token}, | ||||||
|  |         .target{target}, | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<Location, Stack> Stack::Pop(Token token) const { | ||||||
|  |     const std::optional<Location> pc{Peek(token)}; | ||||||
|  |     if (!pc) { | ||||||
|  |         throw LogicError("Token could not be found"); | ||||||
|  |     } | ||||||
|  |     return {*pc, Remove(token)}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::optional<Location> Stack::Peek(Token token) const { | ||||||
|  |     const auto reverse_entries{entries | std::views::reverse}; | ||||||
|  |     const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)}; | ||||||
|  |     if (it == reverse_entries.end()) { | ||||||
|  |         return std::nullopt; | ||||||
|  |     } | ||||||
|  |     return it->target; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Stack Stack::Remove(Token token) const { | ||||||
|  |     const auto reverse_entries{entries | std::views::reverse}; | ||||||
|  |     const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)}; | ||||||
|  |     const auto pos{std::distance(reverse_entries.begin(), it)}; | ||||||
|  |     Stack result; | ||||||
|  |     result.entries.insert(result.entries.end(), entries.begin(), entries.end() - pos - 1); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Block::Contains(Location pc) const noexcept { | ||||||
|  |     return pc >= begin && pc < end; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Function::Function(Location start_address) | ||||||
|  |     : entrypoint{start_address}, labels{Label{ | ||||||
|  |                                      .address{start_address}, | ||||||
|  |                                      .block_id{0}, | ||||||
|  |                                      .stack{}, | ||||||
|  |                                  }} {} | ||||||
|  |  | ||||||
|  | CFG::CFG(Environment& env_, Location start_address) : env{env_} { | ||||||
|  |     functions.emplace_back(start_address); | ||||||
|  |     for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { | ||||||
|  |         while (!functions[function_id].labels.empty()) { | ||||||
|  |             Function& function{functions[function_id]}; | ||||||
|  |             Label label{function.labels.back()}; | ||||||
|  |             function.labels.pop_back(); | ||||||
|  |             AnalyzeLabel(function_id, label); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CFG::AnalyzeLabel(FunctionId function_id, Label& label) { | ||||||
|  |     if (InspectVisitedBlocks(function_id, label)) { | ||||||
|  |         // Label address has been visited | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // Try to find the next block | ||||||
|  |     Function* function{&functions[function_id]}; | ||||||
|  |     Location pc{label.address}; | ||||||
|  |     const auto next{std::upper_bound(function->blocks.begin(), function->blocks.end(), pc, | ||||||
|  |                                      [function](Location pc, u32 block_index) { | ||||||
|  |                                          return pc < function->blocks_data[block_index].begin; | ||||||
|  |                                      })}; | ||||||
|  |     const auto next_index{std::distance(function->blocks.begin(), next)}; | ||||||
|  |     const bool is_last{next == function->blocks.end()}; | ||||||
|  |     Location next_pc; | ||||||
|  |     BlockId next_id{UNREACHABLE_BLOCK_ID}; | ||||||
|  |     if (!is_last) { | ||||||
|  |         next_pc = function->blocks_data[*next].begin; | ||||||
|  |         next_id = function->blocks_data[*next].id; | ||||||
|  |     } | ||||||
|  |     // Insert before the next block | ||||||
|  |     Block block{ | ||||||
|  |         .begin{pc}, | ||||||
|  |         .end{pc}, | ||||||
|  |         .end_class{EndClass::Branch}, | ||||||
|  |         .id{label.block_id}, | ||||||
|  |         .stack{std::move(label.stack)}, | ||||||
|  |         .cond{true}, | ||||||
|  |         .branch_true{UNREACHABLE_BLOCK_ID}, | ||||||
|  |         .branch_false{UNREACHABLE_BLOCK_ID}, | ||||||
|  |     }; | ||||||
|  |     // Analyze instructions until it reaches an already visited block or there's a branch | ||||||
|  |     bool is_branch{false}; | ||||||
|  |     while (is_last || pc < next_pc) { | ||||||
|  |         is_branch = AnalyzeInst(block, function_id, pc) == AnalysisState::Branch; | ||||||
|  |         if (is_branch) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ++pc; | ||||||
|  |     } | ||||||
|  |     if (!is_branch) { | ||||||
|  |         // If the block finished without a branch, | ||||||
|  |         // it means that the next instruction is already visited, jump to it | ||||||
|  |         block.end = pc; | ||||||
|  |         block.cond = true; | ||||||
|  |         block.branch_true = next_id; | ||||||
|  |         block.branch_false = UNREACHABLE_BLOCK_ID; | ||||||
|  |     } | ||||||
|  |     // Function's pointer might be invalid, resolve it again | ||||||
|  |     function = &functions[function_id]; | ||||||
|  |     const u32 new_block_index = static_cast<u32>(function->blocks_data.size()); | ||||||
|  |     function->blocks.insert(function->blocks.begin() + next_index, new_block_index); | ||||||
|  |     function->blocks_data.push_back(std::move(block)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool CFG::InspectVisitedBlocks(FunctionId function_id, const Label& label) { | ||||||
|  |     const Location pc{label.address}; | ||||||
|  |     Function& function{functions[function_id]}; | ||||||
|  |     const auto it{std::ranges::find_if(function.blocks, [&function, pc](u32 block_index) { | ||||||
|  |         return function.blocks_data[block_index].Contains(pc); | ||||||
|  |     })}; | ||||||
|  |     if (it == function.blocks.end()) { | ||||||
|  |         // Address has not been visited | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     Block& block{function.blocks_data[*it]}; | ||||||
|  |     if (block.begin == pc) { | ||||||
|  |         throw LogicError("Dangling branch"); | ||||||
|  |     } | ||||||
|  |     const u32 first_index{*it}; | ||||||
|  |     const u32 second_index{static_cast<u32>(function.blocks_data.size())}; | ||||||
|  |     const std::array new_indices{first_index, second_index}; | ||||||
|  |     std::array split_blocks{Split(std::move(block), pc, label.block_id)}; | ||||||
|  |     function.blocks_data[*it] = std::move(split_blocks[0]); | ||||||
|  |     function.blocks_data.push_back(std::move(split_blocks[1])); | ||||||
|  |     function.blocks.insert(function.blocks.erase(it), new_indices.begin(), new_indices.end()); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Location pc) { | ||||||
|  |     const Instruction inst{env.ReadInstruction(pc.Offset())}; | ||||||
|  |     const Opcode opcode{Decode(inst.raw)}; | ||||||
|  |     switch (opcode) { | ||||||
|  |     case Opcode::BRA: | ||||||
|  |     case Opcode::BRX: | ||||||
|  |     case Opcode::JMP: | ||||||
|  |     case Opcode::JMX: | ||||||
|  |     case Opcode::RET: | ||||||
|  |         if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { | ||||||
|  |             return AnalysisState::Continue; | ||||||
|  |         } | ||||||
|  |         switch (opcode) { | ||||||
|  |         case Opcode::BRA: | ||||||
|  |         case Opcode::JMP: | ||||||
|  |             AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode)); | ||||||
|  |             break; | ||||||
|  |         case Opcode::BRX: | ||||||
|  |         case Opcode::JMX: | ||||||
|  |             AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode)); | ||||||
|  |             break; | ||||||
|  |         case Opcode::RET: | ||||||
|  |             block.end_class = EndClass::Return; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         block.end = pc; | ||||||
|  |         return AnalysisState::Branch; | ||||||
|  |     case Opcode::BRK: | ||||||
|  |     case Opcode::CONT: | ||||||
|  |     case Opcode::LONGJMP: | ||||||
|  |     case Opcode::SYNC: { | ||||||
|  |         if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { | ||||||
|  |             return AnalysisState::Continue; | ||||||
|  |         } | ||||||
|  |         const auto [stack_pc, new_stack]{block.stack.Pop(OpcodeToken(opcode))}; | ||||||
|  |         block.branch_true = AddLabel(block, new_stack, stack_pc, function_id); | ||||||
|  |         block.end = pc; | ||||||
|  |         return AnalysisState::Branch; | ||||||
|  |     } | ||||||
|  |     case Opcode::PBK: | ||||||
|  |     case Opcode::PCNT: | ||||||
|  |     case Opcode::PEXIT: | ||||||
|  |     case Opcode::PLONGJMP: | ||||||
|  |     case Opcode::SSY: | ||||||
|  |         block.stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst)); | ||||||
|  |         return AnalysisState::Continue; | ||||||
|  |     case Opcode::EXIT: | ||||||
|  |         return AnalyzeEXIT(block, function_id, pc, inst); | ||||||
|  |     case Opcode::PRET: | ||||||
|  |         throw NotImplementedException("PRET flow analysis"); | ||||||
|  |     case Opcode::CAL: | ||||||
|  |     case Opcode::JCAL: { | ||||||
|  |         const bool is_absolute{IsAbsoluteJump(opcode)}; | ||||||
|  |         const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; | ||||||
|  |         // Technically CAL pushes into PRET, but that's implicit in the function call for us | ||||||
|  |         // Insert the function into the list if it doesn't exist | ||||||
|  |         if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { | ||||||
|  |             functions.push_back(cal_pc); | ||||||
|  |         } | ||||||
|  |         // Handle CAL like a regular instruction | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     const Predicate pred{inst.Pred()}; | ||||||
|  |     if (pred == Predicate{true} || pred == Predicate{false}) { | ||||||
|  |         return AnalysisState::Continue; | ||||||
|  |     } | ||||||
|  |     const IR::Condition cond{static_cast<IR::Pred>(pred.index), pred.negated}; | ||||||
|  |     AnalyzeCondInst(block, function_id, pc, EndClass::Branch, cond); | ||||||
|  |     return AnalysisState::Branch; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, | ||||||
|  |                           EndClass insn_end_class, IR::Condition cond) { | ||||||
|  |     if (block.begin != pc) { | ||||||
|  |         // If the block doesn't start in the conditional instruction | ||||||
|  |         // mark it as a label to visit it later | ||||||
|  |         block.end = pc; | ||||||
|  |         block.cond = true; | ||||||
|  |         block.branch_true = AddLabel(block, block.stack, pc, function_id); | ||||||
|  |         block.branch_false = UNREACHABLE_BLOCK_ID; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // Impersonate the visited block with a virtual block | ||||||
|  |     // Jump from this virtual to the real conditional instruction and the next instruction | ||||||
|  |     Function& function{functions[function_id]}; | ||||||
|  |     const BlockId conditional_block_id{++function.current_block_id}; | ||||||
|  |     function.blocks.push_back(static_cast<u32>(function.blocks_data.size())); | ||||||
|  |     Block& virtual_block{function.blocks_data.emplace_back(Block{ | ||||||
|  |         .begin{}, // Virtual block | ||||||
|  |         .end{}, | ||||||
|  |         .end_class{EndClass::Branch}, | ||||||
|  |         .id{block.id}, // Impersonating | ||||||
|  |         .stack{block.stack}, | ||||||
|  |         .cond{cond}, | ||||||
|  |         .branch_true{conditional_block_id}, | ||||||
|  |         .branch_false{UNREACHABLE_BLOCK_ID}, | ||||||
|  |     })}; | ||||||
|  |     // Set the end properties of the conditional instruction and give it a new identity | ||||||
|  |     Block& conditional_block{block}; | ||||||
|  |     conditional_block.end = pc; | ||||||
|  |     conditional_block.end_class = insn_end_class; | ||||||
|  |     conditional_block.id = conditional_block_id; | ||||||
|  |     // Add a label to the instruction after the conditional instruction | ||||||
|  |     const BlockId endif_block_id{AddLabel(conditional_block, block.stack, pc + 1, function_id)}; | ||||||
|  |     // Branch to the next instruction from the virtual block | ||||||
|  |     virtual_block.branch_false = endif_block_id; | ||||||
|  |     // And branch to it from the conditional instruction if it is a branch | ||||||
|  |     if (insn_end_class == EndClass::Branch) { | ||||||
|  |         conditional_block.cond = true; | ||||||
|  |         conditional_block.branch_true = endif_block_id; | ||||||
|  |         conditional_block.branch_false = UNREACHABLE_BLOCK_ID; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool CFG::AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst, | ||||||
|  |                         Opcode opcode) { | ||||||
|  |     if (inst.branch.is_cbuf) { | ||||||
|  |         throw NotImplementedException("Branch with constant buffer offset"); | ||||||
|  |     } | ||||||
|  |     const Predicate pred{inst.Pred()}; | ||||||
|  |     if (pred == Predicate{false}) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     const bool has_flow_test{HasFlowTest(opcode)}; | ||||||
|  |     const IR::FlowTest flow_test{has_flow_test ? inst.branch.flow_test.Value() : IR::FlowTest::T}; | ||||||
|  |     if (pred != Predicate{true} || flow_test != IR::FlowTest::T) { | ||||||
|  |         block.cond = IR::Condition(flow_test, static_cast<IR::Pred>(pred.index), pred.negated); | ||||||
|  |         block.branch_false = AddLabel(block, block.stack, pc + 1, function_id); | ||||||
|  |     } else { | ||||||
|  |         block.cond = true; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CFG::AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, | ||||||
|  |                      bool is_absolute) { | ||||||
|  |     const Location bra_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; | ||||||
|  |     block.branch_true = AddLabel(block, block.stack, bra_pc, function_id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CFG::AnalyzeBRX(Block&, Location, Instruction, bool is_absolute) { | ||||||
|  |     throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) { | ||||||
|  |     const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; | ||||||
|  |     // Technically CAL pushes into PRET, but that's implicit in the function call for us | ||||||
|  |     // Insert the function to the function list if it doesn't exist | ||||||
|  |     const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)}; | ||||||
|  |     if (it == functions.end()) { | ||||||
|  |         functions.emplace_back(cal_pc); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CFG::AnalysisState CFG::AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, | ||||||
|  |                                     Instruction inst) { | ||||||
|  |     const IR::FlowTest flow_test{inst.branch.flow_test}; | ||||||
|  |     const Predicate pred{inst.Pred()}; | ||||||
|  |     if (pred == Predicate{false} || flow_test == IR::FlowTest::F) { | ||||||
|  |         // EXIT will never be taken | ||||||
|  |         return AnalysisState::Continue; | ||||||
|  |     } | ||||||
|  |     if (pred != Predicate{true} || flow_test != IR::FlowTest::T) { | ||||||
|  |         if (block.stack.Peek(Token::PEXIT).has_value()) { | ||||||
|  |             throw NotImplementedException("Conditional EXIT with PEXIT token"); | ||||||
|  |         } | ||||||
|  |         const IR::Condition cond{flow_test, static_cast<IR::Pred>(pred.index), pred.negated}; | ||||||
|  |         AnalyzeCondInst(block, function_id, pc, EndClass::Exit, cond); | ||||||
|  |         return AnalysisState::Branch; | ||||||
|  |     } | ||||||
|  |     if (const std::optional<Location> exit_pc{block.stack.Peek(Token::PEXIT)}) { | ||||||
|  |         const Stack popped_stack{block.stack.Remove(Token::PEXIT)}; | ||||||
|  |         block.cond = true; | ||||||
|  |         block.branch_true = AddLabel(block, popped_stack, *exit_pc, function_id); | ||||||
|  |         block.branch_false = UNREACHABLE_BLOCK_ID; | ||||||
|  |         return AnalysisState::Branch; | ||||||
|  |     } | ||||||
|  |     block.end = pc; | ||||||
|  |     block.end_class = EndClass::Exit; | ||||||
|  |     return AnalysisState::Branch; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | BlockId CFG::AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id) { | ||||||
|  |     Function& function{functions[function_id]}; | ||||||
|  |     if (block.begin == pc) { | ||||||
|  |         return block.id; | ||||||
|  |     } | ||||||
|  |     const auto target{std::ranges::find(function.blocks_data, pc, &Block::begin)}; | ||||||
|  |     if (target != function.blocks_data.end()) { | ||||||
|  |         return target->id; | ||||||
|  |     } | ||||||
|  |     const BlockId block_id{++function.current_block_id}; | ||||||
|  |     function.labels.push_back(Label{ | ||||||
|  |         .address{pc}, | ||||||
|  |         .block_id{block_id}, | ||||||
|  |         .stack{std::move(stack)}, | ||||||
|  |     }); | ||||||
|  |     return block_id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string CFG::Dot() const { | ||||||
|  |     int node_uid{0}; | ||||||
|  |  | ||||||
|  |     std::string dot{"digraph shader {\n"}; | ||||||
|  |     for (const Function& function : functions) { | ||||||
|  |         dot += fmt::format("\tsubgraph cluster_{} {{\n", function.entrypoint); | ||||||
|  |         dot += fmt::format("\t\tnode [style=filled];\n"); | ||||||
|  |         for (const u32 block_index : function.blocks) { | ||||||
|  |             const Block& block{function.blocks_data[block_index]}; | ||||||
|  |             const std::string name{Name(block)}; | ||||||
|  |             const auto add_branch = [&](BlockId branch_id, bool add_label) { | ||||||
|  |                 const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; | ||||||
|  |                 dot += fmt::format("\t\t{}->", name); | ||||||
|  |                 if (it == function.blocks_data.end()) { | ||||||
|  |                     dot += fmt::format("\"Unknown label {}\"", branch_id); | ||||||
|  |                 } else { | ||||||
|  |                     dot += Name(*it); | ||||||
|  |                 }; | ||||||
|  |                 if (add_label && block.cond != true && block.cond != false) { | ||||||
|  |                     dot += fmt::format(" [label=\"{}\"]", block.cond); | ||||||
|  |                 } | ||||||
|  |                 dot += '\n'; | ||||||
|  |             }; | ||||||
|  |             dot += fmt::format("\t\t{};\n", name); | ||||||
|  |             switch (block.end_class) { | ||||||
|  |             case EndClass::Branch: | ||||||
|  |                 if (block.cond != false) { | ||||||
|  |                     add_branch(block.branch_true, true); | ||||||
|  |                 } | ||||||
|  |                 if (block.cond != true) { | ||||||
|  |                     add_branch(block.branch_false, false); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case EndClass::Exit: | ||||||
|  |                 dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | ||||||
|  |                 dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n", | ||||||
|  |                                    node_uid); | ||||||
|  |                 ++node_uid; | ||||||
|  |                 break; | ||||||
|  |             case EndClass::Return: | ||||||
|  |                 dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | ||||||
|  |                 dot += fmt::format("\t\tN{} [label=\"Return\"][shape=square][style=stripped];\n", | ||||||
|  |                                    node_uid); | ||||||
|  |                 ++node_uid; | ||||||
|  |                 break; | ||||||
|  |             case EndClass::Unreachable: | ||||||
|  |                 dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | ||||||
|  |                 dot += fmt::format( | ||||||
|  |                     "\t\tN{} [label=\"Unreachable\"][shape=square][style=stripped];\n", node_uid); | ||||||
|  |                 ++node_uid; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (function.entrypoint == 8) { | ||||||
|  |             dot += fmt::format("\t\tlabel = \"main\";\n"); | ||||||
|  |         } else { | ||||||
|  |             dot += fmt::format("\t\tlabel = \"Function {}\";\n", function.entrypoint); | ||||||
|  |         } | ||||||
|  |         dot += "\t}\n"; | ||||||
|  |     } | ||||||
|  |     if (!functions.empty()) { | ||||||
|  |         if (functions.front().blocks.empty()) { | ||||||
|  |             dot += "Start;\n"; | ||||||
|  |         } else { | ||||||
|  |             dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front())); | ||||||
|  |         } | ||||||
|  |         dot += fmt::format("\tStart [shape=diamond];\n"); | ||||||
|  |     } | ||||||
|  |     dot += "}\n"; | ||||||
|  |     return dot; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell::Flow | ||||||
							
								
								
									
										137
									
								
								src/shader_recompiler/frontend/maxwell/control_flow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/shader_recompiler/frontend/maxwell/control_flow.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <compare> | ||||||
|  | #include <optional> | ||||||
|  | #include <span> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include <boost/container/small_vector.hpp> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/condition.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/instruction.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/location.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell::Flow { | ||||||
|  |  | ||||||
|  | using BlockId = u32; | ||||||
|  | using FunctionId = size_t; | ||||||
|  |  | ||||||
|  | constexpr BlockId UNREACHABLE_BLOCK_ID{static_cast<u32>(-1)}; | ||||||
|  |  | ||||||
|  | enum class EndClass { | ||||||
|  |     Branch, | ||||||
|  |     Exit, | ||||||
|  |     Return, | ||||||
|  |     Unreachable, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum class Token { | ||||||
|  |     SSY, | ||||||
|  |     PBK, | ||||||
|  |     PEXIT, | ||||||
|  |     PRET, | ||||||
|  |     PCNT, | ||||||
|  |     PLONGJMP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct StackEntry { | ||||||
|  |     auto operator<=>(const StackEntry&) const noexcept = default; | ||||||
|  |  | ||||||
|  |     Token token; | ||||||
|  |     Location target; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Stack { | ||||||
|  | public: | ||||||
|  |     void Push(Token token, Location target); | ||||||
|  |     [[nodiscard]] std::pair<Location, Stack> Pop(Token token) const; | ||||||
|  |     [[nodiscard]] std::optional<Location> Peek(Token token) const; | ||||||
|  |     [[nodiscard]] Stack Remove(Token token) const; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     boost::container::small_vector<StackEntry, 3> entries; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct Block { | ||||||
|  |     [[nodiscard]] bool Contains(Location pc) const noexcept; | ||||||
|  |  | ||||||
|  |     Location begin; | ||||||
|  |     Location end; | ||||||
|  |     EndClass end_class; | ||||||
|  |     BlockId id; | ||||||
|  |     Stack stack; | ||||||
|  |     IR::Condition cond; | ||||||
|  |     BlockId branch_true; | ||||||
|  |     BlockId branch_false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct Label { | ||||||
|  |     Location address; | ||||||
|  |     BlockId block_id; | ||||||
|  |     Stack stack; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct Function { | ||||||
|  |     Function(Location start_address); | ||||||
|  |  | ||||||
|  |     Location entrypoint; | ||||||
|  |     BlockId current_block_id{0}; | ||||||
|  |     boost::container::small_vector<Label, 16> labels; | ||||||
|  |     boost::container::small_vector<u32, 0x130> blocks; | ||||||
|  |     boost::container::small_vector<Block, 0x130> blocks_data; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class CFG { | ||||||
|  |     enum class AnalysisState { | ||||||
|  |         Branch, | ||||||
|  |         Continue, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit CFG(Environment& env, Location start_address); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] std::string Dot() const; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] std::span<const Function> Functions() const noexcept { | ||||||
|  |         return std::span(functions.data(), functions.size()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     void AnalyzeLabel(FunctionId function_id, Label& label); | ||||||
|  |  | ||||||
|  |     /// Inspect already visited blocks. | ||||||
|  |     /// Return true when the block has already been visited | ||||||
|  |     [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc); | ||||||
|  |  | ||||||
|  |     void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class, | ||||||
|  |                          IR::Condition cond); | ||||||
|  |  | ||||||
|  |     /// Return true when the branch instruction is confirmed to be a branch | ||||||
|  |     [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, | ||||||
|  |                                      Instruction inst, Opcode opcode); | ||||||
|  |  | ||||||
|  |     void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, | ||||||
|  |                     bool is_absolute); | ||||||
|  |     void AnalyzeBRX(Block& block, Location pc, Instruction inst, bool is_absolute); | ||||||
|  |     void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute); | ||||||
|  |     AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst); | ||||||
|  |  | ||||||
|  |     /// Return the branch target block id | ||||||
|  |     [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc, | ||||||
|  |                                    FunctionId function_id); | ||||||
|  |  | ||||||
|  |     Environment& env; | ||||||
|  |     boost::container::small_vector<Function, 1> functions; | ||||||
|  |     FunctionId current_function_id{0}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell::Flow | ||||||
							
								
								
									
										149
									
								
								src/shader_recompiler/frontend/maxwell/decode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/shader_recompiler/frontend/maxwell/decode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <bit> | ||||||
|  | #include <memory> | ||||||
|  | #include <string_view> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/decode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | struct MaskValue { | ||||||
|  |     u64 mask; | ||||||
|  |     u64 value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr MaskValue MaskValueFromEncoding(const char* encoding) { | ||||||
|  |     u64 mask{}; | ||||||
|  |     u64 value{}; | ||||||
|  |     u64 bit{u64(1) << 63}; | ||||||
|  |     while (*encoding) { | ||||||
|  |         switch (*encoding) { | ||||||
|  |         case '0': | ||||||
|  |             mask |= bit; | ||||||
|  |             break; | ||||||
|  |         case '1': | ||||||
|  |             mask |= bit; | ||||||
|  |             value |= bit; | ||||||
|  |             break; | ||||||
|  |         case '-': | ||||||
|  |             break; | ||||||
|  |         case ' ': | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             throw LogicError("Invalid encoding character '{}'", *encoding); | ||||||
|  |         } | ||||||
|  |         ++encoding; | ||||||
|  |         if (*encoding != ' ') { | ||||||
|  |             bit >>= 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return MaskValue{.mask{mask}, .value{value}}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct InstEncoding { | ||||||
|  |     MaskValue mask_value; | ||||||
|  |     Opcode opcode; | ||||||
|  | }; | ||||||
|  | constexpr std::array UNORDERED_ENCODINGS{ | ||||||
|  | #define INST(name, cute, encode)                                                                   \ | ||||||
|  |     InstEncoding{                                                                                  \ | ||||||
|  |         .mask_value{MaskValueFromEncoding(encode)},                                                \ | ||||||
|  |         .opcode{Opcode::name},                                                                     \ | ||||||
|  |     }, | ||||||
|  | #include "maxwell.inc" | ||||||
|  | #undef INST | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr auto SortedEncodings() { | ||||||
|  |     std::array encodings{UNORDERED_ENCODINGS}; | ||||||
|  |     std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) { | ||||||
|  |         return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask); | ||||||
|  |     }); | ||||||
|  |     return encodings; | ||||||
|  | } | ||||||
|  | constexpr auto ENCODINGS{SortedEncodings()}; | ||||||
|  |  | ||||||
|  | constexpr int WidestLeftBits() { | ||||||
|  |     int bits{64}; | ||||||
|  |     for (const InstEncoding& encoding : ENCODINGS) { | ||||||
|  |         bits = std::min(bits, std::countr_zero(encoding.mask_value.mask)); | ||||||
|  |     } | ||||||
|  |     return 64 - bits; | ||||||
|  | } | ||||||
|  | constexpr int WIDEST_LEFT_BITS{WidestLeftBits()}; | ||||||
|  | constexpr int MASK_SHIFT{64 - WIDEST_LEFT_BITS}; | ||||||
|  |  | ||||||
|  | constexpr size_t ToFastLookupIndex(u64 value) { | ||||||
|  |     return static_cast<size_t>(value >> MASK_SHIFT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | constexpr size_t FastLookupSize() { | ||||||
|  |     size_t max_width{}; | ||||||
|  |     for (const InstEncoding& encoding : ENCODINGS) { | ||||||
|  |         max_width = std::max(max_width, ToFastLookupIndex(encoding.mask_value.mask)); | ||||||
|  |     } | ||||||
|  |     return max_width + 1; | ||||||
|  | } | ||||||
|  | constexpr size_t FAST_LOOKUP_SIZE{FastLookupSize()}; | ||||||
|  |  | ||||||
|  | struct InstInfo { | ||||||
|  |     [[nodiscard]] u64 Mask() const noexcept { | ||||||
|  |         return static_cast<u64>(high_mask) << MASK_SHIFT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] u64 Value() const noexcept { | ||||||
|  |         return static_cast<u64>(high_value) << MASK_SHIFT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     u16 high_mask; | ||||||
|  |     u16 high_value; | ||||||
|  |     Opcode opcode; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr auto MakeFastLookupTableIndex(size_t index) { | ||||||
|  |     std::array<InstInfo, 2> encodings{}; | ||||||
|  |     size_t element{}; | ||||||
|  |     for (const auto& encoding : ENCODINGS) { | ||||||
|  |         const size_t mask{ToFastLookupIndex(encoding.mask_value.mask)}; | ||||||
|  |         const size_t value{ToFastLookupIndex(encoding.mask_value.value)}; | ||||||
|  |         if ((index & mask) == value) { | ||||||
|  |             encodings.at(element) = InstInfo{ | ||||||
|  |                 .high_mask{static_cast<u16>(encoding.mask_value.mask >> MASK_SHIFT)}, | ||||||
|  |                 .high_value{static_cast<u16>(encoding.mask_value.value >> MASK_SHIFT)}, | ||||||
|  |                 .opcode{encoding.opcode}, | ||||||
|  |             }; | ||||||
|  |             ++element; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return encodings; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*constexpr*/ auto MakeFastLookupTable() { | ||||||
|  |     auto encodings{std::make_unique<std::array<std::array<InstInfo, 2>, FAST_LOOKUP_SIZE>>()}; | ||||||
|  |     for (size_t index = 0; index < FAST_LOOKUP_SIZE; ++index) { | ||||||
|  |         (*encodings)[index] = MakeFastLookupTableIndex(index); | ||||||
|  |     } | ||||||
|  |     return encodings; | ||||||
|  | } | ||||||
|  | const auto FAST_LOOKUP_TABLE{MakeFastLookupTable()}; | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | Opcode Decode(u64 insn) { | ||||||
|  |     const auto& table{(*FAST_LOOKUP_TABLE)[ToFastLookupIndex(insn)]}; | ||||||
|  |     const auto it{std::ranges::find_if( | ||||||
|  |         table, [insn](const InstInfo& info) { return (insn & info.Mask()) == info.Value(); })}; | ||||||
|  |     if (it == table.end()) { | ||||||
|  |         throw NotImplementedException("Instruction 0x{:016x} is unknown / unimplemented", insn); | ||||||
|  |     } | ||||||
|  |     return it->opcode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										14
									
								
								src/shader_recompiler/frontend/maxwell/decode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/shader_recompiler/frontend/maxwell/decode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | [[nodiscard]] Opcode Decode(u64 insn); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										62
									
								
								src/shader_recompiler/frontend/maxwell/instruction.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/shader_recompiler/frontend/maxwell/instruction.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/flow_test.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | struct Predicate { | ||||||
|  |     Predicate() = default; | ||||||
|  |     Predicate(unsigned index_, bool negated_ = false) : index{index_}, negated{negated_} {} | ||||||
|  |     Predicate(bool value) : index{7}, negated{!value} {} | ||||||
|  |     Predicate(u64 raw) : index{static_cast<unsigned>(raw & 7)}, negated{(raw & 8) != 0} {} | ||||||
|  |  | ||||||
|  |     unsigned index; | ||||||
|  |     bool negated; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | inline bool operator==(const Predicate& lhs, const Predicate& rhs) noexcept { | ||||||
|  |     return lhs.index == rhs.index && lhs.negated == rhs.negated; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline bool operator!=(const Predicate& lhs, const Predicate& rhs) noexcept { | ||||||
|  |     return !(lhs == rhs); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | union Instruction { | ||||||
|  |     Instruction(u64 raw_) : raw{raw_} {} | ||||||
|  |  | ||||||
|  |     u64 raw; | ||||||
|  |  | ||||||
|  |     union { | ||||||
|  |         BitField<5, 1, u64> is_cbuf; | ||||||
|  |         BitField<0, 5, IR::FlowTest> flow_test; | ||||||
|  |  | ||||||
|  |         [[nodiscard]] u32 Absolute() const noexcept { | ||||||
|  |             return static_cast<u32>(absolute); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [[nodiscard]] s32 Offset() const noexcept { | ||||||
|  |             return static_cast<s32>(offset); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         BitField<20, 24, s64> offset; | ||||||
|  |         BitField<20, 32, u64> absolute; | ||||||
|  |     } branch; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] Predicate Pred() const noexcept { | ||||||
|  |         return Predicate{pred}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     BitField<16, 4, u64> pred; | ||||||
|  | }; | ||||||
|  | static_assert(std::is_trivially_copyable_v<Instruction>); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										106
									
								
								src/shader_recompiler/frontend/maxwell/location.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/shader_recompiler/frontend/maxwell/location.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <compare> | ||||||
|  | #include <iterator> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | class Location { | ||||||
|  |     static constexpr u32 VIRTUAL_OFFSET{std::numeric_limits<u32>::max()}; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     constexpr Location() = default; | ||||||
|  |  | ||||||
|  |     constexpr Location(u32 initial_offset) : offset{initial_offset} { | ||||||
|  |         if (initial_offset % 8 != 0) { | ||||||
|  |             throw InvalidArgument("initial_offset={} is not a multiple of 8", initial_offset); | ||||||
|  |         } | ||||||
|  |         Align(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] constexpr u32 Offset() const noexcept { | ||||||
|  |         return offset; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] constexpr bool IsVirtual() const { | ||||||
|  |         return offset == VIRTUAL_OFFSET; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr auto operator<=>(const Location&) const noexcept = default; | ||||||
|  |  | ||||||
|  |     constexpr Location operator++() noexcept { | ||||||
|  |         const Location copy{*this}; | ||||||
|  |         Step(); | ||||||
|  |         return copy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr Location operator++(int) noexcept { | ||||||
|  |         Step(); | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr Location operator--() noexcept { | ||||||
|  |         const Location copy{*this}; | ||||||
|  |         Back(); | ||||||
|  |         return copy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr Location operator--(int) noexcept { | ||||||
|  |         Back(); | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr Location operator+(int number) const { | ||||||
|  |         Location new_pc{*this}; | ||||||
|  |         while (number > 0) { | ||||||
|  |             --number; | ||||||
|  |             ++new_pc; | ||||||
|  |         } | ||||||
|  |         while (number < 0) { | ||||||
|  |             ++number; | ||||||
|  |             --new_pc; | ||||||
|  |         } | ||||||
|  |         return new_pc; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr Location operator-(int number) const { | ||||||
|  |         return operator+(-number); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     constexpr void Align() { | ||||||
|  |         offset += offset % 32 == 0 ? 8 : 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr void Step() { | ||||||
|  |         offset += 8 + (offset % 32 == 24 ? 8 : 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr void Back() { | ||||||
|  |         offset -= 8 + (offset % 32 == 8 ? 8 : 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     u32 offset{VIRTUAL_OFFSET}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::Maxwell::Location> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::Maxwell::Location& location, FormatContext& ctx) { | ||||||
|  |         return fmt::format_to(ctx.out(), "{:04x}", location.Offset()); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										285
									
								
								src/shader_recompiler/frontend/maxwell/maxwell.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								src/shader_recompiler/frontend/maxwell/maxwell.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | INST(AL2P,         "AL2P",           "1110 1111 1010 0---") | ||||||
|  | INST(ALD,          "ALD",            "1110 1111 1101 1---") | ||||||
|  | INST(AST,          "AST",            "1110 1111 1111 0---") | ||||||
|  | INST(ATOM_cas,     "ATOM (cas)",     "1110 1110 1111 ----") | ||||||
|  | INST(ATOM,         "ATOM",           "1110 1101 ---- ----") | ||||||
|  | INST(ATOMS_cas,    "ATOMS (cas)",    "1110 1110 ---- ----") | ||||||
|  | INST(ATOMS,        "ATOMS",          "1110 1100 ---- ----") | ||||||
|  | INST(B2R,          "B2R",            "1111 0000 1011 1---") | ||||||
|  | INST(BAR,          "BAR",            "1111 0000 1010 1---") | ||||||
|  | INST(BFE_reg,      "BFE (reg)",      "0101 1100 0000 0---") | ||||||
|  | INST(BFE_cbuf,     "BFE (cbuf)",     "0100 1100 0000 0---") | ||||||
|  | INST(BFE_imm,      "BFE (imm)",      "0011 100- 0000 0---") | ||||||
|  | INST(BFI_reg,      "BFI (reg)",      "0101 1011 1111 0---") | ||||||
|  | INST(BFI_rc,       "BFI (rc)",       "0101 0011 1111 0---") | ||||||
|  | INST(BFI_cr,       "BFI (cr)",       "0100 1011 1111 0---") | ||||||
|  | INST(BFI_imm,      "BFI (imm)",      "0011 011- 1111 0---") | ||||||
|  | INST(BPT,          "BPT",            "1110 0011 1010 ----") | ||||||
|  | INST(BRA,          "BRA",            "1110 0010 0100 ----") | ||||||
|  | INST(BRK,          "BRK",            "1110 0011 0100 ----") | ||||||
|  | INST(BRX,          "BRX",            "1110 0010 0101 ----") | ||||||
|  | INST(CAL,          "CAL",            "1110 0010 0110 ----") | ||||||
|  | INST(CCTL,         "CCTL",           "1110 1111 011- ----") | ||||||
|  | INST(CCTLL,        "CCTLL",          "1110 1111 100- ----") | ||||||
|  | INST(CONT,         "CONT",           "1110 0011 0101 ----") | ||||||
|  | INST(CS2R,         "CS2R",           "0101 0000 1100 1---") | ||||||
|  | INST(CSET,         "CSET",           "0101 0000 1001 1---") | ||||||
|  | INST(CSETP,        "CSETP",          "0101 0000 1010 0---") | ||||||
|  | INST(DADD_reg,     "DADD (reg)",     "0101 1100 0111 0---") | ||||||
|  | INST(DADD_cbuf,    "DADD (cbuf)",    "0100 1100 0111 0---") | ||||||
|  | INST(DADD_imm,     "DADD (imm)",     "0011 100- 0111 0---") | ||||||
|  | INST(DEPBAR,       "DEPBAR",         "1111 0000 1111 0---") | ||||||
|  | INST(DFMA_reg,     "DFMA (reg)",     "0101 1011 0111 ----") | ||||||
|  | INST(DFMA_rc,      "DFMA (rc)",      "0101 0011 0111 ----") | ||||||
|  | INST(DFMA_cr,      "DFMA (cr)",      "0010 1011 0111 ----") | ||||||
|  | INST(DFMA_imm,     "DFMA (imm)",     "0011 011- 0111 ----") | ||||||
|  | INST(DMNMX_reg,    "DMNMX (reg)",    "0100 1100 0101 0---") | ||||||
|  | INST(DMNMX_cbuf,   "DMNMX (cbuf)",   "0101 1100 0101 0---") | ||||||
|  | INST(DMNMX_imm,    "DMNMX (imm)",    "0011 100- 0101 0---") | ||||||
|  | INST(DMUL_reg,     "DMUL (reg)",     "0101 1100 1000 0---") | ||||||
|  | INST(DMUL_cbuf,    "DMUL (cbuf)",    "0100 1100 1000 0---") | ||||||
|  | INST(DMUL_imm,     "DMUL (imm)",     "0011 100- 1000 0---") | ||||||
|  | INST(DSET_reg,     "DSET (reg)",     "0101 1001 0--- ----") | ||||||
|  | INST(DSET_cbuf,    "DSET (cbuf)",    "0100 1001 0--- ----") | ||||||
|  | INST(DSET_imm,     "DSET (imm)",     "0011 001- 0--- ----") | ||||||
|  | INST(DSETP_reg,    "DSETP (reg)",    "0101 1011 1000 ----") | ||||||
|  | INST(DSETP_cbuf,   "DSETP (cbuf)",   "0100 1011 1000 ----") | ||||||
|  | INST(DSETP_imm,    "DSETP (imm)",    "0011 011- 1000 ----") | ||||||
|  | INST(EXIT,         "EXIT",           "1110 0011 0000 ----") | ||||||
|  | INST(F2F_reg,      "F2F (reg)",      "0101 1100 1010 1---") | ||||||
|  | INST(F2F_cbuf,     "F2F (cbuf)",     "0100 1100 1010 1---") | ||||||
|  | INST(F2F_imm,      "F2F (imm)",      "0011 100- 1010 1---") | ||||||
|  | INST(F2I_reg,      "F2I (reg)",      "0101 1100 1011 0---") | ||||||
|  | INST(F2I_cbuf,     "F2I (cbuf)",     "0100 1100 1011 0---") | ||||||
|  | INST(F2I_imm,      "F2I (imm)",      "0011 100- 1011 0---") | ||||||
|  | INST(FADD_reg,     "FADD (reg)",     "0101 1100 0101 1---") | ||||||
|  | INST(FADD_cbuf,    "FADD (cbuf)",    "0100 1100 0101 1---") | ||||||
|  | INST(FADD_imm,     "FADD (imm)",     "0011 100- 0101 1---") | ||||||
|  | INST(FADD32I,      "FADD32I",        "0000 10-- ---- ----") | ||||||
|  | INST(FCHK_reg,     "FCHK (reg)",     "0101 1100 1000 1---") | ||||||
|  | INST(FCHK_cbuf,    "FCHK (cbuf)",    "0100 1100 1000 1---") | ||||||
|  | INST(FCHK_imm,     "FCHK (imm)",     "0011 100- 1000 1---") | ||||||
|  | INST(FCMP_reg,     "FCMP (reg)",     "0101 1011 1010 ----") | ||||||
|  | INST(FCMP_rc,      "FCMP (rc)",      "0101 0011 1010 ----") | ||||||
|  | INST(FCMP_cr,      "FCMP (cr)",      "0100 1011 1010 ----") | ||||||
|  | INST(FCMP_imm,     "FCMP (imm)",     "0011 011- 1010 ----") | ||||||
|  | INST(FFMA_reg,     "FFMA (reg)",     "0101 1001 1--- ----") | ||||||
|  | INST(FFMA_rc,      "FFMA (rc)",      "0101 0001 1--- ----") | ||||||
|  | INST(FFMA_cr,      "FFMA (cr)",      "0100 1001 1--- ----") | ||||||
|  | INST(FFMA_imm,     "FFMA (imm)",     "0011 001- 1--- ----") | ||||||
|  | INST(FFMA32I,      "FFMA32I",        "0000 11-- ---- ----") | ||||||
|  | INST(FLO_reg,      "FLO (reg)",      "0101 1100 0011 0---") | ||||||
|  | INST(FLO_cbuf,     "FLO (cbuf)",     "0100 1100 0011 0---") | ||||||
|  | INST(FLO_imm,      "FLO (imm)",      "0011 100- 0011 0---") | ||||||
|  | INST(FMNMX_reg,    "FMNMX (reg)",    "0101 1100 0110 0---") | ||||||
|  | INST(FMNMX_cbuf,   "FMNMX (cbuf)",   "0100 1100 0110 0---") | ||||||
|  | INST(FMNMX_imm,    "FMNMX (imm)",    "0011 100- 0110 0---") | ||||||
|  | INST(FMUL_reg,     "FMUL (reg)",     "0101 1100 0110 1---") | ||||||
|  | INST(FMUL_cbuf,    "FMUL (cbuf)",    "0100 1100 0110 1---") | ||||||
|  | INST(FMUL_imm,     "FMUL (imm)",     "0011 100- 0110 1---") | ||||||
|  | INST(FMUL32I,      "FMUL32I",        "0001 1110 ---- ----") | ||||||
|  | INST(FSET_reg,     "FSET (reg)",     "0101 1000 ---- ----") | ||||||
|  | INST(FSET_cbuf,    "FSET (cbuf)",    "0100 1000 ---- ----") | ||||||
|  | INST(FSET_imm,     "FSET (imm)",     "0011 000- ---- ----") | ||||||
|  | INST(FSETP_reg,    "FSETP (reg)",    "0101 1011 1011 ----") | ||||||
|  | INST(FSETP_cbuf,   "FSETP (cbuf)",   "0100 1011 1011 ----") | ||||||
|  | INST(FSETP_imm,    "FSETP (imm)",    "0011 011- 1011 ----") | ||||||
|  | INST(FSWZADD,      "FSWZADD",        "0101 0000 1111 1---") | ||||||
|  | INST(GETCRSPTR,    "GETCRSPTR",      "1110 0010 1100 ----") | ||||||
|  | INST(GETLMEMBASE,  "GETLMEMBASE",    "1110 0010 1101 ----") | ||||||
|  | INST(HADD2_reg,    "HADD2 (reg)",    "0101 1101 0001 0---") | ||||||
|  | INST(HADD2_cbuf,   "HADD2 (cbuf)",   "0111 101- 1--- ----") | ||||||
|  | INST(HADD2_imm,    "HADD2 (imm)",    "0111 101- 0--- ----") | ||||||
|  | INST(HADD2_32I,    "HADD2_32I",      "0010 110- ---- ----") | ||||||
|  | INST(HFMA2_reg,    "HFMA2 (reg)",    "0101 1101 0000 0---") | ||||||
|  | INST(HFMA2_rc,     "HFMA2 (rc)",     "0110 0--- 1--- ----") | ||||||
|  | INST(HFMA2_cr,     "HFMA2 (cr)",     "0111 0--- 1--- ----") | ||||||
|  | INST(HFMA2_imm,    "HFMA2 (imm)",    "0111 0--- 0--- ----") | ||||||
|  | INST(HFMA2_32I,    "HFMA2_32I",      "0010 100- ---- ----") | ||||||
|  | INST(HMUL2_reg,    "HMUL2 (reg)",    "0101 1101 0000 1---") | ||||||
|  | INST(HMUL2_cbuf,   "HMUL2 (cbuf)",   "0111 100- 1--- ----") | ||||||
|  | INST(HMUL2_imm,    "HMUL2 (imm)",    "0111 100- 0--- ----") | ||||||
|  | INST(HMUL2_32I,    "HMUL2_32I",      "0010 101- ---- ----") | ||||||
|  | INST(HSET2_reg,    "HSET2 (reg)",    "0101 1101 0001 1---") | ||||||
|  | INST(HSET2_cbuf,   "HSET2 (cbuf)",   "0111 1100 1--- ----") | ||||||
|  | INST(HSET2_imm,    "HSET2 (imm)",    "0111 1100 0--- ----") | ||||||
|  | INST(HSETP2_reg,   "HSETP2 (reg)",   "0101 1101 0010 0---") | ||||||
|  | INST(HSETP2_cbuf,  "HSETP2 (cbuf)",  "0111 111- 1--- ----") | ||||||
|  | INST(HSETP2_imm,   "HSETP2 (imm)",   "0111 111- 0--- ----") | ||||||
|  | INST(I2F_reg,      "I2F (reg)",      "0101 1100 1011 1---") | ||||||
|  | INST(I2F_cbuf,     "I2F (cbuf)",     "0100 1100 1011 1---") | ||||||
|  | INST(I2F_imm,      "I2F (imm)",      "0011 100- 1011 1---") | ||||||
|  | INST(I2I_reg,      "I2I (reg)",      "0101 1100 1110 0---") | ||||||
|  | INST(I2I_cbuf,     "I2I (cbuf)",     "0100 1100 1110 0---") | ||||||
|  | INST(I2I_imm,      "I2I (imm)",      "0011 100- 1110 0---") | ||||||
|  | INST(IADD_reg,     "IADD (reg)",     "0101 1100 0001 0---") | ||||||
|  | INST(IADD_cbuf,    "IADD (cbuf)",    "0100 1100 0001 0---") | ||||||
|  | INST(IADD_imm,     "IADD (imm)",     "0011 100- 0001 0---") | ||||||
|  | INST(IADD3_reg,    "IADD3 (reg)",    "0101 1100 1100 ----") | ||||||
|  | INST(IADD3_cbuf,   "IADD3 (cbuf)",   "0100 1100 1100 ----") | ||||||
|  | INST(IADD3_imm,    "IADD3 (imm)",    "0011 100- 1100 ----") | ||||||
|  | INST(IADD32I,      "IADD32I",        "0001 110- ---- ----") | ||||||
|  | INST(ICMP_reg,     "ICMP (reg)",     "0101 1011 0100 ----") | ||||||
|  | INST(ICMP_rc,      "ICMP (rc)",      "0101 0011 0100 ----") | ||||||
|  | INST(ICMP_cr,      "ICMP (cr)",      "0100 1011 0100 ----") | ||||||
|  | INST(ICMP_imm,     "ICMP (imm)",     "0011 011- 0100 ----") | ||||||
|  | INST(IDE,          "IDE",            "1110 0011 1001 ----") | ||||||
|  | INST(IDP_reg,      "IDP (reg)",      "0101 0011 1111 1---") | ||||||
|  | INST(IDP_imm,      "IDP (imm)",      "0101 0011 1101 1---") | ||||||
|  | INST(IMAD_reg,     "IMAD (reg)",     "0101 1010 0--- ----") | ||||||
|  | INST(IMAD_rc,      "IMAD (rc)",      "0101 0010 0--- ----") | ||||||
|  | INST(IMAD_cr,      "IMAD (cr)",      "0100 1010 0--- ----") | ||||||
|  | INST(IMAD_imm,     "IMAD (imm)",     "0011 010- 0--- ----") | ||||||
|  | INST(IMAD32I,      "IMAD32I",        "1000 00-- ---- ----") | ||||||
|  | INST(IMADSP_reg,   "IMADSP (reg)",   "0101 1010 1--- ----") | ||||||
|  | INST(IMADSP_rc,    "IMADSP (rc)",    "0101 0010 1--- ----") | ||||||
|  | INST(IMADSP_cr,    "IMADSP (cr)",    "0100 1010 1--- ----") | ||||||
|  | INST(IMADSP_imm,   "IMADSP (imm)",   "0011 010- 1--- ----") | ||||||
|  | INST(IMNMX_reg,    "IMNMX (reg)",    "0101 1100 0010 0---") | ||||||
|  | INST(IMNMX_cbuf,   "IMNMX (cbuf)",   "0100 1100 0010 0---") | ||||||
|  | INST(IMNMX_imm,    "IMNMX (imm)",    "0011 100- 0010 0---") | ||||||
|  | INST(IMUL_reg,     "IMUL (reg)",     "0101 1100 0011 1---") | ||||||
|  | INST(IMUL_cbuf,    "IMUL (cbuf)",    "0100 1100 0011 1---") | ||||||
|  | INST(IMUL_imm,     "IMUL (imm)",     "0011 100- 0011 1---") | ||||||
|  | INST(IMUL32I,      "IMUL32I",        "0001 1111 ---- ----") | ||||||
|  | INST(IPA,          "IPA",            "1110 0000 ---- ----") | ||||||
|  | INST(ISBERD,       "ISBERD",         "1110 1111 1101 0---") | ||||||
|  | INST(ISCADD_reg,   "ISCADD (reg)",   "0101 1100 0001 1---") | ||||||
|  | INST(ISCADD_cbuf,  "ISCADD (cbuf)",  "0100 1100 0001 1---") | ||||||
|  | INST(ISCADD_imm,   "ISCADD (imm)",   "0011 100- 0001 1---") | ||||||
|  | INST(ISCADD32I,    "ISCADD32I",      "0001 01-- ---- ----") | ||||||
|  | INST(ISET_reg,     "ISET (reg)",     "0101 1011 0101 ----") | ||||||
|  | INST(ISET_cbuf,    "ISET (cbuf)",    "0100 1011 0101 ----") | ||||||
|  | INST(ISET_imm,     "ISET (imm)",     "0011 011- 0101 ----") | ||||||
|  | INST(ISETP_reg,    "ISETP (reg)",    "0101 1011 0110 ----") | ||||||
|  | INST(ISETP_cbuf,   "ISETP (cbuf)",   "0100 1011 0110 ----") | ||||||
|  | INST(ISETP_imm,    "ISETP (imm)",    "0011 011- 0110 ----") | ||||||
|  | INST(JCAL,         "JCAL",           "1110 0010 0010 ----") | ||||||
|  | INST(JMP,          "JMP",            "1110 0010 0001 ----") | ||||||
|  | INST(JMX,          "JMX",            "1110 0010 0000 ----") | ||||||
|  | INST(KIL,          "KIL",            "1110 0011 0011 ----") | ||||||
|  | INST(LD,           "LD",             "100- ---- ---- ----") | ||||||
|  | INST(LDC,          "LDC",            "1110 1111 1001 0---") | ||||||
|  | INST(LDG,          "LDG",            "1110 1110 1101 0---") | ||||||
|  | INST(LDL,          "LDL",            "1110 1111 0100 0---") | ||||||
|  | INST(LDS,          "LDS",            "1110 1111 0100 1---") | ||||||
|  | INST(LEA_hi_reg,   "LEA (hi reg)",   "0101 1011 1101 1---") | ||||||
|  | INST(LEA_hi_cbuf,  "LEA (hi cbuf)",  "0001 10-- ---- ----") | ||||||
|  | INST(LEA_lo_reg,   "LEA (lo reg)",   "0101 1011 1101 0---") | ||||||
|  | INST(LEA_lo_cbuf,  "LEA (lo cbuf)",  "0100 1011 1101 ----") | ||||||
|  | INST(LEA_lo_imm,   "LEA (lo imm)",   "0011 011- 1101 0---") | ||||||
|  | INST(LEPC,         "LEPC",           "0101 0000 1101 0---") | ||||||
|  | INST(LONGJMP,      "LONGJMP",        "1110 0011 0001 ----") | ||||||
|  | INST(LOP_reg,      "LOP (reg)",      "0101 1100 0100 0---") | ||||||
|  | INST(LOP_cbuf,     "LOP (cbuf)",     "0100 1100 0100 0---") | ||||||
|  | INST(LOP_imm,      "LOP (imm)",      "0011 100- 0100 0---") | ||||||
|  | INST(LOP3_reg,     "LOP3 (reg)",     "0101 1011 1110 0---") | ||||||
|  | INST(LOP3_cbuf,    "LOP3 (cbuf)",    "0011 11-- ---- ----") | ||||||
|  | INST(LOP3_imm,     "LOP3 (imm)",     "0000 001- ---- ----") | ||||||
|  | INST(LOP32I,       "LOP32I",         "0000 01-- ---- ----") | ||||||
|  | INST(MEMBAR,       "MEMBAR",         "1110 1111 1001 1---") | ||||||
|  | INST(MOV_reg,      "MOV (reg)",      "0101 1100 1001 1---") | ||||||
|  | INST(MOV_cbuf,     "MOV (cbuf)",     "0100 1100 1001 1---") | ||||||
|  | INST(MOV_imm,      "MOV (imm)",      "0011 100- 1001 1---") | ||||||
|  | INST(MOV32I,       "MOV32I",         "0000 0001 0000 ----") | ||||||
|  | INST(MUFU,         "MUFU",           "0101 0000 1000 0---") | ||||||
|  | INST(NOP,          "NOP",            "0101 0000 1011 0---") | ||||||
|  | INST(OUT_reg,      "OUT (reg)",      "1111 1011 1110 0---") | ||||||
|  | INST(OUT_cbuf,     "OUT (cbuf)",     "1110 1011 1110 0---") | ||||||
|  | INST(OUT_imm,      "OUT (imm)",      "1111 011- 1110 0---") | ||||||
|  | INST(P2R_reg,      "P2R (reg)",      "0101 1100 1110 1---") | ||||||
|  | INST(P2R_cbuf,     "P2R (cbuf)",     "0100 1100 1110 1---") | ||||||
|  | INST(P2R_imm,      "P2R (imm)",      "0011 1000 1110 1---") | ||||||
|  | INST(PBK,          "PBK",            "1110 0010 1010 ----") | ||||||
|  | INST(PCNT,         "PCNT",           "1110 0010 1011 ----") | ||||||
|  | INST(PEXIT,        "PEXIT",          "1110 0010 0011 ----") | ||||||
|  | INST(PIXLD,        "PIXLD",          "1110 1111 1110 1---") | ||||||
|  | INST(PLONGJMP,     "PLONGJMP",       "1110 0010 1000 ----") | ||||||
|  | INST(POPC_reg,     "POPC (reg)",     "0101 1100 0000 1---") | ||||||
|  | INST(POPC_cbuf,    "POPC (cbuf)",    "0100 1100 0000 1---") | ||||||
|  | INST(POPC_imm,     "POPC (imm)",     "0011 100- 0000 1---") | ||||||
|  | INST(PRET,         "PRET",           "1110 0010 0111 ----") | ||||||
|  | INST(PRMT_reg,     "PRMT (reg)",     "0101 1011 1100 ----") | ||||||
|  | INST(PRMT_rc,      "PRMT (rc)",      "0101 0011 1100 ----") | ||||||
|  | INST(PRMT_cr,      "PRMT (cr)",      "0100 1011 1100 ----") | ||||||
|  | INST(PRMT_imm,     "PRMT (imm)",     "0011 011- 1100 ----") | ||||||
|  | INST(PSET,         "PSET",           "0101 0000 1000 1---") | ||||||
|  | INST(PSETP,        "PSETP",          "0101 0000 1001 0---") | ||||||
|  | INST(R2B,          "R2B",            "1111 0000 1100 0---") | ||||||
|  | INST(R2P_reg,      "R2P (reg)",      "0101 1100 1111 0---") | ||||||
|  | INST(R2P_cbuf,     "R2P (cbuf)",     "0100 1100 1111 0---") | ||||||
|  | INST(R2P_imm,      "R2P (imm)",      "0011 100- 1111 0---") | ||||||
|  | INST(RAM,          "RAM",            "1110 0011 1000 ----") | ||||||
|  | INST(RED,          "RED",            "1110 1011 1111 1---") | ||||||
|  | INST(RET,          "RET",            "1110 0011 0010 ----") | ||||||
|  | INST(RRO_reg,      "RRO (reg)",      "0101 1100 1001 0---") | ||||||
|  | INST(RRO_cbuf,     "RRO (cbuf)",     "0100 1100 1001 0---") | ||||||
|  | INST(RRO_imm,      "RRO (imm)",      "0011 100- 1001 0---") | ||||||
|  | INST(RTT,          "RTT",            "1110 0011 0110 ----") | ||||||
|  | INST(S2R,          "S2R",            "1111 0000 1100 1---") | ||||||
|  | INST(SAM,          "SAM",            "1110 0011 0111 ----") | ||||||
|  | INST(SEL_reg,      "SEL (reg)",      "0101 1100 1010 0---") | ||||||
|  | INST(SEL_cbuf,     "SEL (cbuf)",     "0100 1100 1010 0---") | ||||||
|  | INST(SEL_imm,      "SEL (imm)",      "0011 100- 1010 0---") | ||||||
|  | INST(SETCRSPTR,    "SETCRSPTR",      "1110 0010 1110 ----") | ||||||
|  | INST(SETLMEMBASE,  "SETLMEMBASE",    "1110 0010 1111 ----") | ||||||
|  | INST(SHF_l_reg,    "SHF (l reg)",    "0101 1011 1111 1---") | ||||||
|  | INST(SHF_l_imm,    "SHF (l imm)",    "0011 011- 1111 1---") | ||||||
|  | INST(SHF_r_reg,    "SHF (r reg)",    "0101 1100 1111 1---") | ||||||
|  | INST(SHF_r_imm,    "SHF (r imm)",    "0011 100- 1111 1---") | ||||||
|  | INST(SHFL,         "SHFL",           "1110 1111 0001 0---") | ||||||
|  | INST(SHL_reg,      "SHL (reg)",      "0101 1100 0100 1---") | ||||||
|  | INST(SHL_cbuf,     "SHL (cbuf)",     "0100 1100 0100 1---") | ||||||
|  | INST(SHL_imm,      "SHL (imm)",      "0011 100- 0100 1---") | ||||||
|  | INST(SHR_reg,      "SHR (reg)",      "0101 1100 0010 1---") | ||||||
|  | INST(SHR_cbuf,     "SHR (cbuf)",     "0100 1100 0010 1---") | ||||||
|  | INST(SHR_imm,      "SHR (imm)",      "0011 100- 0010 1---") | ||||||
|  | INST(SSY,          "SSY",            "1110 0010 1001 ----") | ||||||
|  | INST(ST,           "ST",             "101- ---- ---- ----") | ||||||
|  | INST(STG,          "STG",            "1110 1110 1101 1---") | ||||||
|  | INST(STL,          "STL",            "1110 1111 0101 0---") | ||||||
|  | INST(STP,          "STP",            "1110 1110 1010 0---") | ||||||
|  | INST(STS,          "STS",            "1110 1111 0101 1---") | ||||||
|  | INST(SUATOM_cas,   "SUATOM",         "1110 1010 ---- ----") | ||||||
|  | INST(SULD,         "SULD",           "1110 1011 000- ----") | ||||||
|  | INST(SURED,        "SURED",          "1110 1011 010- ----") | ||||||
|  | INST(SUST,         "SUST",           "1110 1011 001- ----") | ||||||
|  | INST(SYNC,         "SYNC",           "1111 0000 1111 1---") | ||||||
|  | INST(TEX,          "TEX",            "1100 00-- --11 1---") | ||||||
|  | INST(TEX_b,        "TEX (b)",        "1101 1110 1011 1---") | ||||||
|  | INST(TEXS,         "TEXS",           "1101 -00- ---- ----") | ||||||
|  | INST(TLD,          "TLD",            "1101 1100 --11 1---") | ||||||
|  | INST(TLD_b,        "TLD (b)",        "1101 1101 --11 1---") | ||||||
|  | INST(TLD4,         "TLD4",           "1100 10-- --11 1---") | ||||||
|  | INST(TLD4_b,       "TLD4 (b)",       "1101 1110 1111 1---") | ||||||
|  | INST(TLD4S,        "TLD4S",          "1101 1111 -0-- ----") | ||||||
|  | INST(TLDS,         "TLDS",           "1101 -01- ---- ----") | ||||||
|  | INST(TMML,         "TMML",           "1101 1111 0101 1---") | ||||||
|  | INST(TMML_b,       "TMML (b)",       "1101 1111 0110 0---") | ||||||
|  | INST(TXA,          "TXA",            "1101 1111 0100 0---") | ||||||
|  | INST(TXD,          "TXD",            "1101 1110 0011 10--") | ||||||
|  | INST(TXD_b,        "TXD (b)",        "1101 1110 0111 10--") | ||||||
|  | INST(TXQ,          "TXQ",            "1101 1111 0100 1---") | ||||||
|  | INST(TXQ_b,        "TXQ (b)",        "1101 1111 0101 0---") | ||||||
|  | INST(VABSDIFF,     "VABSDIFF",       "0101 0100 ---- ----") | ||||||
|  | INST(VABSDIFF4,    "VABSDIFF4",      "0101 0000 0--- ----") | ||||||
|  | INST(VADD,         "VADD",           "0010 00-- ---- ----") | ||||||
|  | INST(VMAD,         "VMAD",           "0101 1111 ---- ----") | ||||||
|  | INST(VMNMX,        "VMNMX",          "0011 101- ---- ----") | ||||||
|  | INST(VOTE,         "VOTE",           "0101 0000 1101 1---") | ||||||
|  | INST(VOTE_vtg,     "VOTE (vtg)",     "0101 0000 1110 0---") | ||||||
|  | INST(VSET,         "VSET",           "0100 000- ---- ----") | ||||||
|  | INST(VSETP,        "VSETP",          "0101 0000 1111 0---") | ||||||
|  | INST(VSHL,         "VSHL",           "0101 0111 ---- ----") | ||||||
|  | INST(VSHR,         "VSHR",           "0101 0110 ---- ----") | ||||||
|  | INST(XMAD_reg,     "XMAD (reg)",     "0101 1011 00-- ----") | ||||||
|  | INST(XMAD_rc,      "XMAD (rc)",      "0101 0001 0--- ----") | ||||||
|  | INST(XMAD_cr,      "XMAD (cr)",      "0100 111- ---- ----") | ||||||
|  | INST(XMAD_imm,     "XMAD (imm)",     "0011 011- 00-- ----") | ||||||
|  |  | ||||||
|  | // Removed due to its weird formatting making fast tables larger | ||||||
|  | // INST(CCTLT,        "CCTLT",          "1110 1011 1111 0--0") | ||||||
							
								
								
									
										26
									
								
								src/shader_recompiler/frontend/maxwell/opcode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/shader_recompiler/frontend/maxwell/opcode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | constexpr std::array NAME_TABLE{ | ||||||
|  | #define INST(name, cute, encode) #cute, | ||||||
|  | #include "maxwell.inc" | ||||||
|  | #undef INST | ||||||
|  | }; | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | const char* NameOf(Opcode opcode) { | ||||||
|  |     if (static_cast<size_t>(opcode) >= NAME_TABLE.size()) { | ||||||
|  |         throw InvalidArgument("Invalid opcode with raw value {}", static_cast<int>(opcode)); | ||||||
|  |     } | ||||||
|  |     return NAME_TABLE[static_cast<size_t>(opcode)]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										30
									
								
								src/shader_recompiler/frontend/maxwell/opcode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/shader_recompiler/frontend/maxwell/opcode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | enum class Opcode { | ||||||
|  | #define INST(name, cute, encode) name, | ||||||
|  | #include "maxwell.inc" | ||||||
|  | #undef INST | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const char* NameOf(Opcode opcode); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | struct fmt::formatter<Shader::Maxwell::Opcode> { | ||||||
|  |     constexpr auto parse(format_parse_context& ctx) { | ||||||
|  |         return ctx.begin(); | ||||||
|  |     } | ||||||
|  |     template <typename FormatContext> | ||||||
|  |     auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) { | ||||||
|  |         return format_to(ctx.out(), "{}", NameOf(opcode)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										69
									
								
								src/shader_recompiler/frontend/maxwell/program.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/shader_recompiler/frontend/maxwell/program.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/maxwell/program.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/termination_code.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | Program::Function::~Function() { | ||||||
|  |     std::ranges::for_each(blocks, &std::destroy_at<IR::Block>); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Program::Program(Environment& env, const Flow::CFG& cfg) { | ||||||
|  |     std::vector<IR::Block*> block_map; | ||||||
|  |     functions.reserve(cfg.Functions().size()); | ||||||
|  |  | ||||||
|  |     for (const Flow::Function& cfg_function : cfg.Functions()) { | ||||||
|  |         Function& function{functions.emplace_back()}; | ||||||
|  |  | ||||||
|  |         const size_t num_blocks{cfg_function.blocks.size()}; | ||||||
|  |         IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)}; | ||||||
|  |         function.blocks.reserve(num_blocks); | ||||||
|  |  | ||||||
|  |         block_map.resize(cfg_function.blocks_data.size()); | ||||||
|  |  | ||||||
|  |         // Visit the instructions of all blocks | ||||||
|  |         for (const Flow::BlockId block_id : cfg_function.blocks) { | ||||||
|  |             const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; | ||||||
|  |  | ||||||
|  |             IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))}; | ||||||
|  |             ++block_memory; | ||||||
|  |             function.blocks.push_back(block); | ||||||
|  |             block_map[flow_block.id] = block; | ||||||
|  |         } | ||||||
|  |         // Now that all blocks are defined, emit the termination instructions | ||||||
|  |         for (const Flow::BlockId block_id : cfg_function.blocks) { | ||||||
|  |             const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; | ||||||
|  |             EmitTerminationCode(flow_block, block_map); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string DumpProgram(const Program& program) { | ||||||
|  |     size_t index{0}; | ||||||
|  |     std::map<const IR::Inst*, size_t> inst_to_index; | ||||||
|  |     std::map<const IR::Block*, size_t> block_to_index; | ||||||
|  |  | ||||||
|  |     for (const Program::Function& function : program.functions) { | ||||||
|  |         for (const IR::Block* const block : function.blocks) { | ||||||
|  |             block_to_index.emplace(block, index); | ||||||
|  |             ++index; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     std::string ret; | ||||||
|  |     for (const Program::Function& function : program.functions) { | ||||||
|  |         ret += fmt::format("Function\n"); | ||||||
|  |         for (const IR::Block* const block : function.blocks) { | ||||||
|  |             ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										39
									
								
								src/shader_recompiler/frontend/maxwell/program.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/shader_recompiler/frontend/maxwell/program.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include <boost/pool/pool_alloc.hpp> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | class Program { | ||||||
|  |     friend std::string DumpProgram(const Program& program); | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit Program(Environment& env, const Flow::CFG& cfg); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     struct Function { | ||||||
|  |         ~Function(); | ||||||
|  |  | ||||||
|  |         std::vector<IR::Block*> blocks; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete, | ||||||
|  |                           boost::details::pool::null_mutex> | ||||||
|  |         block_alloc_pool; | ||||||
|  |     std::vector<Function> functions; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | [[nodiscard]] std::string DumpProgram(const Program& program); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										79
									
								
								src/shader_recompiler/frontend/maxwell/termination_code.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/shader_recompiler/frontend/maxwell/termination_code.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <span> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/termination_code.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | static void EmitExit(IR::IREmitter& ir) { | ||||||
|  |     ir.Exit(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static IR::U1 GetFlowTest(IR::FlowTest flow_test, IR::IREmitter& ir) { | ||||||
|  |     switch (flow_test) { | ||||||
|  |     case IR::FlowTest::T: | ||||||
|  |         return ir.Imm1(true); | ||||||
|  |     case IR::FlowTest::F: | ||||||
|  |         return ir.Imm1(false); | ||||||
|  |     case IR::FlowTest::NE: | ||||||
|  |         // FIXME: Verify this | ||||||
|  |         return ir.LogicalNot(ir.GetZFlag()); | ||||||
|  |     case IR::FlowTest::NaN: | ||||||
|  |         // FIXME: Verify this | ||||||
|  |         return ir.LogicalAnd(ir.GetSFlag(), ir.GetZFlag()); | ||||||
|  |     default: | ||||||
|  |         throw NotImplementedException("Flow test {}", flow_test); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) { | ||||||
|  |     const IR::FlowTest flow_test{cond.FlowTest()}; | ||||||
|  |     const auto [pred, pred_negated]{cond.Pred()}; | ||||||
|  |     if (pred == IR::Pred::PT && !pred_negated) { | ||||||
|  |         return GetFlowTest(flow_test, ir); | ||||||
|  |     } | ||||||
|  |     if (flow_test == IR::FlowTest::T) { | ||||||
|  |         return ir.GetPred(pred, pred_negated); | ||||||
|  |     } | ||||||
|  |     return ir.LogicalAnd(ir.GetPred(pred, pred_negated), GetFlowTest(flow_test, ir)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map, | ||||||
|  |                        IR::IREmitter& ir) { | ||||||
|  |     if (flow_block.cond == true) { | ||||||
|  |         return ir.Branch(block_map[flow_block.branch_true]); | ||||||
|  |     } | ||||||
|  |     if (flow_block.cond == false) { | ||||||
|  |         return ir.Branch(block_map[flow_block.branch_false]); | ||||||
|  |     } | ||||||
|  |     return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true], | ||||||
|  |                                 block_map[flow_block.branch_false]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map) { | ||||||
|  |     IR::Block* const block{block_map[flow_block.id]}; | ||||||
|  |     IR::IREmitter ir(*block); | ||||||
|  |     switch (flow_block.end_class) { | ||||||
|  |     case Flow::EndClass::Branch: | ||||||
|  |         EmitBranch(flow_block, block_map, ir); | ||||||
|  |         break; | ||||||
|  |     case Flow::EndClass::Exit: | ||||||
|  |         EmitExit(ir); | ||||||
|  |         break; | ||||||
|  |     case Flow::EndClass::Return: | ||||||
|  |         ir.Return(); | ||||||
|  |         break; | ||||||
|  |     case Flow::EndClass::Unreachable: | ||||||
|  |         ir.Unreachable(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										16
									
								
								src/shader_recompiler/frontend/maxwell/termination_code.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/shader_recompiler/frontend/maxwell/termination_code.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <span> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::EXIT(u64) { | ||||||
|  |     ir.Exit(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,133 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | enum class DestFormat : u64 { | ||||||
|  |     Invalid, | ||||||
|  |     I16, | ||||||
|  |     I32, | ||||||
|  |     I64, | ||||||
|  | }; | ||||||
|  | enum class SrcFormat : u64 { | ||||||
|  |     Invalid, | ||||||
|  |     F16, | ||||||
|  |     F32, | ||||||
|  |     F64, | ||||||
|  | }; | ||||||
|  | enum class Rounding : u64 { | ||||||
|  |     Round, | ||||||
|  |     Floor, | ||||||
|  |     Ceil, | ||||||
|  |     Trunc, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | union F2I { | ||||||
|  |     u64 raw; | ||||||
|  |     BitField<0, 8, IR::Reg> dest_reg; | ||||||
|  |     BitField<8, 2, DestFormat> dest_format; | ||||||
|  |     BitField<10, 2, SrcFormat> src_format; | ||||||
|  |     BitField<12, 1, u64> is_signed; | ||||||
|  |     BitField<39, 1, Rounding> rounding; | ||||||
|  |     BitField<49, 1, u64> half; | ||||||
|  |     BitField<44, 1, u64> ftz; | ||||||
|  |     BitField<45, 1, u64> abs; | ||||||
|  |     BitField<47, 1, u64> cc; | ||||||
|  |     BitField<49, 1, u64> neg; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | size_t BitSize(DestFormat dest_format) { | ||||||
|  |     switch (dest_format) { | ||||||
|  |     case DestFormat::I16: | ||||||
|  |         return 16; | ||||||
|  |     case DestFormat::I32: | ||||||
|  |         return 32; | ||||||
|  |     case DestFormat::I64: | ||||||
|  |         return 64; | ||||||
|  |     default: | ||||||
|  |         throw NotImplementedException("Invalid destination format {}", dest_format); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::U16U32U64& op_a) { | ||||||
|  |     // F2I is used to convert from a floating point value to an integer | ||||||
|  |     const F2I f2i{insn}; | ||||||
|  |  | ||||||
|  |     const IR::U16U32U64 float_value{v.ir.FPAbsNeg(op_a, f2i.abs != 0, f2i.neg != 0)}; | ||||||
|  |     const IR::U16U32U64 rounded_value{[&] { | ||||||
|  |         switch (f2i.rounding) { | ||||||
|  |         case Rounding::Round: | ||||||
|  |             return v.ir.FPRoundEven(float_value); | ||||||
|  |         case Rounding::Floor: | ||||||
|  |             return v.ir.FPFloor(float_value); | ||||||
|  |         case Rounding::Ceil: | ||||||
|  |             return v.ir.FPCeil(float_value); | ||||||
|  |         case Rounding::Trunc: | ||||||
|  |             return v.ir.FPTrunc(float_value); | ||||||
|  |         default: | ||||||
|  |             throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value()); | ||||||
|  |         } | ||||||
|  |     }()}; | ||||||
|  |  | ||||||
|  |     // TODO: Handle out of bounds conversions. | ||||||
|  |     // For example converting F32 65537.0 to U16, the expected value is 0xffff, | ||||||
|  |  | ||||||
|  |     const bool is_signed{f2i.is_signed != 0}; | ||||||
|  |     const size_t bitsize{BitSize(f2i.dest_format)}; | ||||||
|  |     const IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, rounded_value)}; | ||||||
|  |  | ||||||
|  |     v.X(f2i.dest_reg, result); | ||||||
|  |  | ||||||
|  |     if (f2i.cc != 0) { | ||||||
|  |         v.SetZFlag(v.ir.GetZeroFromOp(result)); | ||||||
|  |         if (is_signed) { | ||||||
|  |             v.SetSFlag(v.ir.GetSignFromOp(result)); | ||||||
|  |         } else { | ||||||
|  |             v.ResetSFlag(); | ||||||
|  |         } | ||||||
|  |         v.ResetCFlag(); | ||||||
|  |  | ||||||
|  |         // TODO: Investigate if out of bound conversions sets the overflow flag | ||||||
|  |         v.ResetOFlag(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::F2I_reg(u64 insn) { | ||||||
|  |     union { | ||||||
|  |         F2I base; | ||||||
|  |         BitField<20, 8, IR::Reg> src_reg; | ||||||
|  |     } const f2i{insn}; | ||||||
|  |  | ||||||
|  |     const IR::U16U32U64 op_a{[&]() -> IR::U16U32U64 { | ||||||
|  |         switch (f2i.base.src_format) { | ||||||
|  |         case SrcFormat::F16: | ||||||
|  |             return ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half); | ||||||
|  |         case SrcFormat::F32: | ||||||
|  |             return X(f2i.src_reg); | ||||||
|  |         case SrcFormat::F64: | ||||||
|  |             return ir.PackDouble2x32(ir.CompositeConstruct(X(f2i.src_reg), X(f2i.src_reg + 1))); | ||||||
|  |         default: | ||||||
|  |             throw NotImplementedException("Invalid F2I source format {}", | ||||||
|  |                                           f2i.base.src_format.Value()); | ||||||
|  |         } | ||||||
|  |     }()}; | ||||||
|  |  | ||||||
|  |     TranslateF2I(*this, insn, op_a); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::F2I_cbuf(u64) { | ||||||
|  |     throw NotImplementedException("{}", Opcode::F2I_cbuf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::F2I_imm(u64) { | ||||||
|  |     throw NotImplementedException("{}", Opcode::F2I_imm); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,71 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | enum class Operation { | ||||||
|  |     Cos = 0, | ||||||
|  |     Sin = 1, | ||||||
|  |     Ex2 = 2,    // Base 2 exponent | ||||||
|  |     Lg2 = 3,    // Base 2 logarithm | ||||||
|  |     Rcp = 4,    // Reciprocal | ||||||
|  |     Rsq = 5,    // Reciprocal square root | ||||||
|  |     Rcp64H = 6, // 64-bit reciprocal | ||||||
|  |     Rsq64H = 7, // 64-bit reciprocal square root | ||||||
|  |     Sqrt = 8, | ||||||
|  | }; | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::MUFU(u64 insn) { | ||||||
|  |     // MUFU is used to implement a bunch of special functions. See Operation. | ||||||
|  |     union { | ||||||
|  |         u64 raw; | ||||||
|  |         BitField<0, 8, IR::Reg> dest_reg; | ||||||
|  |         BitField<8, 8, IR::Reg> src_reg; | ||||||
|  |         BitField<20, 4, Operation> operation; | ||||||
|  |         BitField<46, 1, u64> abs; | ||||||
|  |         BitField<48, 1, u64> neg; | ||||||
|  |         BitField<50, 1, u64> sat; | ||||||
|  |     } const mufu{insn}; | ||||||
|  |  | ||||||
|  |     const IR::U32 op_a{ir.FPAbsNeg(X(mufu.src_reg), mufu.abs != 0, mufu.neg != 0)}; | ||||||
|  |     IR::U32 value{[&]() -> IR::U32 { | ||||||
|  |         switch (mufu.operation) { | ||||||
|  |         case Operation::Cos: | ||||||
|  |             return ir.FPCosNotReduced(op_a); | ||||||
|  |         case Operation::Sin: | ||||||
|  |             return ir.FPSinNotReduced(op_a); | ||||||
|  |         case Operation::Ex2: | ||||||
|  |             return ir.FPExp2NotReduced(op_a); | ||||||
|  |         case Operation::Lg2: | ||||||
|  |             return ir.FPLog2(op_a); | ||||||
|  |         case Operation::Rcp: | ||||||
|  |             return ir.FPRecip(op_a); | ||||||
|  |         case Operation::Rsq: | ||||||
|  |             return ir.FPRecipSqrt(op_a); | ||||||
|  |         case Operation::Rcp64H: | ||||||
|  |             throw NotImplementedException("MUFU.RCP64H"); | ||||||
|  |         case Operation::Rsq64H: | ||||||
|  |             throw NotImplementedException("MUFU.RSQ64H"); | ||||||
|  |         case Operation::Sqrt: | ||||||
|  |             return ir.FPSqrt(op_a); | ||||||
|  |         default: | ||||||
|  |             throw NotImplementedException("Invalid MUFU operation {}", mufu.operation.Value()); | ||||||
|  |         } | ||||||
|  |     }()}; | ||||||
|  |  | ||||||
|  |     if (mufu.sat) { | ||||||
|  |         value = ir.FPSaturate(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     X(mufu.dest_reg, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | IR::U32 TranslatorVisitor::X(IR::Reg reg) { | ||||||
|  |     return ir.GetReg(reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::X(IR::Reg dest_reg, const IR::U32& value) { | ||||||
|  |     ir.SetReg(dest_reg, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::U32 TranslatorVisitor::GetCbuf(u64 insn) { | ||||||
|  |     union { | ||||||
|  |         u64 raw; | ||||||
|  |         BitField<20, 14, s64> offset; | ||||||
|  |         BitField<34, 5, u64> binding; | ||||||
|  |     } const cbuf{insn}; | ||||||
|  |     if (cbuf.binding >= 18) { | ||||||
|  |         throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding); | ||||||
|  |     } | ||||||
|  |     if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) { | ||||||
|  |         throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset); | ||||||
|  |     } | ||||||
|  |     const IR::U32 binding{ir.Imm32(static_cast<u32>(cbuf.binding))}; | ||||||
|  |     const IR::U32 byte_offset{ir.Imm32(static_cast<u32>(cbuf.offset) * 4)}; | ||||||
|  |     return ir.GetCbuf(binding, byte_offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::U32 TranslatorVisitor::GetImm(u64 insn) { | ||||||
|  |     union { | ||||||
|  |         u64 raw; | ||||||
|  |         BitField<20, 19, u64> value; | ||||||
|  |         BitField<56, 1, u64> is_negative; | ||||||
|  |     } const imm{insn}; | ||||||
|  |     const s32 positive_value{static_cast<s32>(imm.value)}; | ||||||
|  |     const s32 value{imm.is_negative != 0 ? -positive_value : positive_value}; | ||||||
|  |     return ir.Imm32(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::SetZFlag(const IR::U1& value) { | ||||||
|  |     ir.SetZFlag(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::SetSFlag(const IR::U1& value) { | ||||||
|  |     ir.SetSFlag(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::SetCFlag(const IR::U1& value) { | ||||||
|  |     ir.SetCFlag(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::SetOFlag(const IR::U1& value) { | ||||||
|  |     ir.SetOFlag(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::ResetZero() { | ||||||
|  |     SetZFlag(ir.Imm1(false)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::ResetSFlag() { | ||||||
|  |     SetSFlag(ir.Imm1(false)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::ResetCFlag() { | ||||||
|  |     SetCFlag(ir.Imm1(false)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::ResetOFlag() { | ||||||
|  |     SetOFlag(ir.Imm1(false)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										316
									
								
								src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,316 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/instruction.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | class TranslatorVisitor { | ||||||
|  | public: | ||||||
|  |     explicit TranslatorVisitor(Environment& env_, IR::Block& block) : env{env_} ,ir(block) {} | ||||||
|  |  | ||||||
|  |     Environment& env; | ||||||
|  |     IR::IREmitter ir; | ||||||
|  |  | ||||||
|  |     void AL2P(u64 insn); | ||||||
|  |     void ALD(u64 insn); | ||||||
|  |     void AST(u64 insn); | ||||||
|  |     void ATOM_cas(u64 insn); | ||||||
|  |     void ATOM(u64 insn); | ||||||
|  |     void ATOMS_cas(u64 insn); | ||||||
|  |     void ATOMS(u64 insn); | ||||||
|  |     void B2R(u64 insn); | ||||||
|  |     void BAR(u64 insn); | ||||||
|  |     void BFE_reg(u64 insn); | ||||||
|  |     void BFE_cbuf(u64 insn); | ||||||
|  |     void BFE_imm(u64 insn); | ||||||
|  |     void BFI_reg(u64 insn); | ||||||
|  |     void BFI_rc(u64 insn); | ||||||
|  |     void BFI_cr(u64 insn); | ||||||
|  |     void BFI_imm(u64 insn); | ||||||
|  |     void BPT(u64 insn); | ||||||
|  |     void BRA(u64 insn); | ||||||
|  |     void BRK(u64 insn); | ||||||
|  |     void BRX(u64 insn); | ||||||
|  |     void CAL(u64 insn); | ||||||
|  |     void CCTL(u64 insn); | ||||||
|  |     void CCTLL(u64 insn); | ||||||
|  |     void CONT(u64 insn); | ||||||
|  |     void CS2R(u64 insn); | ||||||
|  |     void CSET(u64 insn); | ||||||
|  |     void CSETP(u64 insn); | ||||||
|  |     void DADD_reg(u64 insn); | ||||||
|  |     void DADD_cbuf(u64 insn); | ||||||
|  |     void DADD_imm(u64 insn); | ||||||
|  |     void DEPBAR(u64 insn); | ||||||
|  |     void DFMA_reg(u64 insn); | ||||||
|  |     void DFMA_rc(u64 insn); | ||||||
|  |     void DFMA_cr(u64 insn); | ||||||
|  |     void DFMA_imm(u64 insn); | ||||||
|  |     void DMNMX_reg(u64 insn); | ||||||
|  |     void DMNMX_cbuf(u64 insn); | ||||||
|  |     void DMNMX_imm(u64 insn); | ||||||
|  |     void DMUL_reg(u64 insn); | ||||||
|  |     void DMUL_cbuf(u64 insn); | ||||||
|  |     void DMUL_imm(u64 insn); | ||||||
|  |     void DSET_reg(u64 insn); | ||||||
|  |     void DSET_cbuf(u64 insn); | ||||||
|  |     void DSET_imm(u64 insn); | ||||||
|  |     void DSETP_reg(u64 insn); | ||||||
|  |     void DSETP_cbuf(u64 insn); | ||||||
|  |     void DSETP_imm(u64 insn); | ||||||
|  |     void EXIT(u64 insn); | ||||||
|  |     void F2F_reg(u64 insn); | ||||||
|  |     void F2F_cbuf(u64 insn); | ||||||
|  |     void F2F_imm(u64 insn); | ||||||
|  |     void F2I_reg(u64 insn); | ||||||
|  |     void F2I_cbuf(u64 insn); | ||||||
|  |     void F2I_imm(u64 insn); | ||||||
|  |     void FADD_reg(u64 insn); | ||||||
|  |     void FADD_cbuf(u64 insn); | ||||||
|  |     void FADD_imm(u64 insn); | ||||||
|  |     void FADD32I(u64 insn); | ||||||
|  |     void FCHK_reg(u64 insn); | ||||||
|  |     void FCHK_cbuf(u64 insn); | ||||||
|  |     void FCHK_imm(u64 insn); | ||||||
|  |     void FCMP_reg(u64 insn); | ||||||
|  |     void FCMP_rc(u64 insn); | ||||||
|  |     void FCMP_cr(u64 insn); | ||||||
|  |     void FCMP_imm(u64 insn); | ||||||
|  |     void FFMA_reg(u64 insn); | ||||||
|  |     void FFMA_rc(u64 insn); | ||||||
|  |     void FFMA_cr(u64 insn); | ||||||
|  |     void FFMA_imm(u64 insn); | ||||||
|  |     void FFMA32I(u64 insn); | ||||||
|  |     void FLO_reg(u64 insn); | ||||||
|  |     void FLO_cbuf(u64 insn); | ||||||
|  |     void FLO_imm(u64 insn); | ||||||
|  |     void FMNMX_reg(u64 insn); | ||||||
|  |     void FMNMX_cbuf(u64 insn); | ||||||
|  |     void FMNMX_imm(u64 insn); | ||||||
|  |     void FMUL_reg(u64 insn); | ||||||
|  |     void FMUL_cbuf(u64 insn); | ||||||
|  |     void FMUL_imm(u64 insn); | ||||||
|  |     void FMUL32I(u64 insn); | ||||||
|  |     void FSET_reg(u64 insn); | ||||||
|  |     void FSET_cbuf(u64 insn); | ||||||
|  |     void FSET_imm(u64 insn); | ||||||
|  |     void FSETP_reg(u64 insn); | ||||||
|  |     void FSETP_cbuf(u64 insn); | ||||||
|  |     void FSETP_imm(u64 insn); | ||||||
|  |     void FSWZADD(u64 insn); | ||||||
|  |     void GETCRSPTR(u64 insn); | ||||||
|  |     void GETLMEMBASE(u64 insn); | ||||||
|  |     void HADD2_reg(u64 insn); | ||||||
|  |     void HADD2_cbuf(u64 insn); | ||||||
|  |     void HADD2_imm(u64 insn); | ||||||
|  |     void HADD2_32I(u64 insn); | ||||||
|  |     void HFMA2_reg(u64 insn); | ||||||
|  |     void HFMA2_rc(u64 insn); | ||||||
|  |     void HFMA2_cr(u64 insn); | ||||||
|  |     void HFMA2_imm(u64 insn); | ||||||
|  |     void HFMA2_32I(u64 insn); | ||||||
|  |     void HMUL2_reg(u64 insn); | ||||||
|  |     void HMUL2_cbuf(u64 insn); | ||||||
|  |     void HMUL2_imm(u64 insn); | ||||||
|  |     void HMUL2_32I(u64 insn); | ||||||
|  |     void HSET2_reg(u64 insn); | ||||||
|  |     void HSET2_cbuf(u64 insn); | ||||||
|  |     void HSET2_imm(u64 insn); | ||||||
|  |     void HSETP2_reg(u64 insn); | ||||||
|  |     void HSETP2_cbuf(u64 insn); | ||||||
|  |     void HSETP2_imm(u64 insn); | ||||||
|  |     void I2F_reg(u64 insn); | ||||||
|  |     void I2F_cbuf(u64 insn); | ||||||
|  |     void I2F_imm(u64 insn); | ||||||
|  |     void I2I_reg(u64 insn); | ||||||
|  |     void I2I_cbuf(u64 insn); | ||||||
|  |     void I2I_imm(u64 insn); | ||||||
|  |     void IADD_reg(u64 insn); | ||||||
|  |     void IADD_cbuf(u64 insn); | ||||||
|  |     void IADD_imm(u64 insn); | ||||||
|  |     void IADD3_reg(u64 insn); | ||||||
|  |     void IADD3_cbuf(u64 insn); | ||||||
|  |     void IADD3_imm(u64 insn); | ||||||
|  |     void IADD32I(u64 insn); | ||||||
|  |     void ICMP_reg(u64 insn); | ||||||
|  |     void ICMP_rc(u64 insn); | ||||||
|  |     void ICMP_cr(u64 insn); | ||||||
|  |     void ICMP_imm(u64 insn); | ||||||
|  |     void IDE(u64 insn); | ||||||
|  |     void IDP_reg(u64 insn); | ||||||
|  |     void IDP_imm(u64 insn); | ||||||
|  |     void IMAD_reg(u64 insn); | ||||||
|  |     void IMAD_rc(u64 insn); | ||||||
|  |     void IMAD_cr(u64 insn); | ||||||
|  |     void IMAD_imm(u64 insn); | ||||||
|  |     void IMAD32I(u64 insn); | ||||||
|  |     void IMADSP_reg(u64 insn); | ||||||
|  |     void IMADSP_rc(u64 insn); | ||||||
|  |     void IMADSP_cr(u64 insn); | ||||||
|  |     void IMADSP_imm(u64 insn); | ||||||
|  |     void IMNMX_reg(u64 insn); | ||||||
|  |     void IMNMX_cbuf(u64 insn); | ||||||
|  |     void IMNMX_imm(u64 insn); | ||||||
|  |     void IMUL_reg(u64 insn); | ||||||
|  |     void IMUL_cbuf(u64 insn); | ||||||
|  |     void IMUL_imm(u64 insn); | ||||||
|  |     void IMUL32I(u64 insn); | ||||||
|  |     void IPA(u64 insn); | ||||||
|  |     void ISBERD(u64 insn); | ||||||
|  |     void ISCADD_reg(u64 insn); | ||||||
|  |     void ISCADD_cbuf(u64 insn); | ||||||
|  |     void ISCADD_imm(u64 insn); | ||||||
|  |     void ISCADD32I(u64 insn); | ||||||
|  |     void ISET_reg(u64 insn); | ||||||
|  |     void ISET_cbuf(u64 insn); | ||||||
|  |     void ISET_imm(u64 insn); | ||||||
|  |     void ISETP_reg(u64 insn); | ||||||
|  |     void ISETP_cbuf(u64 insn); | ||||||
|  |     void ISETP_imm(u64 insn); | ||||||
|  |     void JCAL(u64 insn); | ||||||
|  |     void JMP(u64 insn); | ||||||
|  |     void JMX(u64 insn); | ||||||
|  |     void KIL(u64 insn); | ||||||
|  |     void LD(u64 insn); | ||||||
|  |     void LDC(u64 insn); | ||||||
|  |     void LDG(u64 insn); | ||||||
|  |     void LDL(u64 insn); | ||||||
|  |     void LDS(u64 insn); | ||||||
|  |     void LEA_hi_reg(u64 insn); | ||||||
|  |     void LEA_hi_cbuf(u64 insn); | ||||||
|  |     void LEA_lo_reg(u64 insn); | ||||||
|  |     void LEA_lo_cbuf(u64 insn); | ||||||
|  |     void LEA_lo_imm(u64 insn); | ||||||
|  |     void LEPC(u64 insn); | ||||||
|  |     void LONGJMP(u64 insn); | ||||||
|  |     void LOP_reg(u64 insn); | ||||||
|  |     void LOP_cbuf(u64 insn); | ||||||
|  |     void LOP_imm(u64 insn); | ||||||
|  |     void LOP3_reg(u64 insn); | ||||||
|  |     void LOP3_cbuf(u64 insn); | ||||||
|  |     void LOP3_imm(u64 insn); | ||||||
|  |     void LOP32I(u64 insn); | ||||||
|  |     void MEMBAR(u64 insn); | ||||||
|  |     void MOV_reg(u64 insn); | ||||||
|  |     void MOV_cbuf(u64 insn); | ||||||
|  |     void MOV_imm(u64 insn); | ||||||
|  |     void MOV32I(u64 insn); | ||||||
|  |     void MUFU(u64 insn); | ||||||
|  |     void NOP(u64 insn); | ||||||
|  |     void OUT_reg(u64 insn); | ||||||
|  |     void OUT_cbuf(u64 insn); | ||||||
|  |     void OUT_imm(u64 insn); | ||||||
|  |     void P2R_reg(u64 insn); | ||||||
|  |     void P2R_cbuf(u64 insn); | ||||||
|  |     void P2R_imm(u64 insn); | ||||||
|  |     void PBK(u64 insn); | ||||||
|  |     void PCNT(u64 insn); | ||||||
|  |     void PEXIT(u64 insn); | ||||||
|  |     void PIXLD(u64 insn); | ||||||
|  |     void PLONGJMP(u64 insn); | ||||||
|  |     void POPC_reg(u64 insn); | ||||||
|  |     void POPC_cbuf(u64 insn); | ||||||
|  |     void POPC_imm(u64 insn); | ||||||
|  |     void PRET(u64 insn); | ||||||
|  |     void PRMT_reg(u64 insn); | ||||||
|  |     void PRMT_rc(u64 insn); | ||||||
|  |     void PRMT_cr(u64 insn); | ||||||
|  |     void PRMT_imm(u64 insn); | ||||||
|  |     void PSET(u64 insn); | ||||||
|  |     void PSETP(u64 insn); | ||||||
|  |     void R2B(u64 insn); | ||||||
|  |     void R2P_reg(u64 insn); | ||||||
|  |     void R2P_cbuf(u64 insn); | ||||||
|  |     void R2P_imm(u64 insn); | ||||||
|  |     void RAM(u64 insn); | ||||||
|  |     void RED(u64 insn); | ||||||
|  |     void RET(u64 insn); | ||||||
|  |     void RRO_reg(u64 insn); | ||||||
|  |     void RRO_cbuf(u64 insn); | ||||||
|  |     void RRO_imm(u64 insn); | ||||||
|  |     void RTT(u64 insn); | ||||||
|  |     void S2R(u64 insn); | ||||||
|  |     void SAM(u64 insn); | ||||||
|  |     void SEL_reg(u64 insn); | ||||||
|  |     void SEL_cbuf(u64 insn); | ||||||
|  |     void SEL_imm(u64 insn); | ||||||
|  |     void SETCRSPTR(u64 insn); | ||||||
|  |     void SETLMEMBASE(u64 insn); | ||||||
|  |     void SHF_l_reg(u64 insn); | ||||||
|  |     void SHF_l_imm(u64 insn); | ||||||
|  |     void SHF_r_reg(u64 insn); | ||||||
|  |     void SHF_r_imm(u64 insn); | ||||||
|  |     void SHFL(u64 insn); | ||||||
|  |     void SHL_reg(u64 insn); | ||||||
|  |     void SHL_cbuf(u64 insn); | ||||||
|  |     void SHL_imm(u64 insn); | ||||||
|  |     void SHR_reg(u64 insn); | ||||||
|  |     void SHR_cbuf(u64 insn); | ||||||
|  |     void SHR_imm(u64 insn); | ||||||
|  |     void SSY(u64 insn); | ||||||
|  |     void ST(u64 insn); | ||||||
|  |     void STG(u64 insn); | ||||||
|  |     void STL(u64 insn); | ||||||
|  |     void STP(u64 insn); | ||||||
|  |     void STS(u64 insn); | ||||||
|  |     void SUATOM_cas(u64 insn); | ||||||
|  |     void SULD(u64 insn); | ||||||
|  |     void SURED(u64 insn); | ||||||
|  |     void SUST(u64 insn); | ||||||
|  |     void SYNC(u64 insn); | ||||||
|  |     void TEX(u64 insn); | ||||||
|  |     void TEX_b(u64 insn); | ||||||
|  |     void TEXS(u64 insn); | ||||||
|  |     void TLD(u64 insn); | ||||||
|  |     void TLD_b(u64 insn); | ||||||
|  |     void TLD4(u64 insn); | ||||||
|  |     void TLD4_b(u64 insn); | ||||||
|  |     void TLD4S(u64 insn); | ||||||
|  |     void TLDS(u64 insn); | ||||||
|  |     void TMML(u64 insn); | ||||||
|  |     void TMML_b(u64 insn); | ||||||
|  |     void TXA(u64 insn); | ||||||
|  |     void TXD(u64 insn); | ||||||
|  |     void TXD_b(u64 insn); | ||||||
|  |     void TXQ(u64 insn); | ||||||
|  |     void TXQ_b(u64 insn); | ||||||
|  |     void VABSDIFF(u64 insn); | ||||||
|  |     void VABSDIFF4(u64 insn); | ||||||
|  |     void VADD(u64 insn); | ||||||
|  |     void VMAD(u64 insn); | ||||||
|  |     void VMNMX(u64 insn); | ||||||
|  |     void VOTE(u64 insn); | ||||||
|  |     void VOTE_vtg(u64 insn); | ||||||
|  |     void VSET(u64 insn); | ||||||
|  |     void VSETP(u64 insn); | ||||||
|  |     void VSHL(u64 insn); | ||||||
|  |     void VSHR(u64 insn); | ||||||
|  |     void XMAD_reg(u64 insn); | ||||||
|  |     void XMAD_rc(u64 insn); | ||||||
|  |     void XMAD_cr(u64 insn); | ||||||
|  |     void XMAD_imm(u64 insn); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] IR::U32 X(IR::Reg reg); | ||||||
|  |     void X(IR::Reg dest_reg, const IR::U32& value); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] IR::U32 GetCbuf(u64 insn); | ||||||
|  |  | ||||||
|  |     [[nodiscard]] IR::U32 GetImm(u64 insn); | ||||||
|  |  | ||||||
|  |     void SetZFlag(const IR::U1& value); | ||||||
|  |     void SetSFlag(const IR::U1& value); | ||||||
|  |     void SetCFlag(const IR::U1& value); | ||||||
|  |     void SetOFlag(const IR::U1& value); | ||||||
|  |  | ||||||
|  |     void ResetZero(); | ||||||
|  |     void ResetSFlag(); | ||||||
|  |     void ResetCFlag(); | ||||||
|  |     void ResetOFlag(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,92 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | enum class InterpolationMode : u64 { | ||||||
|  |     Pass = 0, | ||||||
|  |     Multiply = 1, | ||||||
|  |     Constant = 2, | ||||||
|  |     Sc = 3, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum class SampleMode : u64 { | ||||||
|  |     Default = 0, | ||||||
|  |     Centroid = 1, | ||||||
|  |     Offset = 2, | ||||||
|  | }; | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::IPA(u64 insn) { | ||||||
|  |     // IPA is the instruction used to read varyings from a fragment shader. | ||||||
|  |     // gl_FragCoord is mapped to the gl_Position attribute. | ||||||
|  |     // It yields unknown results when used outside of the fragment shader stage. | ||||||
|  |     union { | ||||||
|  |         u64 raw; | ||||||
|  |         BitField<0, 8, IR::Reg> dest_reg; | ||||||
|  |         BitField<8, 8, IR::Reg> index_reg; | ||||||
|  |         BitField<20, 8, IR::Reg> multiplier; | ||||||
|  |         BitField<30, 8, IR::Attribute> attribute; | ||||||
|  |         BitField<38, 1, u64> idx; | ||||||
|  |         BitField<51, 1, u64> sat; | ||||||
|  |         BitField<52, 2, SampleMode> sample_mode; | ||||||
|  |         BitField<54, 2, InterpolationMode> interpolation_mode; | ||||||
|  |     } const ipa{insn}; | ||||||
|  |  | ||||||
|  |     // Indexed IPAs are used for indexed varyings. | ||||||
|  |     // For example: | ||||||
|  |     // | ||||||
|  |     // in vec4 colors[4]; | ||||||
|  |     // uniform int idx; | ||||||
|  |     // void main() { | ||||||
|  |     //     gl_FragColor = colors[idx]; | ||||||
|  |     // } | ||||||
|  |     const bool is_indexed{ipa.idx != 0 && ipa.index_reg != IR::Reg::RZ}; | ||||||
|  |     if (is_indexed) { | ||||||
|  |         throw NotImplementedException("IPA.IDX"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const IR::Attribute attribute{ipa.attribute}; | ||||||
|  |     IR::U32 value{ir.GetAttribute(attribute)}; | ||||||
|  |     if (IR::IsGeneric(attribute)) { | ||||||
|  |         // const bool is_perspective{UnimplementedReadHeader(GenericAttributeIndex(attribute))}; | ||||||
|  |         const bool is_perspective{false}; | ||||||
|  |         if (is_perspective) { | ||||||
|  |             const IR::U32 rcp_position_w{ir.FPRecip(ir.GetAttribute(IR::Attribute::PositionW))}; | ||||||
|  |             value = ir.FPMul(value, rcp_position_w); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     switch (ipa.interpolation_mode) { | ||||||
|  |     case InterpolationMode::Pass: | ||||||
|  |         break; | ||||||
|  |     case InterpolationMode::Multiply: | ||||||
|  |         value = ir.FPMul(value, ir.GetReg(ipa.multiplier)); | ||||||
|  |         break; | ||||||
|  |     case InterpolationMode::Constant: | ||||||
|  |         throw NotImplementedException("IPA.CONSTANT"); | ||||||
|  |     case InterpolationMode::Sc: | ||||||
|  |         throw NotImplementedException("IPA.SC"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Saturated IPAs are generally generated out of clamped varyings. | ||||||
|  |     // For example: clamp(some_varying, 0.0, 1.0) | ||||||
|  |     const bool is_saturated{ipa.sat != 0}; | ||||||
|  |     if (is_saturated) { | ||||||
|  |         if (attribute == IR::Attribute::FrontFace) { | ||||||
|  |             throw NotImplementedException("IPA.SAT on FrontFace"); | ||||||
|  |         } | ||||||
|  |         value = ir.FPSaturate(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ir.SetReg(ipa.dest_reg, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,90 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | enum class StoreSize : u64 { | ||||||
|  |     U8, | ||||||
|  |     S8, | ||||||
|  |     U16, | ||||||
|  |     S16, | ||||||
|  |     B32, | ||||||
|  |     B64, | ||||||
|  |     B128, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // See Table 28 in https://docs.nvidia.com/cuda/parallel-thread-execution/index.html | ||||||
|  | enum class StoreCache : u64 { | ||||||
|  |     WB, // Cache write-back all coherent levels | ||||||
|  |     CG, // Cache at global level | ||||||
|  |     CS, // Cache streaming, likely to be accessed once | ||||||
|  |     WT, // Cache write-through (to system memory) | ||||||
|  | }; | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::STG(u64 insn) { | ||||||
|  |     // STG stores registers into global memory. | ||||||
|  |     union { | ||||||
|  |         u64 raw; | ||||||
|  |         BitField<0, 8, IR::Reg> data_reg; | ||||||
|  |         BitField<8, 8, IR::Reg> addr_reg; | ||||||
|  |         BitField<45, 1, u64> e; | ||||||
|  |         BitField<46, 2, StoreCache> cache; | ||||||
|  |         BitField<48, 3, StoreSize> size; | ||||||
|  |     } const stg{insn}; | ||||||
|  |  | ||||||
|  |     const IR::U64 address{[&]() -> IR::U64 { | ||||||
|  |         if (stg.e == 0) { | ||||||
|  |             // STG without .E uses a 32-bit pointer, zero-extend it | ||||||
|  |             return ir.ConvertU(64, X(stg.addr_reg)); | ||||||
|  |         } | ||||||
|  |         if (!IR::IsAligned(stg.addr_reg, 2)) { | ||||||
|  |             throw NotImplementedException("Unaligned address register"); | ||||||
|  |         } | ||||||
|  |         // Pack two registers to build the 32-bit address | ||||||
|  |         return ir.PackUint2x32(ir.CompositeConstruct(X(stg.addr_reg), X(stg.addr_reg + 1))); | ||||||
|  |     }()}; | ||||||
|  |  | ||||||
|  |     switch (stg.size) { | ||||||
|  |     case StoreSize::U8: | ||||||
|  |         ir.WriteGlobalU8(address, X(stg.data_reg)); | ||||||
|  |         break; | ||||||
|  |     case StoreSize::S8: | ||||||
|  |         ir.WriteGlobalS8(address, X(stg.data_reg)); | ||||||
|  |         break; | ||||||
|  |     case StoreSize::U16: | ||||||
|  |         ir.WriteGlobalU16(address, X(stg.data_reg)); | ||||||
|  |         break; | ||||||
|  |     case StoreSize::S16: | ||||||
|  |         ir.WriteGlobalS16(address, X(stg.data_reg)); | ||||||
|  |         break; | ||||||
|  |     case StoreSize::B32: | ||||||
|  |         ir.WriteGlobal32(address, X(stg.data_reg)); | ||||||
|  |         break; | ||||||
|  |     case StoreSize::B64: { | ||||||
|  |         if (!IR::IsAligned(stg.data_reg, 2)) { | ||||||
|  |             throw NotImplementedException("Unaligned data registers"); | ||||||
|  |         } | ||||||
|  |         const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1))}; | ||||||
|  |         ir.WriteGlobal64(address, vector); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case StoreSize::B128: | ||||||
|  |         if (!IR::IsAligned(stg.data_reg, 4)) { | ||||||
|  |             throw NotImplementedException("Unaligned data registers"); | ||||||
|  |         } | ||||||
|  |         const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1), | ||||||
|  |                                                      X(stg.data_reg + 2), X(stg.data_reg + 3))}; | ||||||
|  |         ir.WriteGlobal128(address, vector); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/bit_field.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/opcode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  | namespace { | ||||||
|  | union MOV { | ||||||
|  |     u64 raw; | ||||||
|  |     BitField<0, 8, IR::Reg> dest_reg; | ||||||
|  |     BitField<20, 8, IR::Reg> src_reg; | ||||||
|  |     BitField<39, 4, u64> mask; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void CheckMask(MOV mov) { | ||||||
|  |     if (mov.mask != 0xf) { | ||||||
|  |         throw NotImplementedException("Non-full move mask"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::MOV_reg(u64 insn) { | ||||||
|  |     const MOV mov{insn}; | ||||||
|  |     CheckMask(mov); | ||||||
|  |     X(mov.dest_reg, X(mov.src_reg)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::MOV_cbuf(u64 insn) { | ||||||
|  |     const MOV mov{insn}; | ||||||
|  |     CheckMask(mov); | ||||||
|  |     X(mov.dest_reg, GetCbuf(insn)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TranslatorVisitor::MOV_imm(u64 insn) { | ||||||
|  |     const MOV mov{insn}; | ||||||
|  |     CheckMask(mov); | ||||||
|  |     X(mov.dest_reg, GetImm(insn)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/decode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/location.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | template <auto visitor_method> | ||||||
|  | static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) { | ||||||
|  |     using MethodType = decltype(visitor_method); | ||||||
|  |     if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, Location, u64>) { | ||||||
|  |         (visitor.*visitor_method)(pc, insn); | ||||||
|  |     } else if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, u64>) { | ||||||
|  |         (visitor.*visitor_method)(insn); | ||||||
|  |     } else { | ||||||
|  |         (visitor.*visitor_method)(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | IR::Block Translate(Environment& env, const Flow::Block& flow_block) { | ||||||
|  |     IR::Block block{flow_block.begin.Offset(), flow_block.end.Offset()}; | ||||||
|  |     TranslatorVisitor visitor{env, block}; | ||||||
|  |  | ||||||
|  |     const Location pc_end{flow_block.end}; | ||||||
|  |     Location pc{flow_block.begin}; | ||||||
|  |     while (pc != pc_end) { | ||||||
|  |         const u64 insn{env.ReadInstruction(pc.Offset())}; | ||||||
|  |         const Opcode opcode{Decode(insn)}; | ||||||
|  |         switch (opcode) { | ||||||
|  | #define INST(name, cute, mask)                                                                     \ | ||||||
|  |     case Opcode::name:                                                                             \ | ||||||
|  |         Invoke<&TranslatorVisitor::name>(visitor, pc, insn);                                       \ | ||||||
|  |         break; | ||||||
|  | #include "shader_recompiler/frontend/maxwell/maxwell.inc" | ||||||
|  | #undef OPCODE | ||||||
|  |         default: | ||||||
|  |             throw LogicError("Invalid opcode {}", opcode); | ||||||
|  |         } | ||||||
|  |         ++pc; | ||||||
|  |     } | ||||||
|  |     return block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										16
									
								
								src/shader_recompiler/frontend/maxwell/translate/translate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/shader_recompiler/frontend/maxwell/translate/translate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/location.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Maxwell { | ||||||
|  |  | ||||||
|  | [[nodiscard]] IR::Block Translate(Environment& env, const Flow::Block& flow_block); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Maxwell | ||||||
							
								
								
									
										23
									
								
								src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <ranges> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/ir_opt/passes.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Optimization { | ||||||
|  |  | ||||||
|  | void DeadCodeEliminationPass(IR::Block& block) { | ||||||
|  |     // We iterate over the instructions in reverse order. | ||||||
|  |     // This is because removing an instruction reduces the number of uses for earlier instructions. | ||||||
|  |     for (IR::Inst& inst : std::views::reverse(block)) { | ||||||
|  |         if (!inst.HasUses() && !inst.MayHaveSideEffects()) { | ||||||
|  |             inst.Invalidate(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Optimization | ||||||
							
								
								
									
										87
									
								
								src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/ir_opt/passes.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Optimization { | ||||||
|  | namespace { | ||||||
|  | using Iterator = IR::Block::iterator; | ||||||
|  |  | ||||||
|  | enum class TrackingType { | ||||||
|  |     Reg, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct RegisterInfo { | ||||||
|  |     IR::Value register_value; | ||||||
|  |     TrackingType tracking_type; | ||||||
|  |     Iterator last_set_instruction; | ||||||
|  |     bool set_instruction_present = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | void DoSet(IR::Block& block, RegisterInfo& info, IR::Value value, Iterator set_inst, | ||||||
|  |            TrackingType tracking_type) { | ||||||
|  |     if (info.set_instruction_present) { | ||||||
|  |         info.last_set_instruction->Invalidate(); | ||||||
|  |         block.Instructions().erase(info.last_set_instruction); | ||||||
|  |     } | ||||||
|  |     info.register_value = value; | ||||||
|  |     info.tracking_type = tracking_type; | ||||||
|  |     info.set_instruction_present = true; | ||||||
|  |     info.last_set_instruction = set_inst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RegisterInfo Nothing(Iterator get_inst, TrackingType tracking_type) { | ||||||
|  |     RegisterInfo info{}; | ||||||
|  |     info.register_value = IR::Value{&*get_inst}; | ||||||
|  |     info.tracking_type = tracking_type; | ||||||
|  |     return info; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void DoGet(RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) { | ||||||
|  |     if (info.register_value.IsEmpty()) { | ||||||
|  |         info = Nothing(get_inst, tracking_type); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (info.tracking_type == tracking_type) { | ||||||
|  |         get_inst->ReplaceUsesWith(info.register_value); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     info = Nothing(get_inst, tracking_type); | ||||||
|  | } | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | void GetSetElimination(IR::Block& block) { | ||||||
|  |     std::array<RegisterInfo, 255> reg_info; | ||||||
|  |  | ||||||
|  |     for (Iterator inst = block.begin(); inst != block.end(); ++inst) { | ||||||
|  |         switch (inst->Opcode()) { | ||||||
|  |         case IR::Opcode::GetRegister: { | ||||||
|  |             const IR::Reg reg{inst->Arg(0).Reg()}; | ||||||
|  |             if (reg == IR::Reg::RZ) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             const size_t index{static_cast<size_t>(reg)}; | ||||||
|  |             DoGet(reg_info.at(index), inst, TrackingType::Reg); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case IR::Opcode::SetRegister: { | ||||||
|  |             const IR::Reg reg{inst->Arg(0).Reg()}; | ||||||
|  |             if (reg == IR::Reg::RZ) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             const size_t index{static_cast<size_t>(reg)}; | ||||||
|  |             DoSet(block, reg_info.at(index), inst->Arg(1), inst, TrackingType::Reg); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Optimization | ||||||
							
								
								
									
										37
									
								
								src/shader_recompiler/ir_opt/identity_removal_pass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/shader_recompiler/ir_opt/identity_removal_pass.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/ir_opt/passes.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Optimization { | ||||||
|  |  | ||||||
|  | void IdentityRemovalPass(IR::Block& block) { | ||||||
|  |     std::vector<IR::Inst*> to_invalidate; | ||||||
|  |  | ||||||
|  |     for (auto inst = block.begin(); inst != block.end();) { | ||||||
|  |         const size_t num_args{inst->NumArgs()}; | ||||||
|  |         for (size_t i = 0; i < num_args; ++i) { | ||||||
|  |             IR::Value arg; | ||||||
|  |             while ((arg = inst->Arg(i)).IsIdentity()) { | ||||||
|  |                 inst->SetArg(i, arg.Inst()->Arg(0)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (inst->Opcode() == IR::Opcode::Identity || inst->Opcode() == IR::Opcode::Void) { | ||||||
|  |             to_invalidate.push_back(&*inst); | ||||||
|  |             inst = block.Instructions().erase(inst); | ||||||
|  |         } else { | ||||||
|  |             ++inst; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (IR::Inst* const inst : to_invalidate) { | ||||||
|  |         inst->Invalidate(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Optimization | ||||||
							
								
								
									
										16
									
								
								src/shader_recompiler/ir_opt/passes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/shader_recompiler/ir_opt/passes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Optimization { | ||||||
|  |  | ||||||
|  | void DeadCodeEliminationPass(IR::Block& block); | ||||||
|  | void GetSetElimination(IR::Block& block); | ||||||
|  | void IdentityRemovalPass(IR::Block& block); | ||||||
|  | void VerificationPass(const IR::Block& block); | ||||||
|  |  | ||||||
|  | } // namespace Shader::Optimization | ||||||
							
								
								
									
										50
									
								
								src/shader_recompiler/ir_opt/verification_pass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/shader_recompiler/ir_opt/verification_pass.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/exception.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||||||
|  | #include "shader_recompiler/ir_opt/passes.h" | ||||||
|  |  | ||||||
|  | namespace Shader::Optimization { | ||||||
|  |  | ||||||
|  | static void ValidateTypes(const IR::Block& block) { | ||||||
|  |     for (const IR::Inst& inst : block) { | ||||||
|  |         const size_t num_args{inst.NumArgs()}; | ||||||
|  |         for (size_t i = 0; i < num_args; ++i) { | ||||||
|  |             const IR::Type t1{inst.Arg(i).Type()}; | ||||||
|  |             const IR::Type t2{IR::ArgTypeOf(inst.Opcode(), i)}; | ||||||
|  |             if (!IR::AreTypesCompatible(t1, t2)) { | ||||||
|  |                 throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(block)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void ValidateUses(const IR::Block& block) { | ||||||
|  |     std::map<IR::Inst*, int> actual_uses; | ||||||
|  |     for (const IR::Inst& inst : block) { | ||||||
|  |         const size_t num_args{inst.NumArgs()}; | ||||||
|  |         for (size_t i = 0; i < num_args; ++i) { | ||||||
|  |             const IR::Value arg{inst.Arg(i)}; | ||||||
|  |             if (!arg.IsImmediate()) { | ||||||
|  |                 ++actual_uses[arg.Inst()]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     for (const auto [inst, uses] : actual_uses) { | ||||||
|  |         if (inst->UseCount() != uses) { | ||||||
|  |             throw LogicError("Invalid uses in block:\n{}", IR::DumpBlock(block)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VerificationPass(const IR::Block& block) { | ||||||
|  |     ValidateTypes(block); | ||||||
|  |     ValidateUses(block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Shader::Optimization | ||||||
							
								
								
									
										60
									
								
								src/shader_recompiler/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/shader_recompiler/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | // Copyright 2021 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <filesystem> | ||||||
|  |  | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "shader_recompiler/file_environment.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/basic_block.h" | ||||||
|  | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/decode.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/location.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/program.h" | ||||||
|  | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | ||||||
|  |  | ||||||
|  | using namespace Shader; | ||||||
|  | using namespace Shader::Maxwell; | ||||||
|  |  | ||||||
|  | template <typename Func> | ||||||
|  | static void ForEachFile(const std::filesystem::path& path, Func&& func) { | ||||||
|  |     std::filesystem::directory_iterator end; | ||||||
|  |     for (std::filesystem::directory_iterator it{path}; it != end; ++it) { | ||||||
|  |         if (std::filesystem::is_directory(*it)) { | ||||||
|  |             ForEachFile(*it, func); | ||||||
|  |         } else { | ||||||
|  |             func(*it); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void RunDatabase() { | ||||||
|  |     std::vector<std::unique_ptr<FileEnvironment>> map; | ||||||
|  |     ForEachFile("D:\\Shaders\\Database", [&](const std::filesystem::path& path) { | ||||||
|  |         map.emplace_back(std::make_unique<FileEnvironment>(path.string().c_str())); | ||||||
|  |     }); | ||||||
|  |     for (int i = 0; i < 1; ++i) { | ||||||
|  |         for (auto& env : map) { | ||||||
|  |             // fmt::print(stdout, "Decoding {}\n", path.string()); | ||||||
|  |             const Location start_address{0}; | ||||||
|  |             auto cfg{std::make_unique<Flow::CFG>(*env, start_address)}; | ||||||
|  |             // fmt::print(stdout, "{}\n", cfg.Dot()); | ||||||
|  |             // IR::Program program{env, cfg}; | ||||||
|  |             // Optimize(program); | ||||||
|  |             // const std::string code{EmitGLASM(program)}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main() { | ||||||
|  |     // RunDatabase(); | ||||||
|  |  | ||||||
|  |     FileEnvironment env{"D:\\Shaders\\Database\\test.bin"}; | ||||||
|  |     auto cfg{std::make_unique<Flow::CFG>(env, 0)}; | ||||||
|  |     // fmt::print(stdout, "{}\n", cfg->Dot()); | ||||||
|  |  | ||||||
|  |     Program program{env, *cfg}; | ||||||
|  |     fmt::print(stdout, "{}\n", DumpProgram(program)); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user