code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 AtomOp : u64 {
    ADD,
    MIN,
    MAX,
    INC,
    DEC,
    AND,
    OR,
    XOR,
    EXCH,
    SAFEADD,
};

enum class AtomSize : u64 {
    U32,
    S32,
    U64,
    F32,
    F16x2,
    S64,
};

IR::U32U64 ApplyIntegerAtomOp(IR::IREmitter& ir, const IR::U32U64& offset, const IR::U32U64& op_b,
                              AtomOp op, bool is_signed) {
    switch (op) {
    case AtomOp::ADD:
        return ir.GlobalAtomicIAdd(offset, op_b);
    case AtomOp::MIN:
        return ir.GlobalAtomicIMin(offset, op_b, is_signed);
    case AtomOp::MAX:
        return ir.GlobalAtomicIMax(offset, op_b, is_signed);
    case AtomOp::INC:
        return ir.GlobalAtomicInc(offset, op_b);
    case AtomOp::DEC:
        return ir.GlobalAtomicDec(offset, op_b);
    case AtomOp::AND:
        return ir.GlobalAtomicAnd(offset, op_b);
    case AtomOp::OR:
        return ir.GlobalAtomicOr(offset, op_b);
    case AtomOp::XOR:
        return ir.GlobalAtomicXor(offset, op_b);
    case AtomOp::EXCH:
        return ir.GlobalAtomicExchange(offset, op_b);
    default:
        throw NotImplementedException("Integer Atom Operation {}", op);
    }
}

IR::Value ApplyFpAtomOp(IR::IREmitter& ir, const IR::U64& offset, const IR::Value& op_b, AtomOp op,
                        AtomSize size) {
    static constexpr IR::FpControl f16_control{
        .no_contraction = false,
        .rounding = IR::FpRounding::RN,
        .fmz_mode = IR::FmzMode::DontCare,
    };
    static constexpr IR::FpControl f32_control{
        .no_contraction = false,
        .rounding = IR::FpRounding::RN,
        .fmz_mode = IR::FmzMode::FTZ,
    };
    switch (op) {
    case AtomOp::ADD:
        return size == AtomSize::F32 ? ir.GlobalAtomicF32Add(offset, op_b, f32_control)
                                     : ir.GlobalAtomicF16x2Add(offset, op_b, f16_control);
    case AtomOp::MIN:
        return ir.GlobalAtomicF16x2Min(offset, op_b, f16_control);
    case AtomOp::MAX:
        return ir.GlobalAtomicF16x2Max(offset, op_b, f16_control);
    default:
        throw NotImplementedException("FP Atom Operation {}", op);
    }
}

IR::U64 AtomOffset(TranslatorVisitor& v, u64 insn) {
    union {
        u64 raw;
        BitField<8, 8, IR::Reg> addr_reg;
        BitField<28, 20, s64> addr_offset;
        BitField<28, 20, u64> rz_addr_offset;
        BitField<48, 1, u64> e;
    } const mem{insn};

    const IR::U64 address{[&]() -> IR::U64 {
        if (mem.e == 0) {
            return v.ir.UConvert(64, v.X(mem.addr_reg));
        }
        return v.L(mem.addr_reg);
    }()};
    const u64 addr_offset{[&]() -> u64 {
        if (mem.addr_reg == IR::Reg::RZ) {
            // When RZ is used, the address is an absolute address
            return static_cast<u64>(mem.rz_addr_offset.Value());
        } else {
            return static_cast<u64>(mem.addr_offset.Value());
        }
    }()};
    return v.ir.IAdd(address, v.ir.Imm64(addr_offset));
}

bool AtomOpNotApplicable(AtomSize size, AtomOp op) {
    // TODO: SAFEADD
    switch (size) {
    case AtomSize::S32:
    case AtomSize::U64:
        return (op == AtomOp::INC || op == AtomOp::DEC);
    case AtomSize::S64:
        return !(op == AtomOp::MIN || op == AtomOp::MAX);
    case AtomSize::F32:
        return op != AtomOp::ADD;
    case AtomSize::F16x2:
        return !(op == AtomOp::ADD || op == AtomOp::MIN || op == AtomOp::MAX);
    default:
        return false;
    }
}

IR::U32U64 LoadGlobal(IR::IREmitter& ir, const IR::U64& offset, AtomSize size) {
    switch (size) {
    case AtomSize::U32:
    case AtomSize::S32:
    case AtomSize::F32:
    case AtomSize::F16x2:
        return ir.LoadGlobal32(offset);
    case AtomSize::U64:
    case AtomSize::S64:
        return ir.PackUint2x32(ir.LoadGlobal64(offset));
    default:
        throw NotImplementedException("Atom Size {}", size);
    }
}

void StoreResult(TranslatorVisitor& v, IR::Reg dest_reg, const IR::Value& result, AtomSize size) {
    switch (size) {
    case AtomSize::U32:
    case AtomSize::S32:
    case AtomSize::F16x2:
        return v.X(dest_reg, IR::U32{result});
    case AtomSize::U64:
    case AtomSize::S64:
        return v.L(dest_reg, IR::U64{result});
    case AtomSize::F32:
        return v.F(dest_reg, IR::F32{result});
    default:
        break;
    }
}

IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64& offset,
                      AtomSize size, AtomOp op) {
    switch (size) {
    case AtomSize::U32:
    case AtomSize::S32:
        return ApplyIntegerAtomOp(v.ir, offset, v.X(operand_reg), op, size == AtomSize::S32);
    case AtomSize::U64:
    case AtomSize::S64:
        return ApplyIntegerAtomOp(v.ir, offset, v.L(operand_reg), op, size == AtomSize::S64);
    case AtomSize::F32:
        return ApplyFpAtomOp(v.ir, offset, v.F(operand_reg), op, size);
    case AtomSize::F16x2: {
        return ApplyFpAtomOp(v.ir, offset, v.ir.UnpackFloat2x16(v.X(operand_reg)), op, size);
    }
    default:
        throw NotImplementedException("Atom Size {}", size);
    }
}

void GlobalAtomic(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg,
                  const IR::U64& offset, AtomSize size, AtomOp op, bool write_dest) {
    IR::Value result;
    if (AtomOpNotApplicable(size, op)) {
        result = LoadGlobal(v.ir, offset, size);
    } else {
        result = ApplyAtomOp(v, operand_reg, offset, size, op);
    }
    if (write_dest) {
        StoreResult(v, dest_reg, result, size);
    }
}
} // Anonymous namespace

void TranslatorVisitor::ATOM(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<20, 8, IR::Reg> operand_reg;
        BitField<49, 3, AtomSize> size;
        BitField<52, 4, AtomOp> op;
    } const atom{insn};
    const IR::U64 offset{AtomOffset(*this, insn)};
    GlobalAtomic(*this, atom.dest_reg, atom.operand_reg, offset, atom.size, atom.op, true);
}

void TranslatorVisitor::RED(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> operand_reg;
        BitField<20, 3, AtomSize> size;
        BitField<23, 3, AtomOp> op;
    } const red{insn};
    const IR::U64 offset{AtomOffset(*this, insn)};
    GlobalAtomic(*this, IR::Reg::RZ, red.operand_reg, offset, red.size, red.op, true);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 AtomOp : u64 {
    ADD,
    MIN,
    MAX,
    INC,
    DEC,
    AND,
    OR,
    XOR,
    EXCH,
};

enum class AtomsSize : u64 {
    U32,
    S32,
    U64,
};

IR::U32U64 ApplyAtomsOp(IR::IREmitter& ir, const IR::U32& offset, const IR::U32U64& op_b, AtomOp op,
                        bool is_signed) {
    switch (op) {
    case AtomOp::ADD:
        return ir.SharedAtomicIAdd(offset, op_b);
    case AtomOp::MIN:
        return ir.SharedAtomicIMin(offset, op_b, is_signed);
    case AtomOp::MAX:
        return ir.SharedAtomicIMax(offset, op_b, is_signed);
    case AtomOp::INC:
        return ir.SharedAtomicInc(offset, op_b);
    case AtomOp::DEC:
        return ir.SharedAtomicDec(offset, op_b);
    case AtomOp::AND:
        return ir.SharedAtomicAnd(offset, op_b);
    case AtomOp::OR:
        return ir.SharedAtomicOr(offset, op_b);
    case AtomOp::XOR:
        return ir.SharedAtomicXor(offset, op_b);
    case AtomOp::EXCH:
        return ir.SharedAtomicExchange(offset, op_b);
    default:
        throw NotImplementedException("Integer Atoms Operation {}", op);
    }
}

IR::U32 AtomsOffset(TranslatorVisitor& v, u64 insn) {
    union {
        u64 raw;
        BitField<8, 8, IR::Reg> offset_reg;
        BitField<30, 22, u64> absolute_offset;
        BitField<30, 22, s64> relative_offset;
    } const encoding{insn};

    if (encoding.offset_reg == IR::Reg::RZ) {
        return v.ir.Imm32(static_cast<u32>(encoding.absolute_offset << 2));
    } else {
        const s32 relative{static_cast<s32>(encoding.relative_offset << 2)};
        return v.ir.IAdd(v.X(encoding.offset_reg), v.ir.Imm32(relative));
    }
}

void StoreResult(TranslatorVisitor& v, IR::Reg dest_reg, const IR::Value& result, AtomsSize size) {
    switch (size) {
    case AtomsSize::U32:
    case AtomsSize::S32:
        return v.X(dest_reg, IR::U32{result});
    case AtomsSize::U64:
        return v.L(dest_reg, IR::U64{result});
    default:
        break;
    }
}
} // Anonymous namespace

void TranslatorVisitor::ATOMS(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> addr_reg;
        BitField<20, 8, IR::Reg> src_reg_b;
        BitField<28, 2, AtomsSize> size;
        BitField<52, 4, AtomOp> op;
    } const atoms{insn};

    const bool size_64{atoms.size == AtomsSize::U64};
    if (size_64 && atoms.op != AtomOp::EXCH) {
        throw NotImplementedException("64-bit Atoms Operation {}", atoms.op.Value());
    }
    const bool is_signed{atoms.size == AtomsSize::S32};
    const IR::U32 offset{AtomsOffset(*this, insn)};

    IR::Value result;
    if (size_64) {
        result = ApplyAtomsOp(ir, offset, L(atoms.src_reg_b), atoms.op, is_signed);
    } else {
        result = ApplyAtomsOp(ir, offset, X(atoms.src_reg_b), atoms.op, is_signed);
    }
    StoreResult(*this, atoms.dest_reg, result, atoms.size);
}

} // namespace Shader::Maxwell

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {

enum class BitSize : u64 {
    B32,
    B64,
    B96,
    B128,
};

void TranslatorVisitor::AL2P(u64 inst) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> result_register;
        BitField<8, 8, IR::Reg> indexing_register;
        BitField<20, 11, s64> offset;
        BitField<47, 2, BitSize> bitsize;
    } al2p{inst};
    if (al2p.bitsize != BitSize::B32) {
        throw NotImplementedException("BitSize {}", al2p.bitsize.Value());
    }
    const IR::U32 converted_offset{ir.Imm32(static_cast<u32>(al2p.offset.Value()))};
    const IR::U32 result{ir.IAdd(X(al2p.indexing_register), converted_offset)};
    X(al2p.result_register, result);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
// Seems to be in CUDA terminology.
enum class LocalScope : u64 {
    CTA,
    GL,
    SYS,
    VC,
};
} // Anonymous namespace

void TranslatorVisitor::MEMBAR(u64 inst) {
    union {
        u64 raw;
        BitField<8, 2, LocalScope> scope;
    } const membar{inst};

    if (membar.scope == LocalScope::CTA) {
        ir.WorkgroupMemoryBarrier();
    } else {
        ir.DeviceMemoryBarrier();
    }
}

void TranslatorVisitor::DEPBAR() {
    // DEPBAR is a no-op
}

void TranslatorVisitor::BAR(u64 insn) {
    enum class Mode {
        RedPopc,
        Scan,
        RedAnd,
        RedOr,
        Sync,
        Arrive,
    };
    union {
        u64 raw;
        BitField<43, 1, u64> is_a_imm;
        BitField<44, 1, u64> is_b_imm;
        BitField<8, 8, u64> imm_a;
        BitField<20, 12, u64> imm_b;
        BitField<42, 1, u64> neg_pred;
        BitField<39, 3, IR::Pred> pred;
    } const bar{insn};

    const Mode mode{[insn] {
        switch (insn & 0x0000009B00000000ULL) {
        case 0x0000000200000000ULL:
            return Mode::RedPopc;
        case 0x0000000300000000ULL:
            return Mode::Scan;
        case 0x0000000A00000000ULL:
            return Mode::RedAnd;
        case 0x0000001200000000ULL:
            return Mode::RedOr;
        case 0x0000008000000000ULL:
            return Mode::Sync;
        case 0x0000008100000000ULL:
            return Mode::Arrive;
        }
        throw NotImplementedException("Invalid encoding");
    }()};
    if (mode != Mode::Sync) {
        throw NotImplementedException("BAR mode {}", mode);
    }
    if (bar.is_a_imm == 0) {
        throw NotImplementedException("Non-immediate input A");
    }
    if (bar.imm_a != 0) {
        throw NotImplementedException("Non-zero input A");
    }
    if (bar.is_b_imm == 0) {
        throw NotImplementedException("Non-immediate input B");
    }
    if (bar.imm_b != 0) {
        throw NotImplementedException("Non-zero input B");
    }
    if (bar.pred != IR::Pred::PT && bar.neg_pred != 0) {
        throw NotImplementedException("Non-true input predicate");
    }
    ir.Barrier();
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void BFE(TranslatorVisitor& v, u64 insn, const IR::U32& src) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> offset_reg;
        BitField<40, 1, u64> brev;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> is_signed;
    } const bfe{insn};

    const IR::U32 offset{v.ir.BitFieldExtract(src, v.ir.Imm32(0), v.ir.Imm32(8), false)};
    const IR::U32 count{v.ir.BitFieldExtract(src, v.ir.Imm32(8), v.ir.Imm32(8), false)};

    // Common constants
    const IR::U32 zero{v.ir.Imm32(0)};
    const IR::U32 one{v.ir.Imm32(1)};
    const IR::U32 max_size{v.ir.Imm32(32)};
    // Edge case conditions
    const IR::U1 zero_count{v.ir.IEqual(count, zero)};
    const IR::U1 exceed_count{v.ir.IGreaterThanEqual(v.ir.IAdd(offset, count), max_size, false)};
    const IR::U1 replicate{v.ir.IGreaterThanEqual(offset, max_size, false)};

    IR::U32 base{v.X(bfe.offset_reg)};
    if (bfe.brev != 0) {
        base = v.ir.BitReverse(base);
    }
    IR::U32 result{v.ir.BitFieldExtract(base, offset, count, bfe.is_signed != 0)};
    if (bfe.is_signed != 0) {
        const IR::U1 is_negative{v.ir.ILessThan(base, zero, true)};
        const IR::U32 replicated_bit{v.ir.Select(is_negative, v.ir.Imm32(-1), zero)};
        const IR::U32 exceed_bit{v.ir.BitFieldExtract(base, v.ir.Imm32(31), one, false)};
        // Replicate condition
        result = IR::U32{v.ir.Select(replicate, replicated_bit, result)};
        // Exceeding condition
        const IR::U32 exceed_result{v.ir.BitFieldInsert(result, exceed_bit, v.ir.Imm32(31), one)};
        result = IR::U32{v.ir.Select(exceed_count, exceed_result, result)};
    }
    // Zero count condition
    result = IR::U32{v.ir.Select(zero_count, zero, result)};

    v.X(bfe.dest_reg, result);

    if (bfe.cc != 0) {
        v.SetZFlag(v.ir.IEqual(result, zero));
        v.SetSFlag(v.ir.ILessThan(result, zero, true));
        v.ResetCFlag();
        v.ResetOFlag();
    }
}
} // Anonymous namespace

void TranslatorVisitor::BFE_reg(u64 insn) {
    BFE(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::BFE_cbuf(u64 insn) {
    BFE(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::BFE_imm(u64 insn) {
    BFE(*this, insn, GetImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void BFI(TranslatorVisitor& v, u64 insn, const IR::U32& src_a, const IR::U32& base) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> insert_reg;
        BitField<47, 1, u64> cc;
    } const bfi{insn};

    const IR::U32 zero{v.ir.Imm32(0)};
    const IR::U32 offset{v.ir.BitFieldExtract(src_a, zero, v.ir.Imm32(8), false)};
    const IR::U32 unsafe_count{v.ir.BitFieldExtract(src_a, v.ir.Imm32(8), v.ir.Imm32(8), false)};
    const IR::U32 max_size{v.ir.Imm32(32)};

    // Edge case conditions
    const IR::U1 exceed_offset{v.ir.IGreaterThanEqual(offset, max_size, false)};
    const IR::U1 exceed_count{v.ir.IGreaterThan(unsafe_count, max_size, false)};

    const IR::U32 remaining_size{v.ir.ISub(max_size, offset)};
    const IR::U32 safe_count{v.ir.Select(exceed_count, remaining_size, unsafe_count)};

    const IR::U32 insert{v.X(bfi.insert_reg)};
    IR::U32 result{v.ir.BitFieldInsert(base, insert, offset, safe_count)};

    result = IR::U32{v.ir.Select(exceed_offset, base, result)};

    v.X(bfi.dest_reg, result);
    if (bfi.cc != 0) {
        v.SetZFlag(v.ir.IEqual(result, zero));
        v.SetSFlag(v.ir.ILessThan(result, zero, true));
        v.ResetCFlag();
        v.ResetOFlag();
    }
}
} // Anonymous namespace

void TranslatorVisitor::BFI_reg(u64 insn) {
    BFI(*this, insn, GetReg20(insn), GetReg39(insn));
}

void TranslatorVisitor::BFI_rc(u64 insn) {
    BFI(*this, insn, GetReg39(insn), GetCbuf(insn));
}

void TranslatorVisitor::BFI_cr(u64 insn) {
    BFI(*this, insn, GetCbuf(insn), GetReg39(insn));
}

void TranslatorVisitor::BFI_imm(u64 insn) {
    BFI(*this, insn, GetImm20(insn), GetReg39(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void Check(u64 insn) {
    union {
        u64 raw;
        BitField<5, 1, u64> cbuf_mode;
        BitField<6, 1, u64> lmt;
    } const encoding{insn};

    if (encoding.cbuf_mode != 0) {
        throw NotImplementedException("Constant buffer mode");
    }
    if (encoding.lmt != 0) {
        throw NotImplementedException("LMT");
    }
}
} // Anonymous namespace

void TranslatorVisitor::BRX(u64 insn) {
    Check(insn);
}

void TranslatorVisitor::JMX(u64 insn) {
    Check(insn);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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:
        // FMZ is manually handled in the instruction
        return IR::FmzMode::FTZ;
    case FmzMode::INVALIDFMZ3:
        break;
    }
    throw NotImplementedException("Invalid FMZ mode {}", fmz_mode);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"

namespace Shader::Maxwell {
IR::U1 IntegerCompare(IR::IREmitter& ir, const IR::U32& operand_1, const IR::U32& operand_2,
                      CompareOp compare_op, bool is_signed) {
    switch (compare_op) {
    case CompareOp::False:
        return ir.Imm1(false);
    case CompareOp::LessThan:
        return ir.ILessThan(operand_1, operand_2, is_signed);
    case CompareOp::Equal:
        return ir.IEqual(operand_1, operand_2);
    case CompareOp::LessThanEqual:
        return ir.ILessThanEqual(operand_1, operand_2, is_signed);
    case CompareOp::GreaterThan:
        return ir.IGreaterThan(operand_1, operand_2, is_signed);
    case CompareOp::NotEqual:
        return ir.INotEqual(operand_1, operand_2);
    case CompareOp::GreaterThanEqual:
        return ir.IGreaterThanEqual(operand_1, operand_2, is_signed);
    case CompareOp::True:
        return ir.Imm1(true);
    default:
        throw NotImplementedException("Invalid compare op {}", compare_op);
    }
}

IR::U1 ExtendedIntegerCompare(IR::IREmitter& ir, const IR::U32& operand_1, const IR::U32& operand_2,
                              CompareOp compare_op, bool is_signed) {
    const IR::U32 zero{ir.Imm32(0)};
    const IR::U32 carry{ir.Select(ir.GetCFlag(), ir.Imm32(1), zero)};
    const IR::U1 z_flag{ir.GetZFlag()};
    const IR::U32 intermediate{ir.IAdd(ir.IAdd(operand_1, ir.BitwiseNot(operand_2)), carry)};
    const IR::U1 flip_logic{is_signed ? ir.Imm1(false)
                                      : ir.LogicalXor(ir.ILessThan(operand_1, zero, true),
                                                      ir.ILessThan(operand_2, zero, true))};
    switch (compare_op) {
    case CompareOp::False:
        return ir.Imm1(false);
    case CompareOp::LessThan:
        return IR::U1{ir.Select(flip_logic, ir.IGreaterThanEqual(intermediate, zero, true),
                                ir.ILessThan(intermediate, zero, true))};
    case CompareOp::Equal:
        return ir.LogicalAnd(ir.IEqual(intermediate, zero), z_flag);
    case CompareOp::LessThanEqual: {
        const IR::U1 base_cmp{ir.Select(flip_logic, ir.IGreaterThanEqual(intermediate, zero, true),
                                        ir.ILessThan(intermediate, zero, true))};
        return ir.LogicalOr(base_cmp, ir.LogicalAnd(ir.IEqual(intermediate, zero), z_flag));
    }
    case CompareOp::GreaterThan: {
        const IR::U1 base_cmp{ir.Select(flip_logic, ir.ILessThanEqual(intermediate, zero, true),
                                        ir.IGreaterThan(intermediate, zero, true))};
        const IR::U1 not_z{ir.LogicalNot(z_flag)};
        return ir.LogicalOr(base_cmp, ir.LogicalAnd(ir.IEqual(intermediate, zero), not_z));
    }
    case CompareOp::NotEqual:
        return ir.LogicalOr(ir.INotEqual(intermediate, zero),
                            ir.LogicalAnd(ir.IEqual(intermediate, zero), ir.LogicalNot(z_flag)));
    case CompareOp::GreaterThanEqual: {
        const IR::U1 base_cmp{ir.Select(flip_logic, ir.ILessThan(intermediate, zero, true),
                                        ir.IGreaterThanEqual(intermediate, zero, true))};
        return ir.LogicalOr(base_cmp, ir.LogicalAnd(ir.IEqual(intermediate, zero), z_flag));
    }
    case CompareOp::True:
        return ir.Imm1(true);
    default:
        throw NotImplementedException("Invalid compare op {}", compare_op);
    }
}

IR::U1 PredicateCombine(IR::IREmitter& ir, const IR::U1& predicate_1, const IR::U1& predicate_2,
                        BooleanOp bop) {
    switch (bop) {
    case BooleanOp::AND:
        return ir.LogicalAnd(predicate_1, predicate_2);
    case BooleanOp::OR:
        return ir.LogicalOr(predicate_1, predicate_2);
    case BooleanOp::XOR:
        return ir.LogicalXor(predicate_1, predicate_2);
    default:
        throw NotImplementedException("Invalid bop {}", bop);
    }
}

IR::U1 PredicateOperation(IR::IREmitter& ir, const IR::U32& result, PredicateOp op) {
    switch (op) {
    case PredicateOp::False:
        return ir.Imm1(false);
    case PredicateOp::True:
        return ir.Imm1(true);
    case PredicateOp::Zero:
        return ir.IEqual(result, ir.Imm32(0));
    case PredicateOp::NonZero:
        return ir.INotEqual(result, ir.Imm32(0));
    default:
        throw NotImplementedException("Invalid Predicate operation {}", op);
    }
}

bool IsCompareOpOrdered(FPCompareOp op) {
    switch (op) {
    case FPCompareOp::LTU:
    case FPCompareOp::EQU:
    case FPCompareOp::LEU:
    case FPCompareOp::GTU:
    case FPCompareOp::NEU:
    case FPCompareOp::GEU:
        return false;
    default:
        return true;
    }
}

IR::U1 FloatingPointCompare(IR::IREmitter& ir, const IR::F16F32F64& operand_1,
                            const IR::F16F32F64& operand_2, FPCompareOp compare_op,
                            IR::FpControl control) {
    const bool ordered{IsCompareOpOrdered(compare_op)};
    switch (compare_op) {
    case FPCompareOp::F:
        return ir.Imm1(false);
    case FPCompareOp::LT:
    case FPCompareOp::LTU:
        return ir.FPLessThan(operand_1, operand_2, control, ordered);
    case FPCompareOp::EQ:
    case FPCompareOp::EQU:
        return ir.FPEqual(operand_1, operand_2, control, ordered);
    case FPCompareOp::LE:
    case FPCompareOp::LEU:
        return ir.FPLessThanEqual(operand_1, operand_2, control, ordered);
    case FPCompareOp::GT:
    case FPCompareOp::GTU:
        return ir.FPGreaterThan(operand_1, operand_2, control, ordered);
    case FPCompareOp::NE:
    case FPCompareOp::NEU:
        return ir.FPNotEqual(operand_1, operand_2, control, ordered);
    case FPCompareOp::GE:
    case FPCompareOp::GEU:
        return ir.FPGreaterThanEqual(operand_1, operand_2, control, ordered);
    case FPCompareOp::NUM:
        return ir.FPOrdered(operand_1, operand_2);
    case FPCompareOp::Nan:
        return ir.FPUnordered(operand_1, operand_2);
    case FPCompareOp::T:
        return ir.Imm1(true);
    default:
        throw NotImplementedException("Invalid FP compare op {}", compare_op);
    }
}
} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
[[nodiscard]] IR::U1 IntegerCompare(IR::IREmitter& ir, const IR::U32& operand_1,
                                    const IR::U32& operand_2, CompareOp compare_op, bool is_signed);

[[nodiscard]] IR::U1 ExtendedIntegerCompare(IR::IREmitter& ir, const IR::U32& operand_1,
                                            const IR::U32& operand_2, CompareOp compare_op,
                                            bool is_signed);

[[nodiscard]] IR::U1 PredicateCombine(IR::IREmitter& ir, const IR::U1& predicate_1,
                                      const IR::U1& predicate_2, BooleanOp bop);

[[nodiscard]] IR::U1 PredicateOperation(IR::IREmitter& ir, const IR::U32& result, PredicateOp op);

[[nodiscard]] bool IsCompareOpOrdered(FPCompareOp op);

[[nodiscard]] IR::U1 FloatingPointCompare(IR::IREmitter& ir, const IR::F16F32F64& operand_1,
                                          const IR::F16F32F64& operand_2, FPCompareOp compare_op,
                                          IR::FpControl control = {});
} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {

void TranslatorVisitor::CSET(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 5, IR::FlowTest> cc_test;
        BitField<39, 3, IR::Pred> bop_pred;
        BitField<42, 1, u64> neg_bop_pred;
        BitField<44, 1, u64> bf;
        BitField<45, 2, BooleanOp> bop;
        BitField<47, 1, u64> cc;
    } const cset{insn};

    const IR::U32 one_mask{ir.Imm32(-1)};
    const IR::U32 fp_one{ir.Imm32(0x3f800000)};
    const IR::U32 zero{ir.Imm32(0)};
    const IR::U32 pass_result{cset.bf == 0 ? one_mask : fp_one};
    const IR::U1 cc_test_result{ir.GetFlowTestResult(cset.cc_test)};
    const IR::U1 bop_pred{ir.GetPred(cset.bop_pred, cset.neg_bop_pred != 0)};
    const IR::U1 pred_result{PredicateCombine(ir, cc_test_result, bop_pred, cset.bop)};
    const IR::U32 result{ir.Select(pred_result, pass_result, zero)};
    X(cset.dest_reg, result);
    if (cset.cc != 0) {
        const IR::U1 is_zero{ir.IEqual(result, zero)};
        SetZFlag(is_zero);
        if (cset.bf != 0) {
            ResetSFlag();
        } else {
            SetSFlag(ir.LogicalNot(is_zero));
        }
        ResetOFlag();
        ResetCFlag();
    }
}

void TranslatorVisitor::CSETP(u64 insn) {
    union {
        u64 raw;
        BitField<0, 3, IR::Pred> dest_pred_b;
        BitField<3, 3, IR::Pred> dest_pred_a;
        BitField<8, 5, IR::FlowTest> cc_test;
        BitField<39, 3, IR::Pred> bop_pred;
        BitField<42, 1, u64> neg_bop_pred;
        BitField<45, 2, BooleanOp> bop;
    } const csetp{insn};

    const BooleanOp bop{csetp.bop};
    const IR::U1 bop_pred{ir.GetPred(csetp.bop_pred, csetp.neg_bop_pred != 0)};
    const IR::U1 cc_test_result{ir.GetFlowTestResult(csetp.cc_test)};
    const IR::U1 result_a{PredicateCombine(ir, cc_test_result, bop_pred, bop)};
    const IR::U1 result_b{PredicateCombine(ir, ir.LogicalNot(cc_test_result), bop_pred, bop)};
    ir.SetPred(csetp.dest_pred_a, result_a);
    ir.SetPred(csetp.dest_pred_b, result_b);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 DADD(TranslatorVisitor& v, u64 insn, const IR::F64& src_b) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 2, FpRounding> fp_rounding;
        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;
    } const dadd{insn};
    if (dadd.cc != 0) {
        throw NotImplementedException("DADD CC");
    }

    const IR::F64 src_a{v.D(dadd.src_a_reg)};
    const IR::F64 op_a{v.ir.FPAbsNeg(src_a, dadd.abs_a != 0, dadd.neg_a != 0)};
    const IR::F64 op_b{v.ir.FPAbsNeg(src_b, dadd.abs_b != 0, dadd.neg_b != 0)};

    const IR::FpControl control{
        .no_contraction = true,
        .rounding = CastFpRounding(dadd.fp_rounding),
        .fmz_mode = IR::FmzMode::None,
    };

    v.D(dadd.dest_reg, v.ir.FPAdd(op_a, op_b, control));
}
} // Anonymous namespace

void TranslatorVisitor::DADD_reg(u64 insn) {
    DADD(*this, insn, GetDoubleReg20(insn));
}

void TranslatorVisitor::DADD_cbuf(u64 insn) {
    DADD(*this, insn, GetDoubleCbuf(insn));
}

void TranslatorVisitor::DADD_imm(u64 insn) {
    DADD(*this, insn, GetDoubleImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void DSET(TranslatorVisitor& v, u64 insn, const IR::F64& src_b) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> pred;
        BitField<42, 1, u64> neg_pred;
        BitField<43, 1, u64> negate_a;
        BitField<44, 1, u64> abs_b;
        BitField<45, 2, BooleanOp> bop;
        BitField<47, 1, u64> cc;
        BitField<48, 4, FPCompareOp> compare_op;
        BitField<52, 1, u64> bf;
        BitField<53, 1, u64> negate_b;
        BitField<54, 1, u64> abs_a;
    } const dset{insn};

    const IR::F64 op_a{v.ir.FPAbsNeg(v.D(dset.src_a_reg), dset.abs_a != 0, dset.negate_a != 0)};
    const IR::F64 op_b{v.ir.FPAbsNeg(src_b, dset.abs_b != 0, dset.negate_b != 0)};

    IR::U1 pred{v.ir.GetPred(dset.pred)};
    if (dset.neg_pred != 0) {
        pred = v.ir.LogicalNot(pred);
    }
    const IR::U1 cmp_result{FloatingPointCompare(v.ir, op_a, op_b, dset.compare_op)};
    const IR::U1 bop_result{PredicateCombine(v.ir, cmp_result, pred, dset.bop)};

    const IR::U32 one_mask{v.ir.Imm32(-1)};
    const IR::U32 fp_one{v.ir.Imm32(0x3f800000)};
    const IR::U32 zero{v.ir.Imm32(0)};
    const IR::U32 pass_result{dset.bf == 0 ? one_mask : fp_one};
    const IR::U32 result{v.ir.Select(bop_result, pass_result, zero)};

    v.X(dset.dest_reg, result);
    if (dset.cc != 0) {
        const IR::U1 is_zero{v.ir.IEqual(result, zero)};
        v.SetZFlag(is_zero);
        if (dset.bf != 0) {
            v.ResetSFlag();
        } else {
            v.SetSFlag(v.ir.LogicalNot(is_zero));
        }
        v.ResetCFlag();
        v.ResetOFlag();
    }
}
} // Anonymous namespace

void TranslatorVisitor::DSET_reg(u64 insn) {
    DSET(*this, insn, GetDoubleReg20(insn));
}

void TranslatorVisitor::DSET_cbuf(u64 insn) {
    DSET(*this, insn, GetDoubleCbuf(insn));
}

void TranslatorVisitor::DSET_imm(u64 insn) {
    DSET(*this, insn, GetDoubleImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 DFMA(TranslatorVisitor& v, u64 insn, const IR::F64& src_b, const IR::F64& src_c) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<50, 2, FpRounding> fp_rounding;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> neg_b;
        BitField<49, 1, u64> neg_c;
    } const dfma{insn};

    if (dfma.cc != 0) {
        throw NotImplementedException("DFMA CC");
    }

    const IR::F64 src_a{v.D(dfma.src_a_reg)};
    const IR::F64 op_b{v.ir.FPAbsNeg(src_b, false, dfma.neg_b != 0)};
    const IR::F64 op_c{v.ir.FPAbsNeg(src_c, false, dfma.neg_c != 0)};

    const IR::FpControl control{
        .no_contraction = true,
        .rounding = CastFpRounding(dfma.fp_rounding),
        .fmz_mode = IR::FmzMode::None,
    };

    v.D(dfma.dest_reg, v.ir.FPFma(src_a, op_b, op_c, control));
}
} // Anonymous namespace

void TranslatorVisitor::DFMA_reg(u64 insn) {
    DFMA(*this, insn, GetDoubleReg20(insn), GetDoubleReg39(insn));
}

void TranslatorVisitor::DFMA_cr(u64 insn) {
    DFMA(*this, insn, GetDoubleCbuf(insn), GetDoubleReg39(insn));
}

void TranslatorVisitor::DFMA_rc(u64 insn) {
    DFMA(*this, insn, GetDoubleReg39(insn), GetDoubleCbuf(insn));
}

void TranslatorVisitor::DFMA_imm(u64 insn) {
    DFMA(*this, insn, GetDoubleImm20(insn), GetDoubleReg39(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void DMNMX(TranslatorVisitor& v, u64 insn, const IR::F64& src_b) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> pred;
        BitField<42, 1, u64> neg_pred;
        BitField<45, 1, u64> negate_b;
        BitField<46, 1, u64> abs_a;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> negate_a;
        BitField<49, 1, u64> abs_b;
    } const dmnmx{insn};

    if (dmnmx.cc != 0) {
        throw NotImplementedException("DMNMX CC");
    }

    const IR::U1 pred{v.ir.GetPred(dmnmx.pred)};
    const IR::F64 op_a{v.ir.FPAbsNeg(v.D(dmnmx.src_a_reg), dmnmx.abs_a != 0, dmnmx.negate_a != 0)};
    const IR::F64 op_b{v.ir.FPAbsNeg(src_b, dmnmx.abs_b != 0, dmnmx.negate_b != 0)};

    IR::F64 max{v.ir.FPMax(op_a, op_b)};
    IR::F64 min{v.ir.FPMin(op_a, op_b)};

    if (dmnmx.neg_pred != 0) {
        std::swap(min, max);
    }
    v.D(dmnmx.dest_reg, IR::F64{v.ir.Select(pred, min, max)});
}
} // Anonymous namespace

void TranslatorVisitor::DMNMX_reg(u64 insn) {
    DMNMX(*this, insn, GetDoubleReg20(insn));
}

void TranslatorVisitor::DMNMX_cbuf(u64 insn) {
    DMNMX(*this, insn, GetDoubleCbuf(insn));
}

void TranslatorVisitor::DMNMX_imm(u64 insn) {
    DMNMX(*this, insn, GetDoubleImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 DMUL(TranslatorVisitor& v, u64 insn, const IR::F64& src_b) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 2, FpRounding> fp_rounding;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> neg;
    } const dmul{insn};

    if (dmul.cc != 0) {
        throw NotImplementedException("DMUL CC");
    }

    const IR::F64 src_a{v.ir.FPAbsNeg(v.D(dmul.src_a_reg), false, dmul.neg != 0)};
    const IR::FpControl control{
        .no_contraction = true,
        .rounding = CastFpRounding(dmul.fp_rounding),
        .fmz_mode = IR::FmzMode::None,
    };

    v.D(dmul.dest_reg, v.ir.FPMul(src_a, src_b, control));
}
} // Anonymous namespace

void TranslatorVisitor::DMUL_reg(u64 insn) {
    DMUL(*this, insn, GetDoubleReg20(insn));
}

void TranslatorVisitor::DMUL_cbuf(u64 insn) {
    DMUL(*this, insn, GetDoubleCbuf(insn));
}

void TranslatorVisitor::DMUL_imm(u64 insn) {
    DMUL(*this, insn, GetDoubleImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void DSETP(TranslatorVisitor& v, u64 insn, const IR::F64& src_b) {
    union {
        u64 insn;
        BitField<0, 3, IR::Pred> dest_pred_b;
        BitField<3, 3, IR::Pred> dest_pred_a;
        BitField<6, 1, u64> negate_b;
        BitField<7, 1, u64> abs_a;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> bop_pred;
        BitField<42, 1, u64> neg_bop_pred;
        BitField<43, 1, u64> negate_a;
        BitField<44, 1, u64> abs_b;
        BitField<45, 2, BooleanOp> bop;
        BitField<48, 4, FPCompareOp> compare_op;
    } const dsetp{insn};

    const IR::F64 op_a{v.ir.FPAbsNeg(v.D(dsetp.src_a_reg), dsetp.abs_a != 0, dsetp.negate_a != 0)};
    const IR::F64 op_b{v.ir.FPAbsNeg(src_b, dsetp.abs_b != 0, dsetp.negate_b != 0)};

    const BooleanOp bop{dsetp.bop};
    const FPCompareOp compare_op{dsetp.compare_op};
    const IR::U1 comparison{FloatingPointCompare(v.ir, op_a, op_b, compare_op)};
    const IR::U1 bop_pred{v.ir.GetPred(dsetp.bop_pred, dsetp.neg_bop_pred != 0)};
    const IR::U1 result_a{PredicateCombine(v.ir, comparison, bop_pred, bop)};
    const IR::U1 result_b{PredicateCombine(v.ir, v.ir.LogicalNot(comparison), bop_pred, bop)};
    v.ir.SetPred(dsetp.dest_pred_a, result_a);
    v.ir.SetPred(dsetp.dest_pred_b, result_b);
}
} // Anonymous namespace

void TranslatorVisitor::DSETP_reg(u64 insn) {
    DSETP(*this, insn, GetDoubleReg20(insn));
}

void TranslatorVisitor::DSETP_cbuf(u64 insn) {
    DSETP(*this, insn, GetDoubleCbuf(insn));
}

void TranslatorVisitor::DSETP_imm(u64 insn) {
    DSETP(*this, insn, GetDoubleImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void ExitFragment(TranslatorVisitor& v) {
    const ProgramHeader sph{v.env.SPH()};
    IR::Reg src_reg{IR::Reg::R0};
    for (u32 render_target = 0; render_target < 8; ++render_target) {
        if (!sph.ps.HasOutputComponents(render_target)) {
            continue;
        }
        const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)};
        for (u32 component = 0; component < 4; ++component) {
            if (!mask[component]) {
                ++src_reg;
                continue;
            }
            v.ir.SetFragColor(render_target, component, v.F(src_reg));
            ++src_reg;
        }
    }
    if (sph.ps.omap.sample_mask != 0) {
        v.ir.SetSampleMask(v.X(src_reg));
    }
    if (sph.ps.omap.depth != 0) {
        v.ir.SetFragDepth(v.F(src_reg + 1));
    }
}
} // Anonymous namespace

void TranslatorVisitor::EXIT() {
    switch (env.ShaderStage()) {
    case Stage::Fragment:
        ExitFragment(*this);
        break;
    default:
        break;
    }
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void FLO(TranslatorVisitor& v, u64 insn, IR::U32 src) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<40, 1, u64> tilde;
        BitField<41, 1, u64> shift;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> is_signed;
    } const flo{insn};

    if (flo.cc != 0) {
        throw NotImplementedException("CC");
    }
    if (flo.tilde != 0) {
        src = v.ir.BitwiseNot(src);
    }
    IR::U32 result{flo.is_signed != 0 ? v.ir.FindSMsb(src) : v.ir.FindUMsb(src)};
    if (flo.shift != 0) {
        const IR::U1 not_found{v.ir.IEqual(result, v.ir.Imm32(-1))};
        result = IR::U32{v.ir.Select(not_found, result, v.ir.BitwiseXor(result, v.ir.Imm32(31)))};
    }
    v.X(flo.dest_reg, result);
}
} // Anonymous namespace

void TranslatorVisitor::FLO_reg(u64 insn) {
    FLO(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::FLO_cbuf(u64 insn) {
    FLO(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::FLO_imm(u64 insn) {
    FLO(*this, insn, GetImm20(insn));
}
} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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::F32& 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 (cc) {
        throw NotImplementedException("FADD CC");
    }
    const IR::F32 op_a{v.ir.FPAbsNeg(v.F(fadd.src_a), abs_a, neg_a)};
    const IR::F32 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),
    };
    IR::F32 value{v.ir.FPAdd(op_a, op_b, control)};
    if (sat) {
        value = v.ir.FPSaturate(value);
    }
    v.F(fadd.dest_reg, value);
}

void FADD(TranslatorVisitor& v, u64 insn, const IR::F32& 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, GetFloatReg20(insn));
}

void TranslatorVisitor::FADD_cbuf(u64 insn) {
    FADD(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::FADD_imm(u64 insn) {
    FADD(*this, insn, GetFloatImm20(insn));
}

void TranslatorVisitor::FADD32I(u64 insn) {
    union {
        u64 raw;
        BitField<55, 1, u64> ftz;
        BitField<56, 1, u64> neg_a;
        BitField<54, 1, u64> abs_a;
        BitField<52, 1, u64> cc;
        BitField<53, 1, u64> neg_b;
        BitField<57, 1, u64> abs_b;
    } const fadd32i{insn};

    FADD(*this, insn, false, fadd32i.cc != 0, fadd32i.ftz != 0, FpRounding::RN, GetFloatImm32(insn),
         fadd32i.abs_a != 0, fadd32i.neg_a != 0, fadd32i.abs_b != 0, fadd32i.neg_b != 0);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void FCMP(TranslatorVisitor& v, u64 insn, const IR::U32& src_a, const IR::F32& operand) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_reg;
        BitField<47, 1, u64> ftz;
        BitField<48, 4, FPCompareOp> compare_op;
    } const fcmp{insn};

    const IR::F32 zero{v.ir.Imm32(0.0f)};
    const IR::FpControl control{.fmz_mode = (fcmp.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None)};
    const IR::U1 cmp_result{FloatingPointCompare(v.ir, operand, zero, fcmp.compare_op, control)};
    const IR::U32 src_reg{v.X(fcmp.src_reg)};
    const IR::U32 result{v.ir.Select(cmp_result, src_reg, src_a)};

    v.X(fcmp.dest_reg, result);
}
} // Anonymous namespace

void TranslatorVisitor::FCMP_reg(u64 insn) {
    FCMP(*this, insn, GetReg20(insn), GetFloatReg39(insn));
}

void TranslatorVisitor::FCMP_rc(u64 insn) {
    FCMP(*this, insn, GetReg39(insn), GetFloatCbuf(insn));
}

void TranslatorVisitor::FCMP_cr(u64 insn) {
    FCMP(*this, insn, GetCbuf(insn), GetFloatReg39(insn));
}

void TranslatorVisitor::FCMP_imm(u64 insn) {
    union {
        u64 raw;
        BitField<20, 19, u64> value;
        BitField<56, 1, u64> is_negative;
    } const fcmp{insn};
    const u32 sign_bit{fcmp.is_negative != 0 ? (1U << 31) : 0};
    const u32 value{static_cast<u32>(fcmp.value) << 12};

    FCMP(*this, insn, ir.Imm32(value | sign_bit), GetFloatReg39(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void FSET(TranslatorVisitor& v, u64 insn, const IR::F32& src_b) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> pred;
        BitField<42, 1, u64> neg_pred;
        BitField<43, 1, u64> negate_a;
        BitField<44, 1, u64> abs_b;
        BitField<45, 2, BooleanOp> bop;
        BitField<47, 1, u64> cc;
        BitField<48, 4, FPCompareOp> compare_op;
        BitField<52, 1, u64> bf;
        BitField<53, 1, u64> negate_b;
        BitField<54, 1, u64> abs_a;
        BitField<55, 1, u64> ftz;
    } const fset{insn};

    const IR::F32 op_a{v.ir.FPAbsNeg(v.F(fset.src_a_reg), fset.abs_a != 0, fset.negate_a != 0)};
    const IR::F32 op_b = v.ir.FPAbsNeg(src_b, fset.abs_b != 0, fset.negate_b != 0);
    const IR::FpControl control{
        .no_contraction = false,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = (fset.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };

    IR::U1 pred{v.ir.GetPred(fset.pred)};
    if (fset.neg_pred != 0) {
        pred = v.ir.LogicalNot(pred);
    }
    const IR::U1 cmp_result{FloatingPointCompare(v.ir, op_a, op_b, fset.compare_op, control)};
    const IR::U1 bop_result{PredicateCombine(v.ir, cmp_result, pred, fset.bop)};

    const IR::U32 one_mask{v.ir.Imm32(-1)};
    const IR::U32 fp_one{v.ir.Imm32(0x3f800000)};
    const IR::U32 zero{v.ir.Imm32(0)};
    const IR::U32 pass_result{fset.bf == 0 ? one_mask : fp_one};
    const IR::U32 result{v.ir.Select(bop_result, pass_result, zero)};

    v.X(fset.dest_reg, result);
    if (fset.cc != 0) {
        const IR::U1 is_zero{v.ir.IEqual(result, zero)};
        v.SetZFlag(is_zero);
        if (fset.bf != 0) {
            v.ResetSFlag();
        } else {
            v.SetSFlag(v.ir.LogicalNot(is_zero));
        }
        v.ResetCFlag();
        v.ResetOFlag();
    }
}
} // Anonymous namespace

void TranslatorVisitor::FSET_reg(u64 insn) {
    FSET(*this, insn, GetFloatReg20(insn));
}

void TranslatorVisitor::FSET_cbuf(u64 insn) {
    FSET(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::FSET_imm(u64 insn) {
    FSET(*this, insn, GetFloatImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/frontend/maxwell/translate/impl/common_encoding.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"

namespace Shader::Maxwell {
namespace {
enum class FloatFormat : u64 {
    F16 = 1,
    F32 = 2,
    F64 = 3,
};

enum class RoundingOp : u64 {
    None = 0,
    Pass = 3,
    Round = 8,
    Floor = 9,
    Ceil = 10,
    Trunc = 11,
};

[[nodiscard]] u32 WidthSize(FloatFormat width) {
    switch (width) {
    case FloatFormat::F16:
        return 16;
    case FloatFormat::F32:
        return 32;
    case FloatFormat::F64:
        return 64;
    default:
        throw NotImplementedException("Invalid width {}", width);
    }
}

void F2F(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a, bool abs) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<44, 1, u64> ftz;
        BitField<45, 1, u64> neg;
        BitField<47, 1, u64> cc;
        BitField<50, 1, u64> sat;
        BitField<39, 4, u64> rounding_op;
        BitField<39, 2, FpRounding> rounding;
        BitField<10, 2, FloatFormat> src_size;
        BitField<8, 2, FloatFormat> dst_size;

        [[nodiscard]] RoundingOp RoundingOperation() const {
            constexpr u64 rounding_mask = 0x0B;
            return static_cast<RoundingOp>(rounding_op.Value() & rounding_mask);
        }
    } const f2f{insn};

    if (f2f.cc != 0) {
        throw NotImplementedException("F2F CC");
    }

    IR::F16F32F64 input{v.ir.FPAbsNeg(src_a, abs, f2f.neg != 0)};

    const bool any_fp64{f2f.src_size == FloatFormat::F64 || f2f.dst_size == FloatFormat::F64};
    IR::FpControl fp_control{
        .no_contraction = false,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = (f2f.ftz != 0 && !any_fp64 ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };
    if (f2f.src_size != f2f.dst_size) {
        fp_control.rounding = CastFpRounding(f2f.rounding);
        input = v.ir.FPConvert(WidthSize(f2f.dst_size), input, fp_control);
    } else {
        switch (f2f.RoundingOperation()) {
        case RoundingOp::None:
        case RoundingOp::Pass:
            // Make sure NANs are handled properly
            switch (f2f.src_size) {
            case FloatFormat::F16:
                input = v.ir.FPAdd(input, v.ir.FPConvert(16, v.ir.Imm32(0.0f)), fp_control);
                break;
            case FloatFormat::F32:
                input = v.ir.FPAdd(input, v.ir.Imm32(0.0f), fp_control);
                break;
            case FloatFormat::F64:
                input = v.ir.FPAdd(input, v.ir.Imm64(0.0), fp_control);
                break;
            }
            break;
        case RoundingOp::Round:
            input = v.ir.FPRoundEven(input, fp_control);
            break;
        case RoundingOp::Floor:
            input = v.ir.FPFloor(input, fp_control);
            break;
        case RoundingOp::Ceil:
            input = v.ir.FPCeil(input, fp_control);
            break;
        case RoundingOp::Trunc:
            input = v.ir.FPTrunc(input, fp_control);
            break;
        default:
            throw NotImplementedException("Unimplemented rounding mode {}", f2f.rounding.Value());
        }
    }
    if (f2f.sat != 0 && !any_fp64) {
        input = v.ir.FPSaturate(input);
    }

    switch (f2f.dst_size) {
    case FloatFormat::F16: {
        const IR::F16 imm{v.ir.FPConvert(16, v.ir.Imm32(0.0f))};
        v.X(f2f.dest_reg, v.ir.PackFloat2x16(v.ir.CompositeConstruct(input, imm)));
        break;
    }
    case FloatFormat::F32:
        v.F(f2f.dest_reg, input);
        break;
    case FloatFormat::F64:
        v.D(f2f.dest_reg, input);
        break;
    default:
        throw NotImplementedException("Invalid dest format {}", f2f.dst_size.Value());
    }
}
} // Anonymous namespace

void TranslatorVisitor::F2F_reg(u64 insn) {
    union {
        u64 insn;
        BitField<49, 1, u64> abs;
        BitField<10, 2, FloatFormat> src_size;
        BitField<41, 1, u64> selector;
    } const f2f{insn};

    IR::F16F32F64 src_a;
    switch (f2f.src_size) {
    case FloatFormat::F16: {
        auto [lhs_a, rhs_a]{Extract(ir, GetReg20(insn), Swizzle::H1_H0)};
        src_a = f2f.selector != 0 ? rhs_a : lhs_a;
        break;
    }
    case FloatFormat::F32:
        src_a = GetFloatReg20(insn);
        break;
    case FloatFormat::F64:
        src_a = GetDoubleReg20(insn);
        break;
    default:
        throw NotImplementedException("Invalid dest format {}", f2f.src_size.Value());
    }
    F2F(*this, insn, src_a, f2f.abs != 0);
}

void TranslatorVisitor::F2F_cbuf(u64 insn) {
    union {
        u64 insn;
        BitField<49, 1, u64> abs;
        BitField<10, 2, FloatFormat> src_size;
        BitField<41, 1, u64> selector;
    } const f2f{insn};

    IR::F16F32F64 src_a;
    switch (f2f.src_size) {
    case FloatFormat::F16: {
        auto [lhs_a, rhs_a]{Extract(ir, GetCbuf(insn), Swizzle::H1_H0)};
        src_a = f2f.selector != 0 ? rhs_a : lhs_a;
        break;
    }
    case FloatFormat::F32:
        src_a = GetFloatCbuf(insn);
        break;
    case FloatFormat::F64:
        src_a = GetDoubleCbuf(insn);
        break;
    default:
        throw NotImplementedException("Invalid dest format {}", f2f.src_size.Value());
    }
    F2F(*this, insn, src_a, f2f.abs != 0);
}

void TranslatorVisitor::F2F_imm([[maybe_unused]] u64 insn) {
    union {
        u64 insn;
        BitField<49, 1, u64> abs;
        BitField<10, 2, FloatFormat> src_size;
        BitField<41, 1, u64> selector;
        BitField<20, 19, u64> imm;
        BitField<56, 1, u64> imm_neg;
    } const f2f{insn};

    IR::F16F32F64 src_a;
    switch (f2f.src_size) {
    case FloatFormat::F16: {
        const u32 imm{static_cast<u32>(f2f.imm & 0x0000ffff)};
        const IR::Value vector{ir.UnpackFloat2x16(ir.Imm32(imm | (imm << 16)))};
        src_a = IR::F16{ir.CompositeExtract(vector, f2f.selector != 0 ? 0 : 1)};
        if (f2f.imm_neg != 0) {
            throw NotImplementedException("Neg bit on F16");
        }
        break;
    }
    case FloatFormat::F32:
        src_a = GetFloatImm20(insn);
        break;
    case FloatFormat::F64:
        src_a = GetDoubleImm20(insn);
        break;
    default:
        throw NotImplementedException("Invalid dest format {}", f2f.src_size.Value());
    }
    F2F(*this, insn, src_a, f2f.abs != 0);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <limits>

#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/opcodes.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, 2, Rounding> rounding;
    BitField<41, 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);
    }
}

std::pair<f64, f64> ClampBounds(DestFormat format, bool is_signed) {
    if (is_signed) {
        switch (format) {
        case DestFormat::I16:
            return {static_cast<f64>(std::numeric_limits<s16>::max()),
                    static_cast<f64>(std::numeric_limits<s16>::min())};
        case DestFormat::I32:
            return {static_cast<f64>(std::numeric_limits<s32>::max()),
                    static_cast<f64>(std::numeric_limits<s32>::min())};
        case DestFormat::I64:
            return {static_cast<f64>(std::numeric_limits<s64>::max()),
                    static_cast<f64>(std::numeric_limits<s64>::min())};
        default:
            break;
        }
    } else {
        switch (format) {
        case DestFormat::I16:
            return {static_cast<f64>(std::numeric_limits<u16>::max()),
                    static_cast<f64>(std::numeric_limits<u16>::min())};
        case DestFormat::I32:
            return {static_cast<f64>(std::numeric_limits<u32>::max()),
                    static_cast<f64>(std::numeric_limits<u32>::min())};
        case DestFormat::I64:
            return {static_cast<f64>(std::numeric_limits<u64>::max()),
                    static_cast<f64>(std::numeric_limits<u64>::min())};
        default:
            break;
        }
    }
    throw NotImplementedException("Invalid destination format {}", format);
}

IR::F64 UnpackCbuf(TranslatorVisitor& v, 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 >= 0x4'000 || cbuf.offset < 0) {
        throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset * 4);
    }
    if (cbuf.offset % 2 != 0) {
        throw NotImplementedException("Unaligned F64 constant buffer offset {}", cbuf.offset * 4);
    }
    const IR::U32 binding{v.ir.Imm32(static_cast<u32>(cbuf.binding))};
    const IR::U32 byte_offset{v.ir.Imm32(static_cast<u32>(cbuf.offset) * 4 + 4)};
    const IR::U32 cbuf_data{v.ir.GetCbuf(binding, byte_offset)};
    const IR::Value vector{v.ir.CompositeConstruct(v.ir.Imm32(0U), cbuf_data)};
    return v.ir.PackDouble2x32(vector);
}

void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) {
    // F2I is used to convert from a floating point value to an integer
    const F2I f2i{insn};

    const bool denorm_cares{f2i.src_format != SrcFormat::F16 && f2i.src_format != SrcFormat::F64 &&
                            f2i.dest_format != DestFormat::I64};
    IR::FmzMode fmz_mode{IR::FmzMode::DontCare};
    if (denorm_cares) {
        fmz_mode = f2i.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None;
    }
    const IR::FpControl fp_control{
        .no_contraction = true,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = fmz_mode,
    };
    const IR::F16F32F64 op_a{v.ir.FPAbsNeg(src_a, f2i.abs != 0, f2i.neg != 0)};
    const IR::F16F32F64 rounded_value{[&] {
        switch (f2i.rounding) {
        case Rounding::Round:
            return v.ir.FPRoundEven(op_a, fp_control);
        case Rounding::Floor:
            return v.ir.FPFloor(op_a, fp_control);
        case Rounding::Ceil:
            return v.ir.FPCeil(op_a, fp_control);
        case Rounding::Trunc:
            return v.ir.FPTrunc(op_a, fp_control);
        default:
            throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
        }
    }()};
    const bool is_signed{f2i.is_signed != 0};
    const auto [max_bound, min_bound] = ClampBounds(f2i.dest_format, is_signed);

    IR::F16F32F64 intermediate;
    switch (f2i.src_format) {
    case SrcFormat::F16: {
        const IR::F16 max_val{v.ir.FPConvert(16, v.ir.Imm32(static_cast<f32>(max_bound)))};
        const IR::F16 min_val{v.ir.FPConvert(16, v.ir.Imm32(static_cast<f32>(min_bound)))};
        intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
        break;
    }
    case SrcFormat::F32: {
        const IR::F32 max_val{v.ir.Imm32(static_cast<f32>(max_bound))};
        const IR::F32 min_val{v.ir.Imm32(static_cast<f32>(min_bound))};
        intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
        break;
    }
    case SrcFormat::F64: {
        const IR::F64 max_val{v.ir.Imm64(max_bound)};
        const IR::F64 min_val{v.ir.Imm64(min_bound)};
        intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
        break;
    }
    default:
        throw NotImplementedException("Invalid destination format {}", f2i.dest_format.Value());
    }

    const size_t bitsize{std::max<size_t>(32, BitSize(f2i.dest_format))};
    IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, intermediate)};

    bool handled_special_case = false;
    const bool special_nan_cases =
        (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64);
    if (special_nan_cases) {
        if (f2i.dest_format == DestFormat::I32) {
            constexpr u32 nan_value = 0x8000'0000U;
            handled_special_case = true;
            result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(nan_value), result)};
        } else if (f2i.dest_format == DestFormat::I64) {
            constexpr u64 nan_value = 0x8000'0000'0000'0000ULL;
            handled_special_case = true;
            result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(nan_value), result)};
        }
    }
    if (!handled_special_case && is_signed) {
        if (bitsize != 64) {
            result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0U), result)};
        } else {
            result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(u64{0}), result)};
        }
    }

    if (bitsize == 64) {
        v.L(f2i.dest_reg, result);
    } else {
        v.X(f2i.dest_reg, result);
    }

    if (f2i.cc != 0) {
        throw NotImplementedException("F2I CC");
    }
}
} // Anonymous namespace

void TranslatorVisitor::F2I_reg(u64 insn) {
    union {
        u64 raw;
        F2I base;
        BitField<20, 8, IR::Reg> src_reg;
    } const f2i{insn};

    const IR::F16F32F64 op_a{[&]() -> IR::F16F32F64 {
        switch (f2i.base.src_format) {
        case SrcFormat::F16:
            return IR::F16{ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half)};
        case SrcFormat::F32:
            return F(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 insn) {
    const F2I f2i{insn};
    const IR::F16F32F64 op_a{[&]() -> IR::F16F32F64 {
        switch (f2i.src_format) {
        case SrcFormat::F16:
            return IR::F16{ir.CompositeExtract(ir.UnpackFloat2x16(GetCbuf(insn)), f2i.half)};
        case SrcFormat::F32:
            return GetFloatCbuf(insn);
        case SrcFormat::F64: {
            return UnpackCbuf(*this, insn);
        }
        default:
            throw NotImplementedException("Invalid F2I source format {}", f2i.src_format.Value());
        }
    }()};
    TranslateF2I(*this, insn, op_a);
}

void TranslatorVisitor::F2I_imm(u64) {
    throw NotImplementedException("{}", Opcode::F2I_imm);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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::F32& src_b, const IR::F32& 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 (cc) {
        throw NotImplementedException("FFMA CC");
    }
    const IR::F32 op_a{v.ir.FPAbsNeg(v.F(ffma.src_a), false, neg_a)};
    const IR::F32 op_b{v.ir.FPAbsNeg(src_b, false, neg_b)};
    const IR::F32 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),
    };
    IR::F32 value{v.ir.FPFma(op_a, op_b, op_c, fp_control)};
    if (fmz_mode == FmzMode::FMZ && !sat) {
        // Do not implement FMZ if SAT is enabled, as it does the logic for us.
        // On D3D9 mode, anything * 0 is zero, even NAN and infinity
        const IR::F32 zero{v.ir.Imm32(0.0f)};
        const IR::U1 zero_a{v.ir.FPEqual(op_a, zero)};
        const IR::U1 zero_b{v.ir.FPEqual(op_b, zero)};
        const IR::U1 any_zero{v.ir.LogicalOr(zero_a, zero_b)};
        value = IR::F32{v.ir.Select(any_zero, op_c, value)};
    }
    if (sat) {
        value = v.ir.FPSaturate(value);
    }
    v.F(ffma.dest_reg, value);
}

void FFMA(TranslatorVisitor& v, u64 insn, const IR::F32& src_b, const IR::F32& 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, GetFloatReg20(insn), GetFloatReg39(insn));
}

void TranslatorVisitor::FFMA_rc(u64 insn) {
    FFMA(*this, insn, GetFloatReg39(insn), GetFloatCbuf(insn));
}

void TranslatorVisitor::FFMA_cr(u64 insn) {
    FFMA(*this, insn, GetFloatCbuf(insn), GetFloatReg39(insn));
}

void TranslatorVisitor::FFMA_imm(u64 insn) {
    FFMA(*this, insn, GetFloatImm20(insn), GetFloatReg39(insn));
}

void TranslatorVisitor::FFMA32I(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> src_c; // FFMA32I mirrors the destination and addition register
        BitField<52, 1, u64> cc;
        BitField<53, 2, FmzMode> fmz_mode;
        BitField<55, 1, u64> sat;
        BitField<56, 1, u64> neg_a;
        BitField<57, 1, u64> neg_c;
    } const ffma32i{insn};

    FFMA(*this, insn, GetFloatImm32(insn), F(ffma32i.src_c), ffma32i.neg_a != 0, false,
         ffma32i.neg_c != 0, ffma32i.sat != 0, ffma32i.cc != 0, ffma32i.fmz_mode, FpRounding::RN);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void FMNMX(TranslatorVisitor& v, u64 insn, const IR::F32& src_b) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> pred;
        BitField<42, 1, u64> neg_pred;
        BitField<44, 1, u64> ftz;
        BitField<45, 1, u64> negate_b;
        BitField<46, 1, u64> abs_a;
        BitField<47, 1, u64> cc;
        BitField<48, 1, u64> negate_a;
        BitField<49, 1, u64> abs_b;
    } const fmnmx{insn};

    if (fmnmx.cc) {
        throw NotImplementedException("FMNMX CC");
    }

    const IR::U1 pred{v.ir.GetPred(fmnmx.pred)};
    const IR::F32 op_a{v.ir.FPAbsNeg(v.F(fmnmx.src_a_reg), fmnmx.abs_a != 0, fmnmx.negate_a != 0)};
    const IR::F32 op_b{v.ir.FPAbsNeg(src_b, fmnmx.abs_b != 0, fmnmx.negate_b != 0)};

    const IR::FpControl control{
        .no_contraction = false,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = (fmnmx.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };
    IR::F32 max{v.ir.FPMax(op_a, op_b, control)};
    IR::F32 min{v.ir.FPMin(op_a, op_b, control)};

    if (fmnmx.neg_pred != 0) {
        std::swap(min, max);
    }

    v.F(fmnmx.dest_reg, IR::F32{v.ir.Select(pred, min, max)});
}
} // Anonymous namespace

void TranslatorVisitor::FMNMX_reg(u64 insn) {
    FMNMX(*this, insn, GetFloatReg20(insn));
}

void TranslatorVisitor::FMNMX_cbuf(u64 insn) {
    FMNMX(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::FMNMX_imm(u64 insn) {
    FMNMX(*this, insn, GetFloatImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
enum class Operation : u64 {
    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::F32 op_a{ir.FPAbsNeg(F(mufu.src_reg), mufu.abs != 0, mufu.neg != 0)};
    IR::F32 value{[&]() -> IR::F32 {
        switch (mufu.operation) {
        case Operation::Cos:
            return ir.FPCos(op_a);
        case Operation::Sin:
            return ir.FPSin(op_a);
        case Operation::Ex2:
            return ir.FPExp2(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);
    }

    F(mufu.dest_reg, value);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/ir_emitter.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::F32& 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");
    }
    IR::F32 op_a{v.F(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::F32 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),
    };
    IR::F32 value{v.ir.FPMul(op_a, op_b, fp_control)};
    if (fmz_mode == FmzMode::FMZ && !sat) {
        // Do not implement FMZ if SAT is enabled, as it does the logic for us.
        // On D3D9 mode, anything * 0 is zero, even NAN and infinity
        const IR::F32 zero{v.ir.Imm32(0.0f)};
        const IR::U1 zero_a{v.ir.FPEqual(op_a, zero)};
        const IR::U1 zero_b{v.ir.FPEqual(op_b, zero)};
        const IR::U1 any_zero{v.ir.LogicalOr(zero_a, zero_b)};
        value = IR::F32{v.ir.Select(any_zero, zero, value)};
    }
    if (sat) {
        value = v.ir.FPSaturate(value);
    }
    v.F(fmul.dest_reg, value);
}

void FMUL(TranslatorVisitor& v, u64 insn, const IR::F32& 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;
    } const 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, GetFloatReg20(insn));
}

void TranslatorVisitor::FMUL_cbuf(u64 insn) {
    return FMUL(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::FMUL_imm(u64 insn) {
    return FMUL(*this, insn, GetFloatImm20(insn));
}

void TranslatorVisitor::FMUL32I(u64 insn) {
    union {
        u64 raw;
        BitField<52, 1, u64> cc;
        BitField<53, 2, FmzMode> fmz;
        BitField<55, 1, u64> sat;
    } const fmul32i{insn};

    FMUL(*this, insn, GetFloatImm32(insn), fmul32i.fmz, FpRounding::RN, Scale::None,
         fmul32i.sat != 0, fmul32i.cc != 0, false);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 Mode : u64 {
    SINCOS,
    EX2,
};

void RRO(TranslatorVisitor& v, u64 insn, const IR::F32& src) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<39, 1, Mode> mode;
        BitField<45, 1, u64> neg;
        BitField<49, 1, u64> abs;
    } const rro{insn};

    v.F(rro.dest_reg, v.ir.FPAbsNeg(src, rro.abs != 0, rro.neg != 0));
}
} // Anonymous namespace

void TranslatorVisitor::RRO_reg(u64 insn) {
    RRO(*this, insn, GetFloatReg20(insn));
}

void TranslatorVisitor::RRO_cbuf(u64 insn) {
    RRO(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::RRO_imm(u64) {
    throw NotImplementedException("RRO (imm)");
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/bit_field.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
void FSETP(TranslatorVisitor& v, u64 insn, const IR::F32& src_b) {
    union {
        u64 insn;
        BitField<0, 3, IR::Pred> dest_pred_b;
        BitField<3, 3, IR::Pred> dest_pred_a;
        BitField<6, 1, u64> negate_b;
        BitField<7, 1, u64> abs_a;
        BitField<8, 8, IR::Reg> src_a_reg;
        BitField<39, 3, IR::Pred> bop_pred;
        BitField<42, 1, u64> neg_bop_pred;
        BitField<43, 1, u64> negate_a;
        BitField<44, 1, u64> abs_b;
        BitField<45, 2, BooleanOp> bop;
        BitField<47, 1, u64> ftz;
        BitField<48, 4, FPCompareOp> compare_op;
    } const fsetp{insn};

    const IR::F32 op_a{v.ir.FPAbsNeg(v.F(fsetp.src_a_reg), fsetp.abs_a != 0, fsetp.negate_a != 0)};
    const IR::F32 op_b = v.ir.FPAbsNeg(src_b, fsetp.abs_b != 0, fsetp.negate_b != 0);
    const IR::FpControl control{
        .no_contraction = false,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = (fsetp.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };

    const BooleanOp bop{fsetp.bop};
    const FPCompareOp compare_op{fsetp.compare_op};
    const IR::U1 comparison{FloatingPointCompare(v.ir, op_a, op_b, compare_op, control)};
    const IR::U1 bop_pred{v.ir.GetPred(fsetp.bop_pred, fsetp.neg_bop_pred != 0)};
    const IR::U1 result_a{PredicateCombine(v.ir, comparison, bop_pred, bop)};
    const IR::U1 result_b{PredicateCombine(v.ir, v.ir.LogicalNot(comparison), bop_pred, bop)};
    v.ir.SetPred(fsetp.dest_pred_a, result_a);
    v.ir.SetPred(fsetp.dest_pred_b, result_b);
}
} // Anonymous namespace

void TranslatorVisitor::FSETP_reg(u64 insn) {
    FSETP(*this, insn, GetFloatReg20(insn));
}

void TranslatorVisitor::FSETP_cbuf(u64 insn) {
    FSETP(*this, insn, GetFloatCbuf(insn));
}

void TranslatorVisitor::FSETP_imm(u64 insn) {
    FSETP(*this, insn, GetFloatImm20(insn));
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 {
void TranslatorVisitor::FSWZADD(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<28, 8, u64> swizzle;
        BitField<38, 1, u64> ndv;
        BitField<39, 2, FpRounding> round;
        BitField<44, 1, u64> ftz;
        BitField<47, 1, u64> cc;
    } const fswzadd{insn};

    if (fswzadd.ndv != 0) {
        LOG_WARNING(Shader, "(STUBBED) FSWZADD - NDV mode");
    }

    const IR::F32 src_a{GetFloatReg8(insn)};
    const IR::F32 src_b{GetFloatReg20(insn)};
    const IR::U32 swizzle{ir.Imm32(static_cast<u32>(fswzadd.swizzle))};

    const IR::FpControl fp_control{
        .no_contraction = false,
        .rounding = CastFpRounding(fswzadd.round),
        .fmz_mode = (fswzadd.ftz != 0 ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };

    const IR::F32 result{ir.FSwizzleAdd(src_a, src_b, swizzle, fp_control)};
    F(fswzadd.dest_reg, result);

    if (fswzadd.cc != 0) {
        throw NotImplementedException("FSWZADD CC");
    }
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"

namespace Shader::Maxwell {
namespace {
void HADD2(TranslatorVisitor& v, u64 insn, Merge merge, bool ftz, bool sat, bool abs_a, bool neg_a,
           Swizzle swizzle_a, bool abs_b, bool neg_b, Swizzle swizzle_b, const IR::U32& src_b) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a;
    } const hadd2{insn};

    auto [lhs_a, rhs_a]{Extract(v.ir, v.X(hadd2.src_a), swizzle_a)};
    auto [lhs_b, rhs_b]{Extract(v.ir, src_b, swizzle_b)};
    const bool promotion{lhs_a.Type() != lhs_b.Type()};
    if (promotion) {
        if (lhs_a.Type() == IR::Type::F16) {
            lhs_a = v.ir.FPConvert(32, lhs_a);
            rhs_a = v.ir.FPConvert(32, rhs_a);
        }
        if (lhs_b.Type() == IR::Type::F16) {
            lhs_b = v.ir.FPConvert(32, lhs_b);
            rhs_b = v.ir.FPConvert(32, rhs_b);
        }
    }
    lhs_a = v.ir.FPAbsNeg(lhs_a, abs_a, neg_a);
    rhs_a = v.ir.FPAbsNeg(rhs_a, abs_a, neg_a);

    lhs_b = v.ir.FPAbsNeg(lhs_b, abs_b, neg_b);
    rhs_b = v.ir.FPAbsNeg(rhs_b, abs_b, neg_b);

    const IR::FpControl fp_control{
        .no_contraction = true,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = (ftz ? IR::FmzMode::FTZ : IR::FmzMode::None),
    };
    IR::F16F32F64 lhs{v.ir.FPAdd(lhs_a, lhs_b, fp_control)};
    IR::F16F32F64 rhs{v.ir.FPAdd(rhs_a, rhs_b, fp_control)};
    if (sat) {
        lhs = v.ir.FPSaturate(lhs);
        rhs = v.ir.FPSaturate(rhs);
    }
    if (promotion) {
        lhs = v.ir.FPConvert(16, lhs);
        rhs = v.ir.FPConvert(16, rhs);
    }
    v.X(hadd2.dest_reg, MergeResult(v.ir, hadd2.dest_reg, lhs, rhs, merge));
}

void HADD2(TranslatorVisitor& v, u64 insn, bool sat, bool abs_b, bool neg_b, Swizzle swizzle_b,
           const IR::U32& src_b) {
    union {
        u64 raw;
        BitField<49, 2, Merge> merge;
        BitField<39, 1, u64> ftz;
        BitField<43, 1, u64> neg_a;
        BitField<44, 1, u64> abs_a;
        BitField<47, 2, Swizzle> swizzle_a;
    } const hadd2{insn};

    HADD2(v, insn, hadd2.merge, hadd2.ftz != 0, sat, hadd2.abs_a != 0, hadd2.neg_a != 0,
          hadd2.swizzle_a, abs_b, neg_b, swizzle_b, src_b);
}
} // Anonymous namespace

void TranslatorVisitor::HADD2_reg(u64 insn) {
    union {
        u64 raw;
        BitField<32, 1, u64> sat;
        BitField<31, 1, u64> neg_b;
        BitField<30, 1, u64> abs_b;
        BitField<28, 2, Swizzle> swizzle_b;
    } const hadd2{insn};

    HADD2(*this, insn, hadd2.sat != 0, hadd2.abs_b != 0, hadd2.neg_b != 0, hadd2.swizzle_b,
          GetReg20(insn));
}

void TranslatorVisitor::HADD2_cbuf(u64 insn) {
    union {
        u64 raw;
        BitField<52, 1, u64> sat;
        BitField<56, 1, u64> neg_b;
        BitField<54, 1, u64> abs_b;
    } const hadd2{insn};

    HADD2(*this, insn, hadd2.sat != 0, hadd2.abs_b != 0, hadd2.neg_b != 0, Swizzle::F32,
          GetCbuf(insn));
}

void TranslatorVisitor::HADD2_imm(u64 insn) {
    union {
        u64 raw;
        BitField<52, 1, u64> sat;
        BitField<56, 1, u64> neg_high;
        BitField<30, 9, u64> high;
        BitField<29, 1, u64> neg_low;
        BitField<20, 9, u64> low;
    } const hadd2{insn};

    const u32 imm{
        static_cast<u32>(hadd2.low << 6) | static_cast<u32>((hadd2.neg_low != 0 ? 1 : 0) << 15) |
        static_cast<u32>(hadd2.high << 22) | static_cast<u32>((hadd2.neg_high != 0 ? 1 : 0) << 31)};
    HADD2(*this, insn, hadd2.sat != 0, false, false, Swizzle::H1_H0, ir.Imm32(imm));
}

void TranslatorVisitor::HADD2_32I(u64 insn) {
    union {
        u64 raw;
        BitField<55, 1, u64> ftz;
        BitField<52, 1, u64> sat;
        BitField<56, 1, u64> neg_a;
        BitField<53, 2, Swizzle> swizzle_a;
        BitField<20, 32, u64> imm32;
    } const hadd2{insn};

    const u32 imm{static_cast<u32>(hadd2.imm32)};
    HADD2(*this, insn, Merge::H1_H0, hadd2.ftz != 0, hadd2.sat != 0, false, hadd2.neg_a != 0,
          hadd2.swizzle_a, false, false, Swizzle::H1_H0, ir.Imm32(imm));
}
} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"

namespace Shader::Maxwell {
namespace {
void HFMA2(TranslatorVisitor& v, u64 insn, Merge merge, Swizzle swizzle_a, bool neg_b, bool neg_c,
           Swizzle swizzle_b, Swizzle swizzle_c, const IR::U32& src_b, const IR::U32& src_c,
           bool sat, HalfPrecision precision) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_a;
    } const hfma2{insn};

    auto [lhs_a, rhs_a]{Extract(v.ir, v.X(hfma2.src_a), swizzle_a)};
    auto [lhs_b, rhs_b]{Extract(v.ir, src_b, swizzle_b)};
    auto [lhs_c, rhs_c]{Extract(v.ir, src_c, swizzle_c)};
    const bool promotion{lhs_a.Type() != lhs_b.Type() || lhs_a.Type() != lhs_c.Type()};
    if (promotion) {
        if (lhs_a.Type() == IR::Type::F16) {
            lhs_a = v.ir.FPConvert(32, lhs_a);
            rhs_a = v.ir.FPConvert(32, rhs_a);
        }
        if (lhs_b.Type() == IR::Type::F16) {
            lhs_b = v.ir.FPConvert(32, lhs_b);
            rhs_b = v.ir.FPConvert(32, rhs_b);
        }
        if (lhs_c.Type() == IR::Type::F16) {
            lhs_c = v.ir.FPConvert(32, lhs_c);
            rhs_c = v.ir.FPConvert(32, rhs_c);
        }
    }

    lhs_b = v.ir.FPAbsNeg(lhs_b, false, neg_b);
    rhs_b = v.ir.FPAbsNeg(rhs_b, false, neg_b);

    lhs_c = v.ir.FPAbsNeg(lhs_c, false, neg_c);
    rhs_c = v.ir.FPAbsNeg(rhs_c, false, neg_c);

    const IR::FpControl fp_control{
        .no_contraction = true,
        .rounding = IR::FpRounding::DontCare,
        .fmz_mode = HalfPrecision2FmzMode(precision),
    };
    IR::F16F32F64 lhs{v.ir.FPFma(lhs_a, lhs_b, lhs_c, fp_control)};
    IR::F16F32F64 rhs{v.ir.FPFma(rhs_a, rhs_b, rhs_c, fp_control)};
    if (precision == HalfPrecision::FMZ && !sat) {
        // Do not implement FMZ if SAT is enabled, as it does the logic for us.
        // On D3D9 mode, anything * 0 is zero, even NAN and infinity
        const IR::F32 zero{v.ir.Imm32(0.0f)};
        const IR::U1 lhs_zero_a{v.ir.FPEqual(lhs_a, zero)};
        const IR::U1 lhs_zero_b{v.ir.FPEqual(lhs_b, zero)};
        const IR::U1 lhs_any_zero{v.ir.LogicalOr(lhs_zero_a, lhs_zero_b)};
        lhs = IR::F16F32F64{v.ir.Select(lhs_any_zero, lhs_c, lhs)};

        const IR::U1 rhs_zero_a{v.ir.FPEqual(rhs_a, zero)};
        const IR::U1 rhs_zero_b{v.ir.FPEqual(rhs_b, zero)};
        const IR::U1 rhs_any_zero{v.ir.LogicalOr(rhs_zero_a, rhs_zero_b)};
        rhs = IR::F16F32F64{v.ir.Select(rhs_any_zero, rhs_c, rhs)};
    }
    if (sat) {
        lhs = v.ir.FPSaturate(lhs);
        rhs = v.ir.FPSaturate(rhs);
    }
    if (promotion) {
        lhs = v.ir.FPConvert(16, lhs);
        rhs = v.ir.FPConvert(16, rhs);
    }
    v.X(hfma2.dest_reg, MergeResult(v.ir, hfma2.dest_reg, lhs, rhs, merge));
}

void HFMA2(TranslatorVisitor& v, u64 insn, bool neg_b, bool neg_c, Swizzle swizzle_b,
           Swizzle swizzle_c, const IR::U32& src_b, const IR::U32& src_c, bool sat,
           HalfPrecision precision) {
    union {
        u64 raw;
        BitField<47, 2, Swizzle> swizzle_a;
        BitField<49, 2, Merge> merge;
    } const hfma2{insn};

    HFMA2(v, insn, hfma2.merge, hfma2.swizzle_a, neg_b, neg_c, swizzle_b, swizzle_c, src_b, src_c,
          sat, precision);
}
} // Anonymous namespace

void TranslatorVisitor::HFMA2_reg(u64 insn) {
    union {
        u64 raw;
        BitField<28, 2, Swizzle> swizzle_b;
        BitField<32, 1, u64> saturate;
        BitField<31, 1, u64> neg_b;
        BitField<30, 1, u64> neg_c;
        BitField<35, 2, Swizzle> swizzle_c;
        BitField<37, 2, HalfPrecision> precision;
    } const hfma2{insn};

    HFMA2(*this, insn, hfma2.neg_b != 0, hfma2.neg_c != 0, hfma2.swizzle_b, hfma2.swizzle_c,
          GetReg20(insn), GetReg39(insn), hfma2.saturate != 0, hfma2.precision);
}

void TranslatorVisitor::HFMA2_rc(u64 insn) {
    union {
        u64 raw;
        BitField<51, 1, u64> neg_c;
        BitField<52, 1, u64> saturate;
        BitField<53, 2, Swizzle> swizzle_b;
        BitField<56, 1, u64> neg_b;
        BitField<57, 2, HalfPrecision> precision;
    } const hfma2{insn};

    HFMA2(*this, insn, hfma2.neg_b != 0, hfma2.neg_c != 0, hfma2.swizzle_b, Swizzle::F32,
          GetReg39(insn), GetCbuf(insn), hfma2.saturate != 0, hfma2.precision);
}

void TranslatorVisitor::HFMA2_cr(u64 insn) {
    union {
        u64 raw;
        BitField<51, 1, u64> neg_c;
        BitField<52, 1, u64> saturate;
        BitField<53, 2, Swizzle> swizzle_c;
        BitField<56, 1, u64> neg_b;
        BitField<57, 2, HalfPrecision> precision;
    } const hfma2{insn};

    HFMA2(*this, insn, hfma2.neg_b != 0, hfma2.neg_c != 0, Swizzle::F32, hfma2.swizzle_c,
          GetCbuf(insn), GetReg39(insn), hfma2.saturate != 0, hfma2.precision);
}

void TranslatorVisitor::HFMA2_imm(u64 insn) {
    union {
        u64 raw;
        BitField<51, 1, u64> neg_c;
        BitField<52, 1, u64> saturate;
        BitField<53, 2, Swizzle> swizzle_c;

        BitField<56, 1, u64> neg_high;
        BitField<30, 9, u64> high;
        BitField<29, 1, u64> neg_low;
        BitField<20, 9, u64> low;
        BitField<57, 2, HalfPrecision> precision;
    } const hfma2{insn};

    const u32 imm{
        static_cast<u32>(hfma2.low << 6) | static_cast<u32>((hfma2.neg_low != 0 ? 1 : 0) << 15) |
        static_cast<u32>(hfma2.high << 22) | static_cast<u32>((hfma2.neg_high != 0 ? 1 : 0) << 31)};

    HFMA2(*this, insn, false, hfma2.neg_c != 0, Swizzle::H1_H0, hfma2.swizzle_c, ir.Imm32(imm),
          GetReg39(insn), hfma2.saturate != 0, hfma2.precision);
}

void TranslatorVisitor::HFMA2_32I(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> src_c;
        BitField<20, 32, u64> imm32;
        BitField<52, 1, u64> neg_c;
        BitField<53, 2, Swizzle> swizzle_a;
        BitField<55, 2, HalfPrecision> precision;
    } const hfma2{insn};

    const u32 imm{static_cast<u32>(hfma2.imm32)};
    HFMA2(*this, insn, Merge::H1_H0, hfma2.swizzle_a, false, hfma2.neg_c != 0, Swizzle::H1_H0,
          Swizzle::H1_H0, ir.Imm32(imm), X(hfma2.src_c), false, hfma2.precision);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/frontend/maxwell/translate/impl/half_floating_point_helper.h"

namespace Shader::Maxwell {

IR::FmzMode HalfPrecision2FmzMode(HalfPrecision precision) {
    switch (precision) {
    case HalfPrecision::None:
        return IR::FmzMode::None;
    case HalfPrecision::FTZ:
        return IR::FmzMode::FTZ;
    case HalfPrecision::FMZ:
        return IR::FmzMode::FMZ;
    default:
        return IR::FmzMode::DontCare;
    }
}

std::pair<IR::F16F32F64, IR::F16F32F64> Extract(IR::IREmitter& ir, IR::U32 value, Swizzle swizzle) {
    switch (swizzle) {
    case Swizzle::H1_H0: {
        const IR::Value vector{ir.UnpackFloat2x16(value)};
        return {IR::F16{ir.CompositeExtract(vector, 0)}, IR::F16{ir.CompositeExtract(vector, 1)}};
    }
    case Swizzle::H0_H0: {
        const IR::F16 scalar{ir.CompositeExtract(ir.UnpackFloat2x16(value), 0)};
        return {scalar, scalar};
    }
    case Swizzle::H1_H1: {
        const IR::F16 scalar{ir.CompositeExtract(ir.UnpackFloat2x16(value), 1)};
        return {scalar, scalar};
    }
    case Swizzle::F32: {
        const IR::F32 scalar{ir.BitCast<IR::F32>(value)};
        return {scalar, scalar};
    }
    }
    throw InvalidArgument("Invalid swizzle {}", swizzle);
}

IR::U32 MergeResult(IR::IREmitter& ir, IR::Reg dest, const IR::F16& lhs, const IR::F16& rhs,
                    Merge merge) {
    switch (merge) {
    case Merge::H1_H0:
        return ir.PackFloat2x16(ir.CompositeConstruct(lhs, rhs));
    case Merge::F32:
        return ir.BitCast<IR::U32, IR::F32>(ir.FPConvert(32, lhs));
    case Merge::MRG_H0:
    case Merge::MRG_H1: {
        const IR::Value vector{ir.UnpackFloat2x16(ir.GetReg(dest))};
        const bool is_h0{merge == Merge::MRG_H0};
        const IR::F16 insert{ir.FPConvert(16, is_h0 ? lhs : rhs)};
        return ir.PackFloat2x16(ir.CompositeInsert(vector, insert, is_h0 ? 0 : 1));
    }
    }
    throw InvalidArgument("Invalid merge {}", merge);
}

} // namespace Shader::Maxwell


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {

enum class Merge : u64 {
    H1_H0,
    F32,
    MRG_H0,
    MRG_H1,
};

enum class Swizzle : u64 {
    H1_H0,
    F32,
    H0_H0,
    H1_H1,
};

enum class HalfPrecision : u64 {
    None = 0,
    FTZ = 1,
    FMZ = 2,
};

IR::FmzMode HalfPrecision2FmzMode(HalfPrecision precision);

std::pair<IR::F16F32F64, IR::F16F32F64> Extract(IR::IREmitter& ir, IR::U32 value, Swizzle swizzle);

IR::U32 MergeResult(IR::IREmitter& ir, IR::Reg dest, const IR::F16& lhs, const IR::F16& rhs,
                    Merge merge);

} // namespace Shader::Maxwell