mirror of https://github.com/yuzu-mirror/yuzu
shader: Initial instruction support
parent
6c4cc0cd06
commit
d24a16045f
@ -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
|
Loading…
Reference in New Issue