committed by
ameerj
28 changed files with 1450 additions and 334 deletions
-
13src/shader_recompiler/CMakeLists.txt
-
4src/shader_recompiler/frontend/ir/basic_block.cpp
-
2src/shader_recompiler/frontend/ir/basic_block.h
-
200src/shader_recompiler/frontend/ir/ir_emitter.cpp
-
67src/shader_recompiler/frontend/ir/ir_emitter.h
-
12src/shader_recompiler/frontend/ir/microinstruction.h
-
28src/shader_recompiler/frontend/ir/modifiers.h
-
139src/shader_recompiler/frontend/ir/opcode.inc
-
11src/shader_recompiler/frontend/ir/pred.h
-
1src/shader_recompiler/frontend/maxwell/program.cpp
-
56src/shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h
-
71src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_add.cpp
-
73src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_fused_multiply_add.cpp
-
108src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multiply.cpp
-
26src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
-
9src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
-
106src/shader_recompiler/frontend/maxwell/translate/impl/integer_add.cpp
-
73src/shader_recompiler/frontend/maxwell/translate/impl/integer_scaled_add.cpp
-
99src/shader_recompiler/frontend/maxwell/translate/impl/integer_set_predicate.cpp
-
71src/shader_recompiler/frontend/maxwell/translate/impl/integer_shift_left.cpp
-
110src/shader_recompiler/frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
-
149src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
-
2src/shader_recompiler/frontend/maxwell/translate/impl/move_register.cpp
-
114src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
-
149src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
-
87src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
-
1src/shader_recompiler/ir_opt/passes.h
-
3src/shader_recompiler/main.cpp
@ -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 |
|||
|
|||
namespace Shader::IR { |
|||
|
|||
enum class FmzMode { |
|||
None, // Denorms are not flushed, NAN is propagated (nouveau) |
|||
FTZ, // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK) |
|||
FMZ, // Flush denorms to zero, x * 0 == 0 (D3D9) |
|||
}; |
|||
|
|||
enum class FpRounding { |
|||
RN, // Round to nearest even, |
|||
RM, // Round towards negative infinity |
|||
RP, // Round towards positive infinity |
|||
RZ, // Round towards zero |
|||
}; |
|||
|
|||
struct FpControl { |
|||
bool no_contraction{false}; |
|||
FpRounding rounding : 8 = FpRounding::RN; |
|||
FmzMode fmz_mode : 8 = FmzMode::FTZ; |
|||
}; |
|||
static_assert(sizeof(FpControl) <= sizeof(u64)); |
|||
} // namespace Shader::IR |
|||
@ -0,0 +1,56 @@ |
|||
// 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/modifiers.h" |
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h" |
|||
|
|||
namespace Shader::Maxwell { |
|||
|
|||
enum class FpRounding : u64 { |
|||
RN, |
|||
RM, |
|||
RP, |
|||
RZ, |
|||
}; |
|||
|
|||
enum class FmzMode : u64 { |
|||
None, |
|||
FTZ, |
|||
FMZ, |
|||
INVALIDFMZ3, |
|||
}; |
|||
|
|||
inline IR::FpRounding CastFpRounding(FpRounding fp_rounding) { |
|||
switch (fp_rounding) { |
|||
case FpRounding::RN: |
|||
return IR::FpRounding::RN; |
|||
case FpRounding::RM: |
|||
return IR::FpRounding::RM; |
|||
case FpRounding::RP: |
|||
return IR::FpRounding::RP; |
|||
case FpRounding::RZ: |
|||
return IR::FpRounding::RZ; |
|||
} |
|||
throw NotImplementedException("Invalid floating-point rounding {}", fp_rounding); |
|||
} |
|||
|
|||
inline IR::FmzMode CastFmzMode(FmzMode fmz_mode) { |
|||
switch (fmz_mode) { |
|||
case FmzMode::None: |
|||
return IR::FmzMode::None; |
|||
case FmzMode::FTZ: |
|||
return IR::FmzMode::FTZ; |
|||
case FmzMode::FMZ: |
|||
return IR::FmzMode::FMZ; |
|||
case FmzMode::INVALIDFMZ3: |
|||
break; |
|||
} |
|||
throw NotImplementedException("Invalid FMZ mode {}", fmz_mode); |
|||
} |
|||
|
|||
} // 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/common_types.h"
|
|||
#include "shader_recompiler/exception.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
|
|||
void FADD(TranslatorVisitor& v, u64 insn, bool sat, bool cc, bool ftz, FpRounding fp_rounding, |
|||
const IR::U32& src_b, bool abs_a, bool neg_a, bool abs_b, bool neg_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_a; |
|||
} const fadd{insn}; |
|||
|
|||
if (sat) { |
|||
throw NotImplementedException("FADD SAT"); |
|||
} |
|||
if (cc) { |
|||
throw NotImplementedException("FADD CC"); |
|||
} |
|||
const IR::U32 op_a{v.ir.FPAbsNeg(v.X(fadd.src_a), abs_a, neg_a)}; |
|||
const IR::U32 op_b{v.ir.FPAbsNeg(src_b, abs_b, neg_b)}; |
|||
IR::FpControl control{ |
|||
.no_contraction{true}, |
|||
.rounding{CastFpRounding(fp_rounding)}, |
|||
.fmz_mode{ftz ? IR::FmzMode::FTZ : IR::FmzMode::None}, |
|||
}; |
|||
v.X(fadd.dest_reg, v.ir.FPAdd(op_a, op_b, control)); |
|||
} |
|||
|
|||
void FADD(TranslatorVisitor& v, u64 insn, const IR::U32& src_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<39, 2, FpRounding> fp_rounding; |
|||
BitField<44, 1, u64> ftz; |
|||
BitField<45, 1, u64> neg_b; |
|||
BitField<46, 1, u64> abs_a; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 1, u64> neg_a; |
|||
BitField<49, 1, u64> abs_b; |
|||
BitField<50, 1, u64> sat; |
|||
} const fadd{insn}; |
|||
|
|||
FADD(v, insn, fadd.sat != 0, fadd.cc != 0, fadd.ftz != 0, fadd.fp_rounding, src_b, |
|||
fadd.abs_a != 0, fadd.neg_a != 0, fadd.abs_b != 0, fadd.neg_b != 0); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::FADD_reg(u64 insn) { |
|||
FADD(*this, insn, GetReg20(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::FADD_cbuf(u64) { |
|||
throw NotImplementedException("FADD (cbuf)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FADD_imm(u64) { |
|||
throw NotImplementedException("FADD (imm)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FADD32I(u64) { |
|||
throw NotImplementedException("FADD32I"); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,73 @@ |
|||
// 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/common_encoding.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
void FFMA(TranslatorVisitor& v, u64 insn, const IR::U32& src_b, const IR::U32& src_c, bool neg_a, |
|||
bool neg_b, bool neg_c, bool sat, bool cc, FmzMode fmz_mode, FpRounding fp_rounding) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_a; |
|||
} const ffma{insn}; |
|||
|
|||
if (sat) { |
|||
throw NotImplementedException("FFMA SAT"); |
|||
} |
|||
if (cc) { |
|||
throw NotImplementedException("FFMA CC"); |
|||
} |
|||
const IR::U32 op_a{v.ir.FPAbsNeg(v.X(ffma.src_a), false, neg_a)}; |
|||
const IR::U32 op_b{v.ir.FPAbsNeg(src_b, false, neg_b)}; |
|||
const IR::U32 op_c{v.ir.FPAbsNeg(src_c, false, neg_c)}; |
|||
const IR::FpControl fp_control{ |
|||
.no_contraction{true}, |
|||
.rounding{CastFpRounding(fp_rounding)}, |
|||
.fmz_mode{CastFmzMode(fmz_mode)}, |
|||
}; |
|||
v.X(ffma.dest_reg, v.ir.FPFma(op_a, op_b, op_c, fp_control)); |
|||
} |
|||
|
|||
void FFMA(TranslatorVisitor& v, u64 insn, const IR::U32& src_b, const IR::U32& src_c) { |
|||
union { |
|||
u64 raw; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 1, u64> neg_b; |
|||
BitField<49, 1, u64> neg_c; |
|||
BitField<50, 1, u64> sat; |
|||
BitField<51, 2, FpRounding> fp_rounding; |
|||
BitField<53, 2, FmzMode> fmz_mode; |
|||
} const ffma{insn}; |
|||
|
|||
FFMA(v, insn, src_b, src_c, false, ffma.neg_b != 0, ffma.neg_c != 0, ffma.sat != 0, |
|||
ffma.cc != 0, ffma.fmz_mode, ffma.fp_rounding); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::FFMA_reg(u64 insn) { |
|||
FFMA(*this, insn, GetReg20(insn), GetReg39(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::FFMA_rc(u64) { |
|||
throw NotImplementedException("FFMA (rc)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FFMA_cr(u64 insn) { |
|||
FFMA(*this, insn, GetCbuf(insn), GetReg39(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::FFMA_imm(u64) { |
|||
throw NotImplementedException("FFMA (imm)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FFMA32I(u64) { |
|||
throw NotImplementedException("FFMA32I"); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,108 @@ |
|||
// 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/frontend/ir/modifiers.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
enum class Scale : u64 { |
|||
None, |
|||
D2, |
|||
D4, |
|||
D8, |
|||
M8, |
|||
M4, |
|||
M2, |
|||
INVALIDSCALE37, |
|||
}; |
|||
|
|||
float ScaleFactor(Scale scale) { |
|||
switch (scale) { |
|||
case Scale::None: |
|||
return 1.0f; |
|||
case Scale::D2: |
|||
return 1.0f / 2.0f; |
|||
case Scale::D4: |
|||
return 1.0f / 4.0f; |
|||
case Scale::D8: |
|||
return 1.0f / 8.0f; |
|||
case Scale::M8: |
|||
return 8.0f; |
|||
case Scale::M4: |
|||
return 4.0f; |
|||
case Scale::M2: |
|||
return 2.0f; |
|||
case Scale::INVALIDSCALE37: |
|||
break; |
|||
} |
|||
throw NotImplementedException("Invalid FMUL scale {}", scale); |
|||
} |
|||
|
|||
void FMUL(TranslatorVisitor& v, u64 insn, const IR::U32& src_b, FmzMode fmz_mode, |
|||
FpRounding fp_rounding, Scale scale, bool sat, bool cc, bool neg_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_a; |
|||
} const fmul{insn}; |
|||
|
|||
if (cc) { |
|||
throw NotImplementedException("FMUL CC"); |
|||
} |
|||
if (sat) { |
|||
throw NotImplementedException("FMUL SAT"); |
|||
} |
|||
IR::U32 op_a{v.X(fmul.src_a)}; |
|||
if (scale != Scale::None) { |
|||
if (fmz_mode != FmzMode::FTZ || fp_rounding != FpRounding::RN) { |
|||
throw NotImplementedException("FMUL scale with non-FMZ or non-RN modifiers"); |
|||
} |
|||
op_a = v.ir.FPMul(op_a, v.ir.Imm32(ScaleFactor(scale))); |
|||
} |
|||
const IR::U32 op_b{v.ir.FPAbsNeg(src_b, false, neg_b)}; |
|||
const IR::FpControl fp_control{ |
|||
.no_contraction{true}, |
|||
.rounding{CastFpRounding(fp_rounding)}, |
|||
.fmz_mode{CastFmzMode(fmz_mode)}, |
|||
}; |
|||
v.X(fmul.dest_reg, v.ir.FPMul(op_a, op_b, fp_control)); |
|||
} |
|||
|
|||
void FMUL(TranslatorVisitor& v, u64 insn, const IR::U32& src_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<39, 2, FpRounding> fp_rounding; |
|||
BitField<41, 3, Scale> scale; |
|||
BitField<44, 2, FmzMode> fmz; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 1, u64> neg_b; |
|||
BitField<50, 1, u64> sat; |
|||
} fmul{insn}; |
|||
|
|||
FMUL(v, insn, src_b, fmul.fmz, fmul.fp_rounding, fmul.scale, fmul.sat != 0, fmul.cc != 0, |
|||
fmul.neg_b != 0); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::FMUL_reg(u64 insn) { |
|||
return FMUL(*this, insn, GetReg20(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::FMUL_cbuf(u64) { |
|||
throw NotImplementedException("FMUL (cbuf)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FMUL_imm(u64) { |
|||
throw NotImplementedException("FMUL (imm)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::FMUL32I(u64) { |
|||
throw NotImplementedException("FMUL32I"); |
|||
} |
|||
|
|||
} // 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.
|
|||
|
|||
#include "common/bit_field.h"
|
|||
#include "common/common_types.h"
|
|||
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
void IADD(TranslatorVisitor& v, u64 insn, const IR::U32 op_b, bool neg_a, bool po, bool sat, bool x, |
|||
bool cc) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_a; |
|||
} const iadd{insn}; |
|||
|
|||
if (sat) { |
|||
throw NotImplementedException("IADD SAT"); |
|||
} |
|||
if (x && po) { |
|||
throw NotImplementedException("IADD X+PO"); |
|||
} |
|||
// Operand A is always read from here, negated if needed
|
|||
IR::U32 op_a{v.X(iadd.src_a)}; |
|||
if (neg_a) { |
|||
op_a = v.ir.INeg(op_a); |
|||
} |
|||
// Add both operands
|
|||
IR::U32 result{v.ir.IAdd(op_a, op_b)}; |
|||
if (x) { |
|||
const IR::U32 carry{v.ir.Select(v.ir.GetCFlag(), v.ir.Imm32(1), v.ir.Imm32(0))}; |
|||
result = v.ir.IAdd(result, carry); |
|||
} |
|||
if (po) { |
|||
// .PO adds one to the result
|
|||
result = v.ir.IAdd(result, v.ir.Imm32(1)); |
|||
} |
|||
if (cc) { |
|||
// Store flags
|
|||
// TODO: Does this grab the result pre-PO or after?
|
|||
if (po) { |
|||
throw NotImplementedException("IADD CC+PO"); |
|||
} |
|||
// TODO: How does CC behave when X is set?
|
|||
if (x) { |
|||
throw NotImplementedException("IADD X+CC"); |
|||
} |
|||
v.SetZFlag(v.ir.GetZeroFromOp(result)); |
|||
v.SetSFlag(v.ir.GetSignFromOp(result)); |
|||
v.SetCFlag(v.ir.GetCarryFromOp(result)); |
|||
v.SetOFlag(v.ir.GetOverflowFromOp(result)); |
|||
} |
|||
// Store result
|
|||
v.X(iadd.dest_reg, result); |
|||
} |
|||
|
|||
void IADD(TranslatorVisitor& v, u64 insn, IR::U32 op_b) { |
|||
union { |
|||
u64 insn; |
|||
BitField<43, 1, u64> x; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 2, u64> three_for_po; |
|||
BitField<48, 1, u64> neg_b; |
|||
BitField<49, 1, u64> neg_a; |
|||
BitField<50, 1, u64> sat; |
|||
} const iadd{insn}; |
|||
|
|||
const bool po{iadd.three_for_po == 3}; |
|||
const bool neg_a{!po && iadd.neg_a != 0}; |
|||
if (!po && iadd.neg_b != 0) { |
|||
op_b = v.ir.INeg(op_b); |
|||
} |
|||
IADD(v, insn, op_b, iadd.neg_a != 0, po, iadd.sat != 0, iadd.x != 0, iadd.cc != 0); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::IADD_reg(u64) { |
|||
throw NotImplementedException("IADD (reg)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::IADD_cbuf(u64 insn) { |
|||
IADD(*this, insn, GetCbuf(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::IADD_imm(u64) { |
|||
throw NotImplementedException("IADD (imm)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::IADD32I(u64 insn) { |
|||
union { |
|||
u64 raw; |
|||
BitField<52, 1, u64> cc; |
|||
BitField<53, 1, u64> x; |
|||
BitField<54, 1, u64> sat; |
|||
BitField<55, 2, u64> three_for_po; |
|||
BitField<56, 1, u64> neg_a; |
|||
} const iadd32i{insn}; |
|||
|
|||
const bool po{iadd32i.three_for_po == 3}; |
|||
const bool neg_a{!po && iadd32i.neg_a != 0}; |
|||
IADD(*this, insn, GetImm32(insn), neg_a, po, iadd32i.sat != 0, iadd32i.x != 0, iadd32i.cc != 0); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,73 @@ |
|||
// 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/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
void ISCADD(TranslatorVisitor& v, u64 insn, IR::U32 op_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> op_a; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 2, u64> three_for_po; |
|||
BitField<48, 1, u64> neg_b; |
|||
BitField<49, 1, u64> neg_a; |
|||
BitField<39, 5, u64> scale; |
|||
} const iscadd{insn}; |
|||
|
|||
const bool po{iscadd.three_for_po == 3}; |
|||
IR::U32 op_a{v.X(iscadd.op_a)}; |
|||
if (!po) { |
|||
// When PO is not present, the bits are interpreted as negation
|
|||
if (iscadd.neg_a != 0) { |
|||
op_a = v.ir.INeg(op_a); |
|||
} |
|||
if (iscadd.neg_b != 0) { |
|||
op_b = v.ir.INeg(op_b); |
|||
} |
|||
} |
|||
// With the operands already processed, scale A
|
|||
const IR::U32 scale{v.ir.Imm32(static_cast<u32>(iscadd.scale))}; |
|||
const IR::U32 scaled_a{v.ir.ShiftLeftLogical(op_a, scale)}; |
|||
|
|||
IR::U32 result{v.ir.IAdd(scaled_a, op_b)}; |
|||
if (po) { |
|||
// .PO adds one to the final result
|
|||
result = v.ir.IAdd(result, v.ir.Imm32(1)); |
|||
} |
|||
v.X(iscadd.dest_reg, result); |
|||
|
|||
if (iscadd.cc != 0) { |
|||
throw NotImplementedException("ISCADD CC"); |
|||
} |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::ISCADD_reg(u64 insn) { |
|||
union { |
|||
u64 raw; |
|||
BitField<20, 8, IR::Reg> op_b; |
|||
} const iscadd{insn}; |
|||
|
|||
ISCADD(*this, insn, X(iscadd.op_b)); |
|||
} |
|||
|
|||
void TranslatorVisitor::ISCADD_cbuf(u64) { |
|||
throw NotImplementedException("ISCADD (cbuf)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::ISCADD_imm(u64) { |
|||
throw NotImplementedException("ISCADD (imm)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::ISCADD32I(u64) { |
|||
throw NotImplementedException("ISCADD32I"); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,99 @@ |
|||
// 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/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
enum class CompareOp : u64 { |
|||
F, // Always false
|
|||
LT, // Less than
|
|||
EQ, // Equal
|
|||
LE, // Less than or equal
|
|||
GT, // Greater than
|
|||
NE, // Not equal
|
|||
GE, // Greater than or equal
|
|||
T, // Always true
|
|||
}; |
|||
|
|||
enum class Bop : u64 { |
|||
AND, |
|||
OR, |
|||
XOR, |
|||
}; |
|||
|
|||
IR::U1 Compare(IR::IREmitter& ir, CompareOp op, const IR::U32& lhs, const IR::U32& rhs, |
|||
bool is_signed) { |
|||
switch (op) { |
|||
case CompareOp::F: |
|||
return ir.Imm1(false); |
|||
case CompareOp::LT: |
|||
return ir.ILessThan(lhs, rhs, is_signed); |
|||
case CompareOp::EQ: |
|||
return ir.IEqual(lhs, rhs); |
|||
case CompareOp::LE: |
|||
return ir.ILessThanEqual(lhs, rhs, is_signed); |
|||
case CompareOp::GT: |
|||
return ir.IGreaterThan(lhs, rhs, is_signed); |
|||
case CompareOp::NE: |
|||
return ir.INotEqual(lhs, rhs); |
|||
case CompareOp::GE: |
|||
return ir.IGreaterThanEqual(lhs, rhs, is_signed); |
|||
case CompareOp::T: |
|||
return ir.Imm1(true); |
|||
} |
|||
throw NotImplementedException("Invalid ISETP compare op {}", op); |
|||
} |
|||
|
|||
IR::U1 Combine(IR::IREmitter& ir, Bop bop, const IR::U1& comparison, const IR::U1& bop_pred) { |
|||
switch (bop) { |
|||
case Bop::AND: |
|||
return ir.LogicalAnd(comparison, bop_pred); |
|||
case Bop::OR: |
|||
return ir.LogicalOr(comparison, bop_pred); |
|||
case Bop::XOR: |
|||
return ir.LogicalXor(comparison, bop_pred); |
|||
} |
|||
throw NotImplementedException("Invalid ISETP bop {}", bop); |
|||
} |
|||
|
|||
void ISETP(TranslatorVisitor& v, u64 insn, const IR::U32& op_b) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 3, IR::Pred> dest_pred_b; |
|||
BitField<3, 3, IR::Pred> dest_pred_a; |
|||
BitField<8, 8, IR::Reg> src_reg_a; |
|||
BitField<39, 3, IR::Pred> bop_pred; |
|||
BitField<42, 1, u64> neg_bop_pred; |
|||
BitField<45, 2, Bop> bop; |
|||
BitField<48, 1, u64> is_signed; |
|||
BitField<49, 3, CompareOp> compare_op; |
|||
} const isetp{insn}; |
|||
|
|||
const Bop bop{isetp.bop}; |
|||
const IR::U32 op_a{v.X(isetp.src_reg_a)}; |
|||
const IR::U1 comparison{Compare(v.ir, isetp.compare_op, op_a, op_b, isetp.is_signed != 0)}; |
|||
const IR::U1 bop_pred{v.ir.GetPred(isetp.bop_pred, isetp.neg_bop_pred != 0)}; |
|||
const IR::U1 result_a{Combine(v.ir, bop, comparison, bop_pred)}; |
|||
const IR::U1 result_b{Combine(v.ir, bop, v.ir.LogicalNot(comparison), bop_pred)}; |
|||
v.ir.SetPred(isetp.dest_pred_a, result_a); |
|||
v.ir.SetPred(isetp.dest_pred_b, result_b); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::ISETP_reg(u64 insn) { |
|||
ISETP(*this, insn, GetReg20(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::ISETP_cbuf(u64 insn) { |
|||
ISETP(*this, insn, GetCbuf(insn)); |
|||
} |
|||
|
|||
void TranslatorVisitor::ISETP_imm(u64) { |
|||
throw NotImplementedException("ISETP_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/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
void SHL(TranslatorVisitor& v, u64 insn, const IR::U32& unsafe_shift) { |
|||
union { |
|||
u64 insn; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_reg_a; |
|||
BitField<39, 1, u64> w; |
|||
BitField<43, 1, u64> x; |
|||
BitField<47, 1, u64> cc; |
|||
} const shl{insn}; |
|||
|
|||
if (shl.x != 0) { |
|||
throw NotImplementedException("SHL.X"); |
|||
} |
|||
if (shl.cc != 0) { |
|||
throw NotImplementedException("SHL.CC"); |
|||
} |
|||
const IR::U32 base{v.X(shl.src_reg_a)}; |
|||
IR::U32 result; |
|||
if (shl.w != 0) { |
|||
// When .W is set, the shift value is wrapped
|
|||
// To emulate this we just have to clamp it ourselves.
|
|||
const IR::U32 shift{v.ir.BitwiseAnd(unsafe_shift, v.ir.Imm32(31))}; |
|||
result = v.ir.ShiftLeftLogical(base, shift); |
|||
} else { |
|||
// When .W is not set, the shift value is clamped between 0 and 32.
|
|||
// To emulate this we have to have in mind the special shift of 32, that evaluates as 0.
|
|||
// We can safely evaluate an out of bounds shift according to the SPIR-V specification:
|
|||
//
|
|||
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpShiftLeftLogical
|
|||
// "Shift is treated as unsigned. The resulting value is undefined if Shift is greater than
|
|||
// or equal to the bit width of the components of Base."
|
|||
//
|
|||
// And on the GLASM specification it is also safe to evaluate out of bounds:
|
|||
//
|
|||
// https://www.khronos.org/registry/OpenGL/extensions/NV/NV_gpu_program4.txt
|
|||
// "The results of a shift operation ("<<") are undefined if the value of the second operand
|
|||
// is negative, or greater than or equal to the number of bits in the first operand."
|
|||
//
|
|||
// Emphasis on undefined results in contrast to undefined behavior.
|
|||
//
|
|||
const IR::U1 is_safe{v.ir.ILessThan(unsafe_shift, v.ir.Imm32(32), false)}; |
|||
const IR::U32 unsafe_result{v.ir.ShiftLeftLogical(base, unsafe_shift)}; |
|||
result = v.ir.Select(is_safe, unsafe_result, v.ir.Imm32(0)); |
|||
} |
|||
v.X(shl.dest_reg, result); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::SHL_reg(u64) { |
|||
throw NotImplementedException("SHL_reg"); |
|||
} |
|||
|
|||
void TranslatorVisitor::SHL_cbuf(u64) { |
|||
throw NotImplementedException("SHL_cbuf"); |
|||
} |
|||
|
|||
void TranslatorVisitor::SHL_imm(u64 insn) { |
|||
SHL(*this, insn, GetImm20(insn)); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,110 @@ |
|||
// 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/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
enum class SelectMode : u64 { |
|||
Default, |
|||
CLO, |
|||
CHI, |
|||
CSFU, |
|||
CBCC, |
|||
}; |
|||
|
|||
enum class Half : u64 { |
|||
H0, // Least-significant bits (15:0)
|
|||
H1, // Most-significant bits (31:16)
|
|||
}; |
|||
|
|||
IR::U32 ExtractHalf(TranslatorVisitor& v, const IR::U32& src, Half half, bool is_signed) { |
|||
const IR::U32 offset{v.ir.Imm32(half == Half::H1 ? 16 : 0)}; |
|||
return v.ir.BitFieldExtract(src, offset, v.ir.Imm32(16), is_signed); |
|||
} |
|||
|
|||
void XMAD(TranslatorVisitor& v, u64 insn, const IR::U32& src_b, const IR::U32& src_c, |
|||
SelectMode select_mode, Half half_b, bool psl, bool mrg, bool x) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<8, 8, IR::Reg> src_reg_a; |
|||
BitField<47, 1, u64> cc; |
|||
BitField<48, 1, u64> is_a_signed; |
|||
BitField<49, 1, u64> is_b_signed; |
|||
BitField<53, 1, Half> half_a; |
|||
} const xmad{insn}; |
|||
|
|||
if (x) { |
|||
throw NotImplementedException("XMAD X"); |
|||
} |
|||
const IR::U32 op_a{ExtractHalf(v, v.X(xmad.src_reg_a), xmad.half_a, xmad.is_a_signed != 0)}; |
|||
const IR::U32 op_b{ExtractHalf(v, src_b, half_b, xmad.is_b_signed != 0)}; |
|||
|
|||
IR::U32 product{v.ir.IMul(op_a, op_b)}; |
|||
if (psl) { |
|||
// .PSL shifts the product 16 bits
|
|||
product = v.ir.ShiftLeftLogical(product, v.ir.Imm32(16)); |
|||
} |
|||
const IR::U32 op_c{[&]() -> IR::U32 { |
|||
switch (select_mode) { |
|||
case SelectMode::Default: |
|||
return src_c; |
|||
case SelectMode::CLO: |
|||
return ExtractHalf(v, src_c, Half::H0, false); |
|||
case SelectMode::CHI: |
|||
return ExtractHalf(v, src_c, Half::H1, false); |
|||
case SelectMode::CBCC: |
|||
return v.ir.IAdd(v.ir.ShiftLeftLogical(src_b, v.ir.Imm32(16)), src_b); |
|||
case SelectMode::CSFU: |
|||
throw NotImplementedException("XMAD CSFU"); |
|||
} |
|||
throw NotImplementedException("Invalid XMAD select mode {}", select_mode); |
|||
}()}; |
|||
IR::U32 result{v.ir.IAdd(product, op_c)}; |
|||
if (mrg) { |
|||
// .MRG inserts src_b [15:0] into result's [31:16].
|
|||
const IR::U32 lsb_b{ExtractHalf(v, src_b, Half::H0, false)}; |
|||
result = v.ir.BitFieldInsert(result, lsb_b, v.ir.Imm32(16), v.ir.Imm32(16)); |
|||
} |
|||
if (xmad.cc) { |
|||
throw NotImplementedException("XMAD CC"); |
|||
} |
|||
// Store result
|
|||
v.X(xmad.dest_reg, result); |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::XMAD_reg(u64) { |
|||
throw NotImplementedException("XMAD (reg)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::XMAD_rc(u64) { |
|||
throw NotImplementedException("XMAD (rc)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::XMAD_cr(u64) { |
|||
throw NotImplementedException("XMAD (cr)"); |
|||
} |
|||
|
|||
void TranslatorVisitor::XMAD_imm(u64 insn) { |
|||
union { |
|||
u64 raw; |
|||
BitField<20, 16, u64> src_b; |
|||
BitField<36, 1, u64> psl; |
|||
BitField<37, 1, u64> mrg; |
|||
BitField<38, 1, u64> x; |
|||
BitField<39, 8, IR::Reg> src_c; |
|||
BitField<50, 3, SelectMode> select_mode; |
|||
} const xmad{insn}; |
|||
|
|||
const IR::U32 src_b{ir.Imm32(static_cast<u32>(xmad.src_b))}; |
|||
const IR::U32 src_c{X(xmad.src_c)}; |
|||
XMAD(*this, insn, src_b, src_c, xmad.select_mode, Half::H0, xmad.psl != 0, xmad.mrg != 0, |
|||
xmad.x != 0); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -0,0 +1,114 @@ |
|||
// 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/frontend/maxwell/translate/impl/impl.h"
|
|||
|
|||
namespace Shader::Maxwell { |
|||
namespace { |
|||
enum class SpecialRegister : u64 { |
|||
SR_LANEID = 0, |
|||
SR_VIRTCFG = 2, |
|||
SR_VIRTID = 3, |
|||
SR_PM0 = 4, |
|||
SR_PM1 = 5, |
|||
SR_PM2 = 6, |
|||
SR_PM3 = 7, |
|||
SR_PM4 = 8, |
|||
SR_PM5 = 9, |
|||
SR_PM6 = 10, |
|||
SR_PM7 = 11, |
|||
SR_ORDERING_TICKET = 15, |
|||
SR_PRIM_TYPE = 16, |
|||
SR_INVOCATION_ID = 17, |
|||
SR_Y_DIRECTION = 18, |
|||
SR_THREAD_KILL = 19, |
|||
SM_SHADER_TYPE = 20, |
|||
SR_DIRECTCBEWRITEADDRESSLOW = 21, |
|||
SR_DIRECTCBEWRITEADDRESSHIGH = 22, |
|||
SR_DIRECTCBEWRITEENABLE = 23, |
|||
SR_MACHINE_ID_0 = 24, |
|||
SR_MACHINE_ID_1 = 25, |
|||
SR_MACHINE_ID_2 = 26, |
|||
SR_MACHINE_ID_3 = 27, |
|||
SR_AFFINITY = 28, |
|||
SR_INVOCATION_INFO = 29, |
|||
SR_WSCALEFACTOR_XY = 30, |
|||
SR_WSCALEFACTOR_Z = 31, |
|||
SR_TID = 32, |
|||
SR_TID_X = 33, |
|||
SR_TID_Y = 34, |
|||
SR_TID_Z = 35, |
|||
SR_CTAID_X = 37, |
|||
SR_CTAID_Y = 38, |
|||
SR_CTAID_Z = 39, |
|||
SR_NTID = 49, |
|||
SR_CirQueueIncrMinusOne = 50, |
|||
SR_NLATC = 51, |
|||
SR_SWINLO = 57, |
|||
SR_SWINSZ = 58, |
|||
SR_SMEMSZ = 59, |
|||
SR_SMEMBANKS = 60, |
|||
SR_LWINLO = 61, |
|||
SR_LWINSZ = 62, |
|||
SR_LMEMLOSZ = 63, |
|||
SR_LMEMHIOFF = 64, |
|||
SR_EQMASK = 65, |
|||
SR_LTMASK = 66, |
|||
SR_LEMASK = 67, |
|||
SR_GTMASK = 68, |
|||
SR_GEMASK = 69, |
|||
SR_REGALLOC = 70, |
|||
SR_GLOBALERRORSTATUS = 73, |
|||
SR_WARPERRORSTATUS = 75, |
|||
SR_PM_HI0 = 81, |
|||
SR_PM_HI1 = 82, |
|||
SR_PM_HI2 = 83, |
|||
SR_PM_HI3 = 84, |
|||
SR_PM_HI4 = 85, |
|||
SR_PM_HI5 = 86, |
|||
SR_PM_HI6 = 87, |
|||
SR_PM_HI7 = 88, |
|||
SR_CLOCKLO = 89, |
|||
SR_CLOCKHI = 90, |
|||
SR_GLOBALTIMERLO = 91, |
|||
SR_GLOBALTIMERHI = 92, |
|||
SR_HWTASKID = 105, |
|||
SR_CIRCULARQUEUEENTRYINDEX = 106, |
|||
SR_CIRCULARQUEUEENTRYADDRESSLOW = 107, |
|||
SR_CIRCULARQUEUEENTRYADDRESSHIGH = 108, |
|||
}; |
|||
|
|||
[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) { |
|||
switch (special_register) { |
|||
case SpecialRegister::SR_TID_X: |
|||
return ir.LocalInvocationIdX(); |
|||
case SpecialRegister::SR_TID_Y: |
|||
return ir.LocalInvocationIdY(); |
|||
case SpecialRegister::SR_TID_Z: |
|||
return ir.LocalInvocationIdZ(); |
|||
case SpecialRegister::SR_CTAID_X: |
|||
return ir.WorkgroupIdX(); |
|||
case SpecialRegister::SR_CTAID_Y: |
|||
return ir.WorkgroupIdY(); |
|||
case SpecialRegister::SR_CTAID_Z: |
|||
return ir.WorkgroupIdZ(); |
|||
default: |
|||
throw NotImplementedException("S2R special register {}", special_register); |
|||
} |
|||
} |
|||
} // Anonymous namespace
|
|||
|
|||
void TranslatorVisitor::S2R(u64 insn) { |
|||
union { |
|||
u64 raw; |
|||
BitField<0, 8, IR::Reg> dest_reg; |
|||
BitField<20, 8, SpecialRegister> src_reg; |
|||
} const s2r{insn}; |
|||
|
|||
X(s2r.dest_reg, Read(ir, s2r.src_reg)); |
|||
} |
|||
|
|||
} // namespace Shader::Maxwell
|
|||
@ -1,87 +0,0 @@ |
|||
// 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
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue