committed by
ameerj
57 changed files with 7061 additions and 0 deletions
-
1src/CMakeLists.txt
-
86src/shader_recompiler/CMakeLists.txt
-
14src/shader_recompiler/environment.h
-
42src/shader_recompiler/exception.h
-
42src/shader_recompiler/file_environment.cpp
-
21src/shader_recompiler/file_environment.h
-
447src/shader_recompiler/frontend/ir/attribute.cpp
-
242src/shader_recompiler/frontend/ir/attribute.h
-
142src/shader_recompiler/frontend/ir/basic_block.cpp
-
134src/shader_recompiler/frontend/ir/basic_block.h
-
31src/shader_recompiler/frontend/ir/condition.cpp
-
60src/shader_recompiler/frontend/ir/condition.h
-
83src/shader_recompiler/frontend/ir/flow_test.cpp
-
61src/shader_recompiler/frontend/ir/flow_test.h
-
533src/shader_recompiler/frontend/ir/ir_emitter.cpp
-
123src/shader_recompiler/frontend/ir/ir_emitter.h
-
189src/shader_recompiler/frontend/ir/microinstruction.cpp
-
82src/shader_recompiler/frontend/ir/microinstruction.h
-
67src/shader_recompiler/frontend/ir/opcode.cpp
-
44src/shader_recompiler/frontend/ir/opcode.h
-
142src/shader_recompiler/frontend/ir/opcode.inc
-
28src/shader_recompiler/frontend/ir/pred.h
-
314src/shader_recompiler/frontend/ir/reg.h
-
36src/shader_recompiler/frontend/ir/type.cpp
-
47src/shader_recompiler/frontend/ir/type.h
-
124src/shader_recompiler/frontend/ir/value.cpp
-
98src/shader_recompiler/frontend/ir/value.h
-
531src/shader_recompiler/frontend/maxwell/control_flow.cpp
-
137src/shader_recompiler/frontend/maxwell/control_flow.h
-
149src/shader_recompiler/frontend/maxwell/decode.cpp
-
14src/shader_recompiler/frontend/maxwell/decode.h
-
62src/shader_recompiler/frontend/maxwell/instruction.h
-
106src/shader_recompiler/frontend/maxwell/location.h
-
285src/shader_recompiler/frontend/maxwell/maxwell.inc
-
26src/shader_recompiler/frontend/maxwell/opcode.cpp
-
30src/shader_recompiler/frontend/maxwell/opcode.h
-
69src/shader_recompiler/frontend/maxwell/program.cpp
-
39src/shader_recompiler/frontend/maxwell/program.h
-
79src/shader_recompiler/frontend/maxwell/termination_code.cpp
-
16src/shader_recompiler/frontend/maxwell/termination_code.h
-
15src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp
-
133src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
-
71src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
-
79src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
-
316src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
-
92src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
-
90src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
-
1105src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
-
45src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp
-
50src/shader_recompiler/frontend/maxwell/translate/translate.cpp
-
16src/shader_recompiler/frontend/maxwell/translate/translate.h
-
23src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
-
87src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
-
37src/shader_recompiler/ir_opt/identity_removal_pass.cpp
-
16src/shader_recompiler/ir_opt/passes.h
-
50src/shader_recompiler/ir_opt/verification_pass.cpp
-
60src/shader_recompiler/main.cpp
@ -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) |
||||
@ -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 |
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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, ) |
||||
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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 |
||||
@ -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()); |
||||
|
} |
||||
|
}; |
||||
@ -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") |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
|
}; |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
1105
src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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)); |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue