shader: Initial recompiler work
This commit is contained in:
		| @@ -142,6 +142,7 @@ add_subdirectory(core) | ||||
| add_subdirectory(audio_core) | ||||
| add_subdirectory(video_core) | ||||
| add_subdirectory(input_common) | ||||
| add_subdirectory(shader_recompiler) | ||||
| add_subdirectory(tests) | ||||
|  | ||||
| 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