Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PPU: restore previous NJ mode handling option #11379

Merged
merged 3 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 46 additions & 43 deletions rpcs3/Emu/Cell/PPUInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ enum class ppu_exec_bit : u64
has_rc,
set_sat,
use_nj,
fix_nj,
set_vnan,
fix_vnan,
set_fpcc,
Expand All @@ -73,7 +74,7 @@ struct ppu_exec_select
static ppu_intrp_func_t select(bs_t<ppu_exec_bit> selected, F func)
{
// Make sure there is no flag duplication, otherwise skip flag
if constexpr (((Flags0 != Flag) && ...))
if constexpr (((Flags0 != Flag) && ...) && (Flag != fix_vnan || ((Flags0 != set_vnan) && ...)) && (Flag != fix_nj || ((Flags0 != use_nj) && ...)))
{
// Test only relevant flags at runtime initialization (compile both variants)
if (selected & Flag)
Expand Down Expand Up @@ -766,10 +767,10 @@ inline v128 ppu_select_vnan(v128 a, v128 b, Vector128 auto... args)
}

// Flush denormals to zero if NJ is 1
template <ppu_exec_bit... Flags>
template <bool Result = false, ppu_exec_bit... Flags>
inline v128 ppu_flush_denormal(const v128& mask, const v128& a)
{
if constexpr (((Flags == use_nj) || ...))
if constexpr (((Flags == use_nj) || ...) || (Result && ((Flags == fix_nj) || ...)))
{
return gv_andn(gv_shr32(gv_eq32(mask & a, gv_bcst32(0)), 1), a);
}
Expand Down Expand Up @@ -826,14 +827,14 @@ template <u32 Build, ppu_exec_bit... Flags>
auto MTVSCR()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<set_sat, use_nj>();
return ppu_exec_select<Flags...>::template select<set_sat, use_nj, fix_nj>();

static const auto exec = [](auto&& sat, auto&& nj, auto&& jm_mask, auto&& b)
{
const u32 vscr = b._u32[3];
if constexpr (((Flags == set_sat) || ...))
sat._u = vscr & 1;
if constexpr (((Flags == use_nj) || ...))
if constexpr (((Flags == use_nj || Flags == fix_nj) || ...))
jm_mask = (vscr & 0x10000) ? 0x7f80'0000 : 0x7fff'ffff;
nj = (vscr & 0x10000) != 0;
};
Expand All @@ -860,14 +861,14 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VADDFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](auto&& d, auto&& a_, auto&& b_, auto&& jm_mask)
{
const auto m = gv_bcst32(jm_mask, &ppu_thread::jm_mask);
const auto a = ppu_flush_denormal<Flags...>(m, a_);
const auto b = ppu_flush_denormal<Flags...>(m, b_);
d = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(gv_addfs(a, b), a, b));
const auto a = ppu_flush_denormal<false, Flags...>(m, a_);
const auto b = ppu_flush_denormal<false, Flags...>(m, b_);
d = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(gv_addfs(a, b), a, b));
};

RETURN_(ppu.vr[op.vd], ppu.vr[op.va], ppu.vr[op.vb], ppu.jm_mask);
Expand Down Expand Up @@ -1508,15 +1509,15 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VMADDFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](auto&& d, auto&& a_, auto&& b_, auto&& c_, auto&& jm_mask)
{
const auto m = gv_bcst32(jm_mask, &ppu_thread::jm_mask);
const auto a = ppu_flush_denormal<Flags...>(m, a_);
const auto b = ppu_flush_denormal<Flags...>(m, b_);
const auto c = ppu_flush_denormal<Flags...>(m, c_);
d = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(gv_fmafs(a, c, b)));
const auto a = ppu_flush_denormal<false, Flags...>(m, a_);
const auto b = ppu_flush_denormal<false, Flags...>(m, b_);
const auto c = ppu_flush_denormal<false, Flags...>(m, c_);
d = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(gv_fmafs(a, c, b)));
};

RETURN_(ppu.vr[op.vd], ppu.vr[op.va], ppu.vr[op.vb], ppu.vr[op.vc], ppu.jm_mask);
Expand All @@ -1526,11 +1527,11 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VMAXFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](auto&& d, auto&& a, auto&& b, auto&& jm_mask)
{
d = ppu_flush_denormal<Flags...>(gv_bcst32(jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(gv_maxfs(a, b), a, b));
d = ppu_flush_denormal<true, Flags...>(gv_bcst32(jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(gv_maxfs(a, b), a, b));
};

RETURN_(ppu.vr[op.vd], ppu.vr[op.va], ppu.vr[op.vb], ppu.jm_mask);
Expand Down Expand Up @@ -1673,11 +1674,11 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VMINFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](auto&& d, auto&& a, auto&& b, auto&& jm_mask)
{
d = ppu_flush_denormal<Flags...>(gv_bcst32(jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(gv_minfs(a, b), a, b));
d = ppu_flush_denormal<true, Flags...>(gv_bcst32(jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(gv_minfs(a, b), a, b));
};

RETURN_(ppu.vr[op.vd], ppu.vr[op.va], ppu.vr[op.vb], ppu.jm_mask);
Expand Down Expand Up @@ -2087,17 +2088,17 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VNMSUBFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
// An odd case with (FLT_MIN, FLT_MIN, FLT_MIN) produces FLT_MIN instead of 0
const auto s = _mm_set1_ps(-0.0f);
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto a = ppu_flush_denormal<Flags...>(m, ppu.vr[op.va]);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto c = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vc]);
const auto a = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.va]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
const auto c = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vc]);
const auto r = _mm_xor_ps(gv_fmafs(a, c, _mm_xor_ps(b, s)), s);
ppu.vr[op.rd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(r));
ppu.vr[op.rd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(r));
};
RETURN_(ppu, op);
}
Expand Down Expand Up @@ -2315,14 +2316,14 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VREFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto a = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f);
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
const auto result = _mm_div_ps(a, b);
ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(result, a, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(result, a, b));
};
RETURN_(ppu, op);
}
Expand All @@ -2331,19 +2332,19 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRFIM()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
v128 d;

for (uint w = 0; w < 4; w++)
{
d._f[w] = std::floor(b._f[w]);
}

ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(d, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(d, b));
};
RETURN_(ppu, op);
}
Expand All @@ -2352,7 +2353,7 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRFIN()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto b = ppu.vr[op.vb];
Expand All @@ -2363,7 +2364,7 @@ auto VRFIN()
d._f[w] = std::nearbyint(b._f[w]);
}

ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(d, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(d, b));
};
RETURN_(ppu, op);
}
Expand All @@ -2372,19 +2373,19 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRFIP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
v128 d;

for (uint w = 0; w < 4; w++)
{
d._f[w] = std::ceil(b._f[w]);
}

ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(d, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(d, b));
};
RETURN_(ppu, op);
}
Expand All @@ -2393,7 +2394,7 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRFIZ()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto b = ppu.vr[op.vb];
Expand All @@ -2404,7 +2405,7 @@ auto VRFIZ()
d._f[w] = std::truncf(b._f[w]);
}

ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(d, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask), ppu_set_vnan<Flags...>(d, b));
};
RETURN_(ppu, op);
}
Expand Down Expand Up @@ -2470,14 +2471,14 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRSQRTEFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto a = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f);
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
const auto result = _mm_div_ps(a, _mm_sqrt_ps(b));
ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(result, a, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(result, a, b));
};
RETURN_(ppu, op);
}
Expand Down Expand Up @@ -2905,14 +2906,14 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VSUBFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<use_nj, set_vnan, fix_vnan>();
return ppu_exec_select<Flags...>::template select<use_nj, fix_nj, set_vnan, fix_vnan>();

static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
const auto m = gv_bcst32(ppu.jm_mask, &ppu_thread::jm_mask);
const auto a = ppu_flush_denormal<Flags...>(m, ppu.vr[op.va]);
const auto b = ppu_flush_denormal<Flags...>(m, ppu.vr[op.vb]);
const auto a = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.va]);
const auto b = ppu_flush_denormal<false, Flags...>(m, ppu.vr[op.vb]);
const auto r = gv_subfs(a, b);
ppu.vr[op.vd] = ppu_flush_denormal<Flags...>(m, ppu_set_vnan<Flags...>(r, a, b));
ppu.vr[op.vd] = ppu_flush_denormal<true, Flags...>(m, ppu_set_vnan<Flags...>(r, a, b));
};
RETURN_(ppu, op);
}
Expand Down Expand Up @@ -7583,6 +7584,8 @@ ppu_interpreter_rt_base::ppu_interpreter_rt_base() noexcept
selected += set_sat;
if (g_cfg.core.ppu_use_nj_bit)
selected += use_nj;
if (g_cfg.core.ppu_llvm_nj_fixup)
selected += fix_nj;
if (g_cfg.core.ppu_set_vnan)
selected += set_vnan;
if (g_cfg.core.ppu_fix_vnan)
Expand Down
11 changes: 7 additions & 4 deletions rpcs3/Emu/Cell/PPUThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3071,13 +3071,14 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
non_win32,
accurate_dfma,
fixup_vnan,
accurate_jm,
fixup_nj_denormals,
accurate_cache_line_stores,
reservations_128_byte,
greedy_mode,
accurate_sat,
accurate_fpcc,
accurate_vnan,
accurate_nj_mode,

__bitset_enum_max
};
Expand All @@ -3091,8 +3092,8 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
settings += ppu_settings::accurate_dfma;
if (g_cfg.core.ppu_fix_vnan)
settings += ppu_settings::fixup_vnan;
if (g_cfg.core.ppu_use_nj_bit)
settings += ppu_settings::accurate_jm;
if (g_cfg.core.ppu_llvm_nj_fixup)
settings += ppu_settings::fixup_nj_denormals;
if (has_dcbz == 2)
settings += ppu_settings::accurate_cache_line_stores;
if (g_cfg.core.ppu_128_reservations_loop_max_length)
Expand All @@ -3104,7 +3105,9 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
if (g_cfg.core.ppu_set_fpcc)
settings += ppu_settings::accurate_fpcc, fmt::throw_exception("FPCC Not implemented");
if (g_cfg.core.ppu_set_vnan)
settings += ppu_settings::accurate_vnan, fmt::throw_exception("VNAN Not implemented");
settings += ppu_settings::accurate_vnan, settings -= ppu_settings::fixup_vnan, fmt::throw_exception("VNAN Not implemented");
if (g_cfg.core.ppu_use_nj_bit)
settings += ppu_settings::accurate_nj_mode, settings -= ppu_settings::fixup_nj_denormals, fmt::throw_exception("NJ Not implemented");

// Write version, hash, CPU, settings
fmt::append(obj_name, "v5-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu));
Expand Down
14 changes: 7 additions & 7 deletions rpcs3/Emu/Cell/PPUTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ Value* PPUTranslator::VecHandleDenormal(Value* val)
Value* PPUTranslator::VecHandleResult(Value* val)
{
val = g_cfg.core.ppu_fix_vnan ? VecHandleNan(val) : val;
val = g_cfg.core.ppu_use_nj_bit ? VecHandleDenormal(val) : val;
val = g_cfg.core.ppu_llvm_nj_fixup ? VecHandleDenormal(val) : val;
return val;
}

Expand Down Expand Up @@ -649,7 +649,7 @@ void PPUTranslator::MTVSCR(ppu_opcode_t op)
const auto vscr = m_ir->CreateExtractElement(GetVr(op.vb, VrType::vi32), m_ir->getInt32(m_is_be ? 3 : 0));
const auto nj = Trunc(m_ir->CreateLShr(vscr, 16), GetType<bool>());
RegStore(nj, m_nj);
if (g_cfg.core.ppu_use_nj_bit)
if (g_cfg.core.ppu_llvm_nj_fixup)
RegStore(m_ir->CreateSelect(nj, m_ir->getInt32(0x7f80'0000), m_ir->getInt32(0x7fff'ffff)), m_jm_mask);
if (g_cfg.core.ppu_set_sat_bit)
RegStore(m_ir->CreateInsertElement(ConstantAggregateZero::get(GetType<u32[4]>()), m_ir->CreateAnd(vscr, 1), m_ir->getInt32(0)), m_sat);
Expand Down Expand Up @@ -999,7 +999,7 @@ void PPUTranslator::VMADDFP(ppu_opcode_t op)
void PPUTranslator::VMAXFP(ppu_opcode_t op)
{
const auto [a, b] = get_vrs<f32[4]>(op.va, op.vb);
set_vr(op.vd, vec_handle_result(select(bitcast<u32[4]>(fmin(a, b)) == bitcast<u32[4]>(a), b, a)));
set_vr(op.vd, vec_handle_result(bitcast<f32[4]>(bitcast<u32[4]>(fmax(a, b)) & bitcast<u32[4]>(fmax(b, a)))));
}

void PPUTranslator::VMAXSB(ppu_opcode_t op)
Expand Down Expand Up @@ -1061,7 +1061,7 @@ void PPUTranslator::VMHRADDSHS(ppu_opcode_t op)
void PPUTranslator::VMINFP(ppu_opcode_t op)
{
const auto [a, b] = get_vrs<f32[4]>(op.va, op.vb);
set_vr(op.vd, vec_handle_result(select(bitcast<u32[4]>(fmax(a, b)) == bitcast<u32[4]>(a), b, a)));
set_vr(op.vd, vec_handle_result(bitcast<f32[4]>(bitcast<u32[4]>(fmin(a, b)) | bitcast<u32[4]>(fmin(b, a)))));
}

void PPUTranslator::VMINSB(ppu_opcode_t op)
Expand Down Expand Up @@ -1260,14 +1260,14 @@ void PPUTranslator::VNMSUBFP(ppu_opcode_t op)
{
if (data == v128{})
{
set_vr(op.vd, vec_handle_result(-a * c));
set_vr(op.vd, vec_handle_result(-(a * c)));
ppu_log.notice("LLVM: VNMSUBFP with 0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0));
return;
}

if (!m_use_fma && data == v128::from32p(1u << 31))
{
set_vr(op.vd, vec_handle_result(-a * c + fsplat<f32[4]>(0.f)));
set_vr(op.vd, vec_handle_result(-(a * c - fsplat<f32[4]>(0.f))));
ppu_log.notice("LLVM: VNMSUBFP with -0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0));
return;
}
Expand All @@ -1276,7 +1276,7 @@ void PPUTranslator::VNMSUBFP(ppu_opcode_t op)
// Differs from the emulated path with regards to negative zero
if (m_use_fma)
{
set_vr(op.vd, vec_handle_result(fmuladd(-a, c, b)));
set_vr(op.vd, vec_handle_result(-fmuladd(a, c, -b)));
return;
}

Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/system_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct cfg_root : cfg::node
cfg::_int<-1, 14> ppu_128_reservations_loop_max_length{ this, "Accurate PPU 128-byte Reservation Op Max Length", 0, true }; // -1: Always accurate, 0: Never accurate, 1-14: max accurate loop length
cfg::_int<-64, 64> stub_ppu_traps{ this, "Stub PPU Traps", 0, true }; // Hack, skip PPU traps for rare cases where the trap is continueable (specify relative instructions to skip)
cfg::_bool full_width_avx512{ this, "Full Width AVX-512", false };
cfg::_bool ppu_llvm_nj_fixup{ this, "PPU LLVM Java Mode Handling", true }; // Partially respect current Java Mode for alti-vec ops by PPU LLVM
cfg::_bool use_accurate_dfma{ this, "Use Accurate DFMA", true }; // Enable accurate double-precision FMA for CPUs which do not support it natively
cfg::_bool ppu_set_sat_bit{ this, "PPU Set Saturation Bit", false }; // Accuracy. If unset, completely disable saturation flag handling.
cfg::_bool ppu_use_nj_bit{ this, "PPU Use Non-Java Mode Bit", false }; // Accuracy. If unset, ignore NJ flag completely.
Expand Down