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 {
enum class LogicalOp : u64 {
    AND,
    OR,
    XOR,
    PASS_B,
};

[[nodiscard]] IR::U32 LogicalOperation(IR::IREmitter& ir, const IR::U32& operand_1,
                                       const IR::U32& operand_2, LogicalOp op) {
    switch (op) {
    case LogicalOp::AND:
        return ir.BitwiseAnd(operand_1, operand_2);
    case LogicalOp::OR:
        return ir.BitwiseOr(operand_1, operand_2);
    case LogicalOp::XOR:
        return ir.BitwiseXor(operand_1, operand_2);
    case LogicalOp::PASS_B:
        return operand_2;
    default:
        throw NotImplementedException("Invalid Logical operation {}", op);
    }
}

void LOP(TranslatorVisitor& v, u64 insn, IR::U32 op_b, bool x, bool cc, bool inv_a, bool inv_b,
         LogicalOp bit_op, std::optional<PredicateOp> pred_op = std::nullopt,
         IR::Pred dest_pred = IR::Pred::PT) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_reg;
    } const lop{insn};

    if (x) {
        throw NotImplementedException("X");
    }
    IR::U32 op_a{v.X(lop.src_reg)};
    if (inv_a != 0) {
        op_a = v.ir.BitwiseNot(op_a);
    }
    if (inv_b != 0) {
        op_b = v.ir.BitwiseNot(op_b);
    }

    const IR::U32 result{LogicalOperation(v.ir, op_a, op_b, bit_op)};
    if (pred_op) {
        const IR::U1 pred_result{PredicateOperation(v.ir, result, *pred_op)};
        v.ir.SetPred(dest_pred, pred_result);
    }
    if (cc) {
        if (bit_op == LogicalOp::PASS_B) {
            v.SetZFlag(v.ir.IEqual(result, v.ir.Imm32(0)));
            v.SetSFlag(v.ir.ILessThan(result, v.ir.Imm32(0), true));
        } else {
            v.SetZFlag(v.ir.GetZeroFromOp(result));
            v.SetSFlag(v.ir.GetSignFromOp(result));
        }
        v.ResetCFlag();
        v.ResetOFlag();
    }
    v.X(lop.dest_reg, result);
}

void LOP(TranslatorVisitor& v, u64 insn, const IR::U32& op_b) {
    union {
        u64 insn;
        BitField<39, 1, u64> inv_a;
        BitField<40, 1, u64> inv_b;
        BitField<41, 2, LogicalOp> bit_op;
        BitField<43, 1, u64> x;
        BitField<44, 2, PredicateOp> pred_op;
        BitField<47, 1, u64> cc;
        BitField<48, 3, IR::Pred> dest_pred;
    } const lop{insn};

    LOP(v, insn, op_b, lop.x != 0, lop.cc != 0, lop.inv_a != 0, lop.inv_b != 0, lop.bit_op,
        lop.pred_op, lop.dest_pred);
}
} // Anonymous namespace

void TranslatorVisitor::LOP_reg(u64 insn) {
    LOP(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::LOP_cbuf(u64 insn) {
    LOP(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::LOP_imm(u64 insn) {
    LOP(*this, insn, GetImm20(insn));
}

void TranslatorVisitor::LOP32I(u64 insn) {
    union {
        u64 raw;
        BitField<53, 2, LogicalOp> bit_op;
        BitField<57, 1, u64> x;
        BitField<52, 1, u64> cc;
        BitField<55, 1, u64> inv_a;
        BitField<56, 1, u64> inv_b;
    } const lop32i{insn};

    LOP(*this, insn, GetImm32(insn), lop32i.x != 0, lop32i.cc != 0, lop32i.inv_a != 0,
        lop32i.inv_b != 0, lop32i.bit_op);
}
} // 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 {
// https://forums.developer.nvidia.com/t/reverse-lut-for-lop3-lut/110651
// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table)
IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c,
                 u64 ttbl) {
    switch (ttbl) {
        // generated code, do not edit manually
    case 0:
        return ir.Imm32(0);
    case 1:
        return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c)));
    case 2:
        return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
    case 3:
        return ir.BitwiseNot(ir.BitwiseOr(a, b));
    case 4:
        return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
    case 5:
        return ir.BitwiseNot(ir.BitwiseOr(a, c));
    case 6:
        return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
    case 7:
        return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c)));
    case 8:
        return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
    case 9:
        return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c)));
    case 10:
        return ir.BitwiseAnd(c, ir.BitwiseNot(a));
    case 11:
        return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b)));
    case 12:
        return ir.BitwiseAnd(b, ir.BitwiseNot(a));
    case 13:
        return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c)));
    case 14:
        return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
    case 15:
        return ir.BitwiseNot(a);
    case 16:
        return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
    case 17:
        return ir.BitwiseNot(ir.BitwiseOr(b, c));
    case 18:
        return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
    case 19:
        return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c)));
    case 20:
        return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
    case 21:
        return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
    case 22:
        return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
    case 23:
        return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)),
                             ir.BitwiseNot(a));
    case 24:
        return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
    case 25:
        return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)));
    case 26:
        return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
    case 27:
        return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
    case 28:
        return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
    case 29:
        return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
    case 30:
        return ir.BitwiseXor(a, ir.BitwiseOr(b, c));
    case 31:
        return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)));
    case 32:
        return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
    case 33:
        return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
    case 34:
        return ir.BitwiseAnd(c, ir.BitwiseNot(b));
    case 35:
        return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
    case 36:
        return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c));
    case 37:
        return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)));
    case 38:
        return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
    case 39:
        return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
    case 40:
        return ir.BitwiseAnd(c, ir.BitwiseXor(a, b));
    case 41:
        return ir.BitwiseXor(ir.BitwiseOr(a, b),
                             ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)));
    case 42:
        return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
    case 43:
        return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)),
                             ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
    case 44:
        return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b));
    case 45:
        return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
    case 46:
        return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c));
    case 47:
        return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a));
    case 48:
        return ir.BitwiseAnd(a, ir.BitwiseNot(b));
    case 49:
        return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c)));
    case 50:
        return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
    case 51:
        return ir.BitwiseNot(b);
    case 52:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
    case 53:
        return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
    case 54:
        return ir.BitwiseXor(b, ir.BitwiseOr(a, c));
    case 55:
        return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)));
    case 56:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b));
    case 57:
        return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
    case 58:
        return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c));
    case 59:
        return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b));
    case 60:
        return ir.BitwiseXor(a, b);
    case 61:
        return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b));
    case 62:
        return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b));
    case 63:
        return ir.BitwiseNot(ir.BitwiseAnd(a, b));
    case 64:
        return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
    case 65:
        return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
    case 66:
        return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c));
    case 67:
        return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)));
    case 68:
        return ir.BitwiseAnd(b, ir.BitwiseNot(c));
    case 69:
        return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
    case 70:
        return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
    case 71:
        return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
    case 72:
        return ir.BitwiseAnd(b, ir.BitwiseXor(a, c));
    case 73:
        return ir.BitwiseXor(ir.BitwiseOr(a, c),
                             ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)));
    case 74:
        return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c));
    case 75:
        return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
    case 76:
        return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
    case 77:
        return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)),
                             ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
    case 78:
        return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c));
    case 79:
        return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a));
    case 80:
        return ir.BitwiseAnd(a, ir.BitwiseNot(c));
    case 81:
        return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b)));
    case 82:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
    case 83:
        return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
    case 84:
        return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
    case 85:
        return ir.BitwiseNot(c);
    case 86:
        return ir.BitwiseXor(c, ir.BitwiseOr(a, b));
    case 87:
        return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)));
    case 88:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c));
    case 89:
        return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
    case 90:
        return ir.BitwiseXor(a, c);
    case 91:
        return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c));
    case 92:
        return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b));
    case 93:
        return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c));
    case 94:
        return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c));
    case 95:
        return ir.BitwiseNot(ir.BitwiseAnd(a, c));
    case 96:
        return ir.BitwiseAnd(a, ir.BitwiseXor(b, c));
    case 97:
        return ir.BitwiseXor(ir.BitwiseOr(b, c),
                             ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)));
    case 98:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c));
    case 99:
        return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
    case 100:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c));
    case 101:
        return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
    case 102:
        return ir.BitwiseXor(b, c);
    case 103:
        return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c));
    case 104:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b)));
    case 105:
        return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
    case 106:
        return ir.BitwiseXor(c, ir.BitwiseAnd(a, b));
    case 107:
        return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)),
                             ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 108:
        return ir.BitwiseXor(b, ir.BitwiseAnd(a, c));
    case 109:
        return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)),
                             ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 110:
        return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
    case 111:
        return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
    case 112:
        return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
    case 113:
        return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)),
                             ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
    case 114:
        return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c));
    case 115:
        return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b));
    case 116:
        return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b));
    case 117:
        return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c));
    case 118:
        return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
    case 119:
        return ir.BitwiseNot(ir.BitwiseAnd(b, c));
    case 120:
        return ir.BitwiseXor(a, ir.BitwiseAnd(b, c));
    case 121:
        return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)),
                             ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 122:
        return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
    case 123:
        return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
    case 124:
        return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
    case 125:
        return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
    case 126:
        return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
    case 127:
        return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)));
    case 128:
        return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c));
    case 129:
        return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
    case 130:
        return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 131:
        return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 132:
        return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 133:
        return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 134:
        return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
    case 135:
        return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
    case 136:
        return ir.BitwiseAnd(b, c);
    case 137:
        return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 138:
        return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
    case 139:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
    case 140:
        return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
    case 141:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c)));
    case 142:
        return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
    case 143:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
    case 144:
        return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 145:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 146:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
    case 147:
        return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
    case 148:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
    case 149:
        return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
    case 150:
        return ir.BitwiseXor(a, ir.BitwiseXor(b, c));
    case 151:
        return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)),
                            ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
    case 152:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 153:
        return ir.BitwiseXor(b, ir.BitwiseNot(c));
    case 154:
        return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
    case 155:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)));
    case 156:
        return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
    case 157:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)));
    case 158:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c)));
    case 159:
        return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c)));
    case 160:
        return ir.BitwiseAnd(a, c);
    case 161:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 162:
        return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
    case 163:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
    case 164:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 165:
        return ir.BitwiseXor(a, ir.BitwiseNot(c));
    case 166:
        return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
    case 167:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)));
    case 168:
        return ir.BitwiseAnd(c, ir.BitwiseOr(a, b));
    case 169:
        return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
    case 170:
        return c;
    case 171:
        return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
    case 172:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
    case 173:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 174:
        return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
    case 175:
        return ir.BitwiseOr(c, ir.BitwiseNot(a));
    case 176:
        return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
    case 177:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c)));
    case 178:
        return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
    case 179:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
    case 180:
        return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
    case 181:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)));
    case 182:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c)));
    case 183:
        return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c)));
    case 184:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
    case 185:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 186:
        return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
    case 187:
        return ir.BitwiseOr(c, ir.BitwiseNot(b));
    case 188:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b));
    case 189:
        return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 190:
        return ir.BitwiseOr(c, ir.BitwiseXor(a, b));
    case 191:
        return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
    case 192:
        return ir.BitwiseAnd(a, b);
    case 193:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 194:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 195:
        return ir.BitwiseXor(a, ir.BitwiseNot(b));
    case 196:
        return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
    case 197:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c)));
    case 198:
        return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
    case 199:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)));
    case 200:
        return ir.BitwiseAnd(b, ir.BitwiseOr(a, c));
    case 201:
        return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
    case 202:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
    case 203:
        return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 204:
        return b;
    case 205:
        return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
    case 206:
        return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
    case 207:
        return ir.BitwiseOr(b, ir.BitwiseNot(a));
    case 208:
        return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
    case 209:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c)));
    case 210:
        return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
    case 211:
        return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)));
    case 212:
        return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
    case 213:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
    case 214:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b)));
    case 215:
        return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b)));
    case 216:
        return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
    case 217:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 218:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c));
    case 219:
        return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 220:
        return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
    case 221:
        return ir.BitwiseOr(b, ir.BitwiseNot(c));
    case 222:
        return ir.BitwiseOr(b, ir.BitwiseXor(a, c));
    case 223:
        return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
    case 224:
        return ir.BitwiseAnd(a, ir.BitwiseOr(b, c));
    case 225:
        return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
    case 226:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
    case 227:
        return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 228:
        return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
    case 229:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 230:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c));
    case 231:
        return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
    case 232:
        return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
    case 233:
        return ir.BitwiseOr(ir.BitwiseAnd(a, b),
                            ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)));
    case 234:
        return ir.BitwiseOr(c, ir.BitwiseAnd(a, b));
    case 235:
        return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
    case 236:
        return ir.BitwiseOr(b, ir.BitwiseAnd(a, c));
    case 237:
        return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
    case 238:
        return ir.BitwiseOr(b, c);
    case 239:
        return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
    case 240:
        return a;
    case 241:
        return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
    case 242:
        return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
    case 243:
        return ir.BitwiseOr(a, ir.BitwiseNot(b));
    case 244:
        return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
    case 245:
        return ir.BitwiseOr(a, ir.BitwiseNot(c));
    case 246:
        return ir.BitwiseOr(a, ir.BitwiseXor(b, c));
    case 247:
        return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
    case 248:
        return ir.BitwiseOr(a, ir.BitwiseAnd(b, c));
    case 249:
        return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
    case 250:
        return ir.BitwiseOr(a, c);
    case 251:
        return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
    case 252:
        return ir.BitwiseOr(a, b);
    case 253:
        return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
    case 254:
        return ir.BitwiseOr(a, ir.BitwiseOr(b, c));
    case 255:
        return ir.Imm32(0xFFFFFFFF);
        // end of generated code
    }
    throw NotImplementedException("LOP3 with out of range ttbl");
}

IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) {
    union {
        u64 insn;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_reg;
        BitField<47, 1, u64> cc;
    } const lop3{insn};

    if (lop3.cc != 0) {
        throw NotImplementedException("LOP3 CC");
    }

    const IR::U32 op_a{v.X(lop3.src_reg)};
    const IR::U32 result{ApplyLUT(v.ir, op_a, op_b, op_c, lut)};
    v.X(lop3.dest_reg, result);
    return result;
}

u64 GetLut48(u64 insn) {
    union {
        u64 raw;
        BitField<48, 8, u64> lut;
    } const lut{insn};
    return lut.lut;
}
} // Anonymous namespace

void TranslatorVisitor::LOP3_reg(u64 insn) {
    union {
        u64 insn;
        BitField<28, 8, u64> lut;
        BitField<38, 1, u64> x;
        BitField<36, 2, PredicateOp> pred_op;
        BitField<48, 3, IR::Pred> pred;
    } const lop3{insn};

    if (lop3.x != 0) {
        throw NotImplementedException("LOP3 X");
    }
    const IR::U32 result{LOP3(*this, insn, GetReg20(insn), GetReg39(insn), lop3.lut)};
    const IR::U1 pred_result{PredicateOperation(ir, result, lop3.pred_op)};
    ir.SetPred(lop3.pred, pred_result);
}

void TranslatorVisitor::LOP3_cbuf(u64 insn) {
    LOP3(*this, insn, GetCbuf(insn), GetReg39(insn), GetLut48(insn));
}

void TranslatorVisitor::LOP3_imm(u64 insn) {
    LOP3(*this, insn, GetImm20(insn), GetReg39(insn), GetLut48(insn));
}
} // namespace Shader::Maxwell

repo_name: litucks/torzu

license: mit


code:
# SPDX-FileCopyrightText: 2022 degasus <markus@selfnet.de>
# SPDX-License-Identifier: WTFPL

from itertools import product

# The primitive instructions
OPS = {
    'ir.BitwiseAnd({}, {})' : (2, 1, lambda a,b: a&b),
    'ir.BitwiseOr({}, {})' : (2, 1, lambda a,b: a|b),
    'ir.BitwiseXor({}, {})' : (2, 1, lambda a,b: a^b),
    'ir.BitwiseNot({})' : (1, 0.1, lambda a: (~a) & 255), # Only tiny cost, as this can often inlined in other instructions
}

# Our database of combination of instructions
optimized_calls = {}
def cmp(lhs, rhs):
    if lhs is None: # new entry
        return True
    if lhs[3] > rhs[3]: # costs
        return True
    if lhs[3] < rhs[3]: # costs
        return False
    if len(lhs[0]) > len(rhs[0]): # string len
        return True
    if len(lhs[0]) < len(rhs[0]): # string len
        return False
    if lhs[0] > rhs[0]: # string sorting
        return True
    if lhs[0] < rhs[0]: # string sorting
        return False
    assert lhs == rhs, "redundant instruction, bug in brute force"
    return False
def register(imm, instruction, count, latency):
    # Use the sum of instruction count and latency as costs to evaluate which combination is best
    costs = count + latency

    old = optimized_calls.get(imm, None)
    new = (instruction, count, latency, costs)

    # Update if new or better
    if cmp(old, new):
        optimized_calls[imm] = new
        return True

    return False

# Constants: 0, 1 (for free)
register(0, 'ir.Imm32(0)', 0, 0)
register(255, 'ir.Imm32(0xFFFFFFFF)', 0, 0)

# Inputs: a, b, c (for free)
ta = 0xF0
tb = 0xCC
tc = 0xAA
inputs = {
    ta : 'a',
    tb : 'b',
    tc : 'c',
}
for imm, instruction in inputs.items():
    register(imm, instruction, 0, 0)
    register((~imm) & 255, 'ir.BitwiseNot({})'.format(instruction), 0.099, 0.099) # slightly cheaper NEG on inputs

# Try to combine two values from the db with an instruction.
# If it is better than the old method, update it.
while True:
    registered = 0
    calls_copy = optimized_calls.copy()
    for OP, (argc, cost, f) in OPS.items():
        for args in product(calls_copy.items(), repeat=argc):
            # unpack(transponse) the arrays
            imm = [arg[0] for arg in args]
            value = [arg[1][0] for arg in args]
            count = [arg[1][1] for arg in args]
            latency = [arg[1][2] for arg in args]

            registered += register(
                f(*imm),
                OP.format(*value),
                sum(count) + cost,
                max(latency) + cost)
    if registered == 0:
        # No update at all? So terminate
        break

# Hacky output. Please improve me to output valid C++ instead.
s = """    case {imm}:
        return {op};"""
for imm in range(256):
    print(s.format(imm=imm, op=optimized_calls[imm][0]))


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

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

namespace Shader::Maxwell {
namespace {
enum class Mode : u64 {
    PR,
    CC,
};
} // Anonymous namespace

void TranslatorVisitor::P2R_reg(u64) {
    throw NotImplementedException("P2R (reg)");
}

void TranslatorVisitor::P2R_cbuf(u64) {
    throw NotImplementedException("P2R (cbuf)");
}

void TranslatorVisitor::P2R_imm(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src;
        BitField<40, 1, Mode> mode;
        BitField<41, 2, u64> byte_selector;
    } const p2r{insn};

    const u32 mask{GetImm20(insn).U32()};
    const bool pr_mode{p2r.mode == Mode::PR};
    const u32 num_items{pr_mode ? 7U : 4U};
    const u32 offset{static_cast<u32>(p2r.byte_selector) * 8};
    IR::U32 insert{ir.Imm32(0)};
    for (u32 index = 0; index < num_items; ++index) {
        if (((mask >> index) & 1) == 0) {
            continue;
        }
        const IR::U1 cond{[this, index, pr_mode] {
            if (pr_mode) {
                return ir.GetPred(IR::Pred{index});
            }
            switch (index) {
            case 0:
                return ir.GetZFlag();
            case 1:
                return ir.GetSFlag();
            case 2:
                return ir.GetCFlag();
            case 3:
                return ir.GetOFlag();
            }
            throw LogicError("Unreachable P2R index");
        }()};
        const IR::U32 bit{ir.Select(cond, ir.Imm32(1U << (index + offset)), ir.Imm32(0))};
        insert = ir.BitwiseOr(insert, bit);
    }
    const IR::U32 masked_out{ir.BitwiseAnd(X(p2r.src), ir.Imm32(~(mask << offset)))};
    X(p2r.dest_reg, ir.BitwiseOr(masked_out, insert));
}

} // 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 MOV(TranslatorVisitor& v, u64 insn, const IR::U32& src, bool is_mov32i = false) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<39, 4, u64> mask;
        BitField<12, 4, u64> mov32i_mask;
    } const mov{insn};

    u64 mask = is_mov32i ? mov.mov32i_mask : mov.mask;
    if (mask != 0xf && mask != 0x1) {
        LOG_WARNING(Shader, "(STUBBED) Masked Mov");
        return;
    }
    v.X(mov.dest_reg, src);
}
} // Anonymous namespace

void TranslatorVisitor::MOV_reg(u64 insn) {
    MOV(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::MOV_cbuf(u64 insn) {
    MOV(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::MOV_imm(u64 insn) {
    MOV(*this, insn, GetImm20(insn));
}

void TranslatorVisitor::MOV32I(u64 insn) {
    MOV(*this, insn, GetImm32(insn), 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 "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
enum class Mode : u64 {
    PR,
    CC,
};

void SetFlag(IR::IREmitter& ir, const IR::U1& inv_mask_bit, const IR::U1& src_bit, u32 index) {
    switch (index) {
    case 0:
        return ir.SetZFlag(IR::U1{ir.Select(inv_mask_bit, ir.GetZFlag(), src_bit)});
    case 1:
        return ir.SetSFlag(IR::U1{ir.Select(inv_mask_bit, ir.GetSFlag(), src_bit)});
    case 2:
        return ir.SetCFlag(IR::U1{ir.Select(inv_mask_bit, ir.GetCFlag(), src_bit)});
    case 3:
        return ir.SetOFlag(IR::U1{ir.Select(inv_mask_bit, ir.GetOFlag(), src_bit)});
    default:
        throw LogicError("Unreachable R2P index");
    }
}

void R2P(TranslatorVisitor& v, u64 insn, const IR::U32& mask) {
    union {
        u64 raw;
        BitField<8, 8, IR::Reg> src_reg;
        BitField<40, 1, Mode> mode;
        BitField<41, 2, u64> byte_selector;
    } const r2p{insn};
    const IR::U32 src{v.X(r2p.src_reg)};
    const IR::U32 count{v.ir.Imm32(1)};
    const bool pr_mode{r2p.mode == Mode::PR};
    const u32 num_items{pr_mode ? 7U : 4U};
    const u32 offset_base{static_cast<u32>(r2p.byte_selector) * 8};
    for (u32 index = 0; index < num_items; ++index) {
        const IR::U32 offset{v.ir.Imm32(offset_base + index)};
        const IR::U1 src_zero{v.ir.GetZeroFromOp(v.ir.BitFieldExtract(src, offset, count, false))};
        const IR::U1 src_bit{v.ir.LogicalNot(src_zero)};
        const IR::U32 mask_bfe{v.ir.BitFieldExtract(mask, v.ir.Imm32(index), count, false)};
        const IR::U1 inv_mask_bit{v.ir.GetZeroFromOp(mask_bfe)};
        if (pr_mode) {
            const IR::Pred pred{index};
            v.ir.SetPred(pred, IR::U1{v.ir.Select(inv_mask_bit, v.ir.GetPred(pred), src_bit)});
        } else {
            SetFlag(v.ir, inv_mask_bit, src_bit, index);
        }
    }
}
} // Anonymous namespace

void TranslatorVisitor::R2P_reg(u64 insn) {
    R2P(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::R2P_cbuf(u64 insn) {
    R2P(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::R2P_imm(u64 insn) {
    R2P(*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 {
enum class SpecialRegister : u64 {
    SR_LANEID = 0,
    SR_CLOCK = 1,
    SR_VIRTCFG = 2,
    SR_VIRTID = 3,
    SR_PM0 = 4,
    SR_PM1 = 5,
    SR_PM2 = 6,
    SR_PM3 = 7,
    SR_PM4 = 8,
    SR_PM5 = 9,
    SR_PM6 = 10,
    SR_PM7 = 11,
    SR12 = 12,
    SR13 = 13,
    SR14 = 14,
    SR_ORDERING_TICKET = 15,
    SR_PRIM_TYPE = 16,
    SR_INVOCATION_ID = 17,
    SR_Y_DIRECTION = 18,
    SR_THREAD_KILL = 19,
    SM_SHADER_TYPE = 20,
    SR_DIRECTCBEWRITEADDRESSLOW = 21,
    SR_DIRECTCBEWRITEADDRESSHIGH = 22,
    SR_DIRECTCBEWRITEENABLE = 23,
    SR_MACHINE_ID_0 = 24,
    SR_MACHINE_ID_1 = 25,
    SR_MACHINE_ID_2 = 26,
    SR_MACHINE_ID_3 = 27,
    SR_AFFINITY = 28,
    SR_INVOCATION_INFO = 29,
    SR_WSCALEFACTOR_XY = 30,
    SR_WSCALEFACTOR_Z = 31,
    SR_TID = 32,
    SR_TID_X = 33,
    SR_TID_Y = 34,
    SR_TID_Z = 35,
    SR_CTA_PARAM = 36,
    SR_CTAID_X = 37,
    SR_CTAID_Y = 38,
    SR_CTAID_Z = 39,
    SR_NTID = 40,
    SR_CirQueueIncrMinusOne = 41,
    SR_NLATC = 42,
    SR43 = 43,
    SR_SM_SPA_VERSION = 44,
    SR_MULTIPASSSHADERINFO = 45,
    SR_LWINHI = 46,
    SR_SWINHI = 47,
    SR_SWINLO = 48,
    SR_SWINSZ = 49,
    SR_SMEMSZ = 50,
    SR_SMEMBANKS = 51,
    SR_LWINLO = 52,
    SR_LWINSZ = 53,
    SR_LMEMLOSZ = 54,
    SR_LMEMHIOFF = 55,
    SR_EQMASK = 56,
    SR_LTMASK = 57,
    SR_LEMASK = 58,
    SR_GTMASK = 59,
    SR_GEMASK = 60,
    SR_REGALLOC = 61,
    SR_BARRIERALLOC = 62,
    SR63 = 63,
    SR_GLOBALERRORSTATUS = 64,
    SR65 = 65,
    SR_WARPERRORSTATUS = 66,
    SR_WARPERRORSTATUSCLEAR = 67,
    SR68 = 68,
    SR69 = 69,
    SR70 = 70,
    SR71 = 71,
    SR_PM_HI0 = 72,
    SR_PM_HI1 = 73,
    SR_PM_HI2 = 74,
    SR_PM_HI3 = 75,
    SR_PM_HI4 = 76,
    SR_PM_HI5 = 77,
    SR_PM_HI6 = 78,
    SR_PM_HI7 = 79,
    SR_CLOCKLO = 80,
    SR_CLOCKHI = 81,
    SR_GLOBALTIMERLO = 82,
    SR_GLOBALTIMERHI = 83,
    SR84 = 84,
    SR85 = 85,
    SR86 = 86,
    SR87 = 87,
    SR88 = 88,
    SR89 = 89,
    SR90 = 90,
    SR91 = 91,
    SR92 = 92,
    SR93 = 93,
    SR94 = 94,
    SR95 = 95,
    SR_HWTASKID = 96,
    SR_CIRCULARQUEUEENTRYINDEX = 97,
    SR_CIRCULARQUEUEENTRYADDRESSLOW = 98,
    SR_CIRCULARQUEUEENTRYADDRESSHIGH = 99,
};

[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) {
    switch (special_register) {
    case SpecialRegister::SR_INVOCATION_ID:
        return ir.InvocationId();
    case SpecialRegister::SR_THREAD_KILL:
        return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
    case SpecialRegister::SR_INVOCATION_INFO:
        return ir.InvocationInfo();
    case SpecialRegister::SR_TID: {
        const IR::Value tid{ir.LocalInvocationId()};
        return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)},
                                                   IR::U32{ir.CompositeExtract(tid, 1)},
                                                   ir.Imm32(16), ir.Imm32(8)),
                                 IR::U32{ir.CompositeExtract(tid, 2)}, ir.Imm32(26), ir.Imm32(6));
    }
    case SpecialRegister::SR_TID_X:
        return ir.LocalInvocationIdX();
    case SpecialRegister::SR_TID_Y:
        return ir.LocalInvocationIdY();
    case SpecialRegister::SR_TID_Z:
        return ir.LocalInvocationIdZ();
    case SpecialRegister::SR_CTAID_X:
        return ir.WorkgroupIdX();
    case SpecialRegister::SR_CTAID_Y:
        return ir.WorkgroupIdY();
    case SpecialRegister::SR_CTAID_Z:
        return ir.WorkgroupIdZ();
    case SpecialRegister::SR_WSCALEFACTOR_XY:
        LOG_WARNING(Shader, "(STUBBED) SR_WSCALEFACTOR_XY");
        return ir.SR_WScaleFactorXY();
    case SpecialRegister::SR_WSCALEFACTOR_Z:
        LOG_WARNING(Shader, "(STUBBED) SR_WSCALEFACTOR_Z");
        return ir.SR_WScaleFactorZ();
    case SpecialRegister::SR_LANEID:
        return ir.LaneId();
    case SpecialRegister::SR_EQMASK:
        return ir.SubgroupEqMask();
    case SpecialRegister::SR_LTMASK:
        return ir.SubgroupLtMask();
    case SpecialRegister::SR_LEMASK:
        return ir.SubgroupLeMask();
    case SpecialRegister::SR_GTMASK:
        return ir.SubgroupGtMask();
    case SpecialRegister::SR_GEMASK:
        return ir.SubgroupGeMask();
    case SpecialRegister::SR_Y_DIRECTION:
        return ir.BitCast<IR::U32>(ir.YDirection());
    case SpecialRegister::SR_AFFINITY:
        LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY");
        return ir.Imm32(0); // This is the default value hardware returns.
    default:
        LOG_CRITICAL(Shader, "(STUBBED) Special register {}", special_register);
        return ir.Imm32(0); // This is the default value hardware returns.
    }
}
} // Anonymous namespace

void TranslatorVisitor::S2R(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<20, 8, SpecialRegister> src_reg;
    } const s2r{insn};

    X(s2r.dest_reg, Read(ir, s2r.src_reg));
}

} // namespace Shader::Maxwell


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/opcodes.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {

[[noreturn]] static void ThrowNotImplemented(Opcode opcode) {
    throw NotImplementedException("Instruction {} is not implemented", opcode);
}

void TranslatorVisitor::ATOM_cas(u64) {
    ThrowNotImplemented(Opcode::ATOM_cas);
}

void TranslatorVisitor::ATOMS_cas(u64) {
    ThrowNotImplemented(Opcode::ATOMS_cas);
}

void TranslatorVisitor::B2R(u64) {
    ThrowNotImplemented(Opcode::B2R);
}

void TranslatorVisitor::BPT(u64) {
    ThrowNotImplemented(Opcode::BPT);
}

void TranslatorVisitor::BRA(u64) {
    ThrowNotImplemented(Opcode::BRA);
}

void TranslatorVisitor::BRK(u64) {
    ThrowNotImplemented(Opcode::BRK);
}

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

void TranslatorVisitor::CCTL(u64) {
    ThrowNotImplemented(Opcode::CCTL);
}

void TranslatorVisitor::CCTLL(u64) {
    ThrowNotImplemented(Opcode::CCTLL);
}

void TranslatorVisitor::CONT(u64) {
    ThrowNotImplemented(Opcode::CONT);
}

void TranslatorVisitor::CS2R(u64) {
    ThrowNotImplemented(Opcode::CS2R);
}

void TranslatorVisitor::FCHK_reg(u64) {
    ThrowNotImplemented(Opcode::FCHK_reg);
}

void TranslatorVisitor::FCHK_cbuf(u64) {
    ThrowNotImplemented(Opcode::FCHK_cbuf);
}

void TranslatorVisitor::FCHK_imm(u64) {
    ThrowNotImplemented(Opcode::FCHK_imm);
}

void TranslatorVisitor::GETCRSPTR(u64) {
    ThrowNotImplemented(Opcode::GETCRSPTR);
}

void TranslatorVisitor::GETLMEMBASE(u64) {
    ThrowNotImplemented(Opcode::GETLMEMBASE);
}

void TranslatorVisitor::IDE(u64) {
    ThrowNotImplemented(Opcode::IDE);
}

void TranslatorVisitor::IDP_reg(u64) {
    ThrowNotImplemented(Opcode::IDP_reg);
}

void TranslatorVisitor::IDP_imm(u64) {
    ThrowNotImplemented(Opcode::IDP_imm);
}

void TranslatorVisitor::IMAD_reg(u64) {
    ThrowNotImplemented(Opcode::IMAD_reg);
}

void TranslatorVisitor::IMAD_rc(u64) {
    ThrowNotImplemented(Opcode::IMAD_rc);
}

void TranslatorVisitor::IMAD_cr(u64) {
    ThrowNotImplemented(Opcode::IMAD_cr);
}

void TranslatorVisitor::IMAD_imm(u64) {
    ThrowNotImplemented(Opcode::IMAD_imm);
}

void TranslatorVisitor::IMAD32I(u64) {
    ThrowNotImplemented(Opcode::IMAD32I);
}

void TranslatorVisitor::IMADSP_reg(u64) {
    ThrowNotImplemented(Opcode::IMADSP_reg);
}

void TranslatorVisitor::IMADSP_rc(u64) {
    ThrowNotImplemented(Opcode::IMADSP_rc);
}

void TranslatorVisitor::IMADSP_cr(u64) {
    ThrowNotImplemented(Opcode::IMADSP_cr);
}

void TranslatorVisitor::IMADSP_imm(u64) {
    ThrowNotImplemented(Opcode::IMADSP_imm);
}

void TranslatorVisitor::IMUL_reg(u64) {
    ThrowNotImplemented(Opcode::IMUL_reg);
}

void TranslatorVisitor::IMUL_cbuf(u64) {
    ThrowNotImplemented(Opcode::IMUL_cbuf);
}

void TranslatorVisitor::IMUL_imm(u64) {
    ThrowNotImplemented(Opcode::IMUL_imm);
}

void TranslatorVisitor::IMUL32I(u64) {
    ThrowNotImplemented(Opcode::IMUL32I);
}

void TranslatorVisitor::JCAL(u64) {
    ThrowNotImplemented(Opcode::JCAL);
}

void TranslatorVisitor::JMP(u64) {
    ThrowNotImplemented(Opcode::JMP);
}

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

void TranslatorVisitor::LD(u64) {
    ThrowNotImplemented(Opcode::LD);
}

void TranslatorVisitor::LEPC(u64) {
    ThrowNotImplemented(Opcode::LEPC);
}

void TranslatorVisitor::LONGJMP(u64) {
    ThrowNotImplemented(Opcode::LONGJMP);
}

void TranslatorVisitor::NOP(u64) {
    // NOP is No-Op.
}

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

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

void TranslatorVisitor::PEXIT(u64) {
    ThrowNotImplemented(Opcode::PEXIT);
}

void TranslatorVisitor::PLONGJMP(u64) {
    ThrowNotImplemented(Opcode::PLONGJMP);
}

void TranslatorVisitor::PRET(u64) {
    ThrowNotImplemented(Opcode::PRET);
}

void TranslatorVisitor::PRMT_reg(u64) {
    ThrowNotImplemented(Opcode::PRMT_reg);
}

void TranslatorVisitor::PRMT_rc(u64) {
    ThrowNotImplemented(Opcode::PRMT_rc);
}

void TranslatorVisitor::PRMT_cr(u64) {
    ThrowNotImplemented(Opcode::PRMT_cr);
}

void TranslatorVisitor::PRMT_imm(u64) {
    ThrowNotImplemented(Opcode::PRMT_imm);
}

void TranslatorVisitor::R2B(u64) {
    ThrowNotImplemented(Opcode::R2B);
}

void TranslatorVisitor::RAM(u64) {
    LOG_WARNING(Shader, "(STUBBED) RAM Instruction");
}

void TranslatorVisitor::RET(u64) {
    ThrowNotImplemented(Opcode::RET);
}

void TranslatorVisitor::RTT(u64) {
    ThrowNotImplemented(Opcode::RTT);
}

void TranslatorVisitor::SAM(u64) {
    LOG_WARNING(Shader, "(STUBBED) SAM Instruction");
}

void TranslatorVisitor::SETCRSPTR(u64) {
    ThrowNotImplemented(Opcode::SETCRSPTR);
}

void TranslatorVisitor::SETLMEMBASE(u64) {
    ThrowNotImplemented(Opcode::SETLMEMBASE);
}

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

void TranslatorVisitor::ST(u64) {
    ThrowNotImplemented(Opcode::ST);
}

void TranslatorVisitor::STP(u64) {
    ThrowNotImplemented(Opcode::STP);
}

void TranslatorVisitor::SUATOM_cas(u64) {
    ThrowNotImplemented(Opcode::SUATOM_cas);
}

void TranslatorVisitor::SYNC(u64) {
    ThrowNotImplemented(Opcode::SYNC);
}

void TranslatorVisitor::TXA(u64) {
    ThrowNotImplemented(Opcode::TXA);
}

void TranslatorVisitor::VABSDIFF(u64) {
    ThrowNotImplemented(Opcode::VABSDIFF);
}

void TranslatorVisitor::VABSDIFF4(u64) {
    ThrowNotImplemented(Opcode::VABSDIFF4);
}

void TranslatorVisitor::VADD(u64) {
    ThrowNotImplemented(Opcode::VADD);
}

void TranslatorVisitor::VSET(u64) {
    ThrowNotImplemented(Opcode::VSET);
}
void TranslatorVisitor::VSHL(u64) {
    ThrowNotImplemented(Opcode::VSHL);
}

void TranslatorVisitor::VSHR(u64) {
    ThrowNotImplemented(Opcode::VSHR);
}

} // 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 OUT(TranslatorVisitor& v, u64 insn, IR::U32 stream_index) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> output_reg; // Not needed on host
        BitField<39, 1, u64> emit;
        BitField<40, 1, u64> cut;
    } const out{insn};

    stream_index = v.ir.BitwiseAnd(stream_index, v.ir.Imm32(0b11));

    if (out.emit != 0) {
        v.ir.EmitVertex(stream_index);
    }
    if (out.cut != 0) {
        v.ir.EndPrimitive(stream_index);
    }
    // Host doesn't need the output register, but we can write to it to avoid undefined reads
    v.X(out.dest_reg, v.ir.Imm32(0));
}
} // Anonymous namespace

void TranslatorVisitor::OUT_reg(u64 insn) {
    OUT(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::OUT_cbuf(u64 insn) {
    OUT(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::OUT_imm(u64 insn) {
    OUT(*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 {
enum class Mode : u64 {
    Default,
    CovMask,
    Covered,
    Offset,
    CentroidOffset,
    MyIndex,
};
} // Anonymous namespace

void TranslatorVisitor::PIXLD(u64 insn) {
    union {
        u64 raw;
        BitField<31, 3, Mode> mode;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> addr_reg;
        BitField<20, 8, s64> addr_offset;
        BitField<45, 3, IR::Pred> dest_pred;
    } const pixld{insn};

    if (pixld.dest_pred != IR::Pred::PT) {
        throw NotImplementedException("Destination predicate");
    }
    if (pixld.addr_reg != IR::Reg::RZ || pixld.addr_offset != 0) {
        throw NotImplementedException("Non-zero source register");
    }
    switch (pixld.mode) {
    case Mode::MyIndex:
        X(pixld.dest_reg, ir.SampleId());
        break;
    default:
        throw NotImplementedException("Mode {}", pixld.mode.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/maxwell/translate/impl/common_funcs.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
void TranslatorVisitor::PSETP(u64 insn) {
    union {
        u64 raw;
        BitField<0, 3, IR::Pred> dest_pred_b;
        BitField<3, 3, IR::Pred> dest_pred_a;
        BitField<12, 3, IR::Pred> pred_a;
        BitField<15, 1, u64> neg_pred_a;
        BitField<24, 2, BooleanOp> bop_1;
        BitField<29, 3, IR::Pred> pred_b;
        BitField<32, 1, u64> neg_pred_b;
        BitField<39, 3, IR::Pred> pred_c;
        BitField<42, 1, u64> neg_pred_c;
        BitField<45, 2, BooleanOp> bop_2;
    } const pset{insn};

    const IR::U1 pred_a{ir.GetPred(pset.pred_a, pset.neg_pred_a != 0)};
    const IR::U1 pred_b{ir.GetPred(pset.pred_b, pset.neg_pred_b != 0)};
    const IR::U1 pred_c{ir.GetPred(pset.pred_c, pset.neg_pred_c != 0)};

    const IR::U1 lhs_a{PredicateCombine(ir, pred_a, pred_b, pset.bop_1)};
    const IR::U1 lhs_b{PredicateCombine(ir, ir.LogicalNot(pred_a), pred_b, pset.bop_1)};
    const IR::U1 result_a{PredicateCombine(ir, lhs_a, pred_c, pset.bop_2)};
    const IR::U1 result_b{PredicateCombine(ir, lhs_b, pred_c, pset.bop_2)};

    ir.SetPred(pset.dest_pred_a, result_a);
    ir.SetPred(pset.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/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::PSET(u64 insn) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<12, 3, IR::Pred> pred_a;
        BitField<15, 1, u64> neg_pred_a;
        BitField<24, 2, BooleanOp> bop_1;
        BitField<29, 3, IR::Pred> pred_b;
        BitField<32, 1, u64> neg_pred_b;
        BitField<39, 3, IR::Pred> pred_c;
        BitField<42, 1, u64> neg_pred_c;
        BitField<44, 1, u64> bf;
        BitField<45, 2, BooleanOp> bop_2;
        BitField<47, 1, u64> cc;
    } const pset{insn};

    const IR::U1 pred_a{ir.GetPred(pset.pred_a, pset.neg_pred_a != 0)};
    const IR::U1 pred_b{ir.GetPred(pset.pred_b, pset.neg_pred_b != 0)};
    const IR::U1 pred_c{ir.GetPred(pset.pred_c, pset.neg_pred_c != 0)};

    const IR::U1 res_1{PredicateCombine(ir, pred_a, pred_b, pset.bop_1)};
    const IR::U1 res_2{PredicateCombine(ir, res_1, pred_c, pset.bop_2)};

    const IR::U32 true_result{pset.bf != 0 ? ir.Imm32(0x3f800000) : ir.Imm32(-1)};
    const IR::U32 zero{ir.Imm32(0)};

    const IR::U32 result{ir.Select(res_2, true_result, zero)};

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

} // 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 SEL(TranslatorVisitor& v, u64 insn, const IR::U32& src) {
    union {
        u64 raw;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> src_reg;
        BitField<39, 3, IR::Pred> pred;
        BitField<42, 1, u64> neg_pred;
    } const sel{insn};

    const IR::U1 pred = v.ir.GetPred(sel.pred);
    IR::U32 op_a{v.X(sel.src_reg)};
    IR::U32 op_b{src};
    if (sel.neg_pred != 0) {
        std::swap(op_a, op_b);
    }
    const IR::U32 result{v.ir.Select(pred, op_a, op_b)};

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

void TranslatorVisitor::SEL_reg(u64 insn) {
    SEL(*this, insn, GetReg20(insn));
}

void TranslatorVisitor::SEL_cbuf(u64 insn) {
    SEL(*this, insn, GetCbuf(insn));
}

void TranslatorVisitor::SEL_imm(u64 insn) {
    SEL(*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/ir/modifiers.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
enum class Type : u64 {
    _1D,
    BUFFER_1D,
    ARRAY_1D,
    _2D,
    ARRAY_2D,
    _3D,
};

enum class Size : u64 {
    U32,
    S32,
    U64,
    S64,
    F32FTZRN,
    F16x2FTZRN,
    SD32,
    SD64,
};

enum class AtomicOp : u64 {
    ADD,
    MIN,
    MAX,
    INC,
    DEC,
    AND,
    OR,
    XOR,
    EXCH,
};

enum class Clamp : u64 {
    IGN,
    Default,
    TRAP,
};

TextureType GetType(Type type) {
    switch (type) {
    case Type::_1D:
        return TextureType::Color1D;
    case Type::BUFFER_1D:
        return TextureType::Buffer;
    case Type::ARRAY_1D:
        return TextureType::ColorArray1D;
    case Type::_2D:
        return TextureType::Color2D;
    case Type::ARRAY_2D:
        return TextureType::ColorArray2D;
    case Type::_3D:
        return TextureType::Color3D;
    }
    throw NotImplementedException("Invalid type {}", type);
}

IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) {
    switch (type) {
    case Type::_1D:
    case Type::BUFFER_1D:
        return v.X(reg);
    case Type::_2D:
        return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
    case Type::_3D:
        return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
    default:
        break;
    }
    throw NotImplementedException("Invalid type {}", type);
}

IR::Value ApplyAtomicOp(IR::IREmitter& ir, const IR::U32& handle, const IR::Value& coords,
                        const IR::Value& op_b, IR::TextureInstInfo info, AtomicOp op,
                        bool is_signed) {
    switch (op) {
    case AtomicOp::ADD:
        return ir.ImageAtomicIAdd(handle, coords, op_b, info);
    case AtomicOp::MIN:
        return ir.ImageAtomicIMin(handle, coords, op_b, is_signed, info);
    case AtomicOp::MAX:
        return ir.ImageAtomicIMax(handle, coords, op_b, is_signed, info);
    case AtomicOp::INC:
        return ir.ImageAtomicInc(handle, coords, op_b, info);
    case AtomicOp::DEC:
        return ir.ImageAtomicDec(handle, coords, op_b, info);
    case AtomicOp::AND:
        return ir.ImageAtomicAnd(handle, coords, op_b, info);
    case AtomicOp::OR:
        return ir.ImageAtomicOr(handle, coords, op_b, info);
    case AtomicOp::XOR:
        return ir.ImageAtomicXor(handle, coords, op_b, info);
    case AtomicOp::EXCH:
        return ir.ImageAtomicExchange(handle, coords, op_b, info);
    default:
        throw NotImplementedException("Atomic Operation {}", op);
    }
}

ImageFormat Format(Size size) {
    switch (size) {
    case Size::U32:
    case Size::S32:
    case Size::SD32:
        return ImageFormat::R32_UINT;
    default:
        break;
    }
    throw NotImplementedException("Invalid size {}", size);
}

bool IsSizeInt32(Size size) {
    switch (size) {
    case Size::U32:
    case Size::S32:
    case Size::SD32:
        return true;
    default:
        return false;
    }
}

void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg,
                 IR::Reg bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type,
                 u64 bound_offset, bool is_bindless, bool write_result) {
    if (clamp != Clamp::IGN) {
        throw NotImplementedException("Clamp {}", clamp);
    }
    if (!IsSizeInt32(size)) {
        throw NotImplementedException("Size {}", size);
    }
    const bool is_signed{size == Size::S32};
    const ImageFormat format{Format(size)};
    const TextureType tex_type{GetType(type)};
    const IR::Value coords{MakeCoords(v, coord_reg, type)};

    const IR::U32 handle{is_bindless != 0 ? v.X(bindless_reg)
                                          : v.ir.Imm32(static_cast<u32>(bound_offset * 4))};
    IR::TextureInstInfo info{};
    info.type.Assign(tex_type);
    info.image_format.Assign(format);

    // TODO: float/64-bit operand
    const IR::Value op_b{v.X(operand_reg)};
    const IR::Value color{ApplyAtomicOp(v.ir, handle, coords, op_b, info, op, is_signed)};

    if (write_result) {
        v.X(dest_reg, IR::U32{color});
    }
}
} // Anonymous namespace

void TranslatorVisitor::SUATOM(u64 insn) {
    union {
        u64 raw;
        BitField<54, 1, u64> is_bindless;
        BitField<29, 4, AtomicOp> op;
        BitField<33, 3, Type> type;
        BitField<51, 3, Size> size;
        BitField<49, 2, Clamp> clamp;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<20, 8, IR::Reg> operand_reg;
        BitField<36, 13, u64> bound_offset;    // !is_bindless
        BitField<39, 8, IR::Reg> bindless_reg; // is_bindless
    } const suatom{insn};

    ImageAtomOp(*this, suatom.dest_reg, suatom.operand_reg, suatom.coord_reg, suatom.bindless_reg,
                suatom.op, suatom.clamp, suatom.size, suatom.type, suatom.bound_offset,
                suatom.is_bindless != 0, true);
}

void TranslatorVisitor::SURED(u64 insn) {
    // TODO: confirm offsets
    union {
        u64 raw;
        BitField<51, 1, u64> is_bound;
        BitField<21, 3, AtomicOp> op;
        BitField<33, 3, Type> type;
        BitField<20, 3, Size> size;
        BitField<49, 2, Clamp> clamp;
        BitField<0, 8, IR::Reg> operand_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<36, 13, u64> bound_offset;    // is_bound
        BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
    } const sured{insn};
    ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, sured.bindless_reg,
                sured.op, sured.clamp, sured.size, sured.type, sured.bound_offset,
                sured.is_bound == 0, false);
}

} // namespace Shader::Maxwell


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

#include <array>
#include <bit>

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

namespace Shader::Maxwell {
namespace {
enum class Type : u64 {
    _1D,
    BUFFER_1D,
    ARRAY_1D,
    _2D,
    ARRAY_2D,
    _3D,
};

constexpr unsigned R = 1 << 0;
constexpr unsigned G = 1 << 1;
constexpr unsigned B = 1 << 2;
constexpr unsigned A = 1 << 3;

constexpr std::array MASK{
    0U,            //
    R,             //
    G,             //
    R | G,         //
    B,             //
    R | B,         //
    G | B,         //
    R | G | B,     //
    A,             //
    R | A,         //
    G | A,         //
    R | G | A,     //
    B | A,         //
    R | B | A,     //
    G | B | A,     //
    R | G | B | A, //
};

enum class Size : u64 {
    U8,
    S8,
    U16,
    S16,
    B32,
    B64,
    B128,
};

enum class Clamp : u64 {
    IGN,
    Default,
    TRAP,
};

// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#cache-operators
enum class LoadCache : u64 {
    CA, // Cache at all levels, likely to be accessed again
    CG, // Cache at global level (L2 and below, not L1)
    CI, // ???
    CV, // Don't cache and fetch again (volatile)
};

enum class StoreCache : u64 {
    WB, // Cache write-back all coherent levels
    CG, // Cache at global level (L2 and below, not L1)
    CS, // Cache streaming, likely to be accessed once
    WT, // Cache write-through (to system memory, volatile?)
};

ImageFormat Format(Size size) {
    switch (size) {
    case Size::U8:
        return ImageFormat::R8_UINT;
    case Size::S8:
        return ImageFormat::R8_SINT;
    case Size::U16:
        return ImageFormat::R16_UINT;
    case Size::S16:
        return ImageFormat::R16_SINT;
    case Size::B32:
        return ImageFormat::R32_UINT;
    case Size::B64:
        return ImageFormat::R32G32_UINT;
    case Size::B128:
        return ImageFormat::R32G32B32A32_UINT;
    }
    throw NotImplementedException("Invalid size {}", size);
}

int SizeInRegs(Size size) {
    switch (size) {
    case Size::U8:
    case Size::S8:
    case Size::U16:
    case Size::S16:
    case Size::B32:
        return 1;
    case Size::B64:
        return 2;
    case Size::B128:
        return 4;
    }
    throw NotImplementedException("Invalid size {}", size);
}

TextureType GetType(Type type) {
    switch (type) {
    case Type::_1D:
        return TextureType::Color1D;
    case Type::BUFFER_1D:
        return TextureType::Buffer;
    case Type::ARRAY_1D:
        return TextureType::ColorArray1D;
    case Type::_2D:
        return TextureType::Color2D;
    case Type::ARRAY_2D:
        return TextureType::ColorArray2D;
    case Type::_3D:
        return TextureType::Color3D;
    }
    throw NotImplementedException("Invalid type {}", type);
}

IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) {
    const auto array{[&](int index) {
        return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16));
    }};
    switch (type) {
    case Type::_1D:
    case Type::BUFFER_1D:
        return v.X(reg);
    case Type::ARRAY_1D:
        return v.ir.CompositeConstruct(v.X(reg), array(1));
    case Type::_2D:
        return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
    case Type::ARRAY_2D:
        return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2));
    case Type::_3D:
        return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
    }
    throw NotImplementedException("Invalid type {}", type);
}

unsigned SwizzleMask(u64 swizzle) {
    if (swizzle == 0 || swizzle >= MASK.size()) {
        throw NotImplementedException("Invalid swizzle {}", swizzle);
    }
    return MASK[swizzle];
}

IR::Value MakeColor(IR::IREmitter& ir, IR::Reg reg, int num_regs) {
    std::array<IR::U32, 4> colors;
    for (int i = 0; i < num_regs; ++i) {
        colors[static_cast<size_t>(i)] = ir.GetReg(reg + i);
    }
    for (int i = num_regs; i < 4; ++i) {
        colors[static_cast<size_t>(i)] = ir.Imm32(0);
    }
    return ir.CompositeConstruct(colors[0], colors[1], colors[2], colors[3]);
}
} // Anonymous namespace

void TranslatorVisitor::SULD(u64 insn) {
    union {
        u64 raw;
        BitField<51, 1, u64> is_bound;
        BitField<52, 1, u64> d;
        BitField<23, 1, u64> ba;
        BitField<33, 3, Type> type;
        BitField<24, 2, LoadCache> cache;
        BitField<20, 3, Size> size;   // .D
        BitField<20, 4, u64> swizzle; // .P
        BitField<49, 2, Clamp> clamp;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<36, 13, u64> bound_offset;    // is_bound
        BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
    } const suld{insn};

    if (suld.clamp != Clamp::IGN) {
        throw NotImplementedException("Clamp {}", suld.clamp.Value());
    }
    if (suld.cache != LoadCache::CA && suld.cache != LoadCache::CG) {
        throw NotImplementedException("Cache {}", suld.cache.Value());
    }
    const bool is_typed{suld.d != 0};
    if (is_typed && suld.ba != 0) {
        throw NotImplementedException("BA");
    }

    const ImageFormat format{is_typed ? Format(suld.size) : ImageFormat::Typeless};
    const TextureType type{GetType(suld.type)};
    const IR::Value coords{MakeCoords(*this, suld.coord_reg, suld.type)};
    const IR::U32 handle{suld.is_bound != 0 ? ir.Imm32(static_cast<u32>(suld.bound_offset * 4))
                                            : X(suld.bindless_reg)};
    IR::TextureInstInfo info{};
    info.type.Assign(type);
    info.image_format.Assign(format);

    const IR::Value result{ir.ImageRead(handle, coords, info)};
    IR::Reg dest_reg{suld.dest_reg};
    if (is_typed) {
        const int num_regs{SizeInRegs(suld.size)};
        for (int i = 0; i < num_regs; ++i) {
            X(dest_reg + i, IR::U32{ir.CompositeExtract(result, static_cast<size_t>(i))});
        }
    } else {
        const unsigned mask{SwizzleMask(suld.swizzle)};
        const int bits{std::popcount(mask)};
        if (!IR::IsAligned(dest_reg, bits == 3 ? 4 : static_cast<size_t>(bits))) {
            throw NotImplementedException("Unaligned destination register");
        }
        for (unsigned component = 0; component < 4; ++component) {
            if (((mask >> component) & 1) == 0) {
                continue;
            }
            X(dest_reg, IR::U32{ir.CompositeExtract(result, component)});
            ++dest_reg;
        }
    }
}

void TranslatorVisitor::SUST(u64 insn) {
    union {
        u64 raw;
        BitField<51, 1, u64> is_bound;
        BitField<52, 1, u64> d;
        BitField<23, 1, u64> ba;
        BitField<33, 3, Type> type;
        BitField<24, 2, StoreCache> cache;
        BitField<20, 3, Size> size;   // .D
        BitField<20, 4, u64> swizzle; // .P
        BitField<49, 2, Clamp> clamp;
        BitField<0, 8, IR::Reg> data_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<36, 13, u64> bound_offset;    // is_bound
        BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
    } const sust{insn};

    if (sust.clamp != Clamp::IGN) {
        throw NotImplementedException("Clamp {}", sust.clamp.Value());
    }
    if (sust.cache != StoreCache::WB && sust.cache != StoreCache::CG) {
        throw NotImplementedException("Cache {}", sust.cache.Value());
    }
    const bool is_typed{sust.d != 0};
    if (is_typed && sust.ba != 0) {
        throw NotImplementedException("BA");
    }
    const ImageFormat format{is_typed ? Format(sust.size) : ImageFormat::Typeless};
    const TextureType type{GetType(sust.type)};
    const IR::Value coords{MakeCoords(*this, sust.coord_reg, sust.type)};
    const IR::U32 handle{sust.is_bound != 0 ? ir.Imm32(static_cast<u32>(sust.bound_offset * 4))
                                            : X(sust.bindless_reg)};
    IR::TextureInstInfo info{};
    info.type.Assign(type);
    info.image_format.Assign(format);

    IR::Value color;
    if (is_typed) {
        color = MakeColor(ir, sust.data_reg, SizeInRegs(sust.size));
    } else {
        const unsigned mask{SwizzleMask(sust.swizzle)};
        if (mask != 0xf) {
            throw NotImplementedException("Non-full mask");
        }
        color = MakeColor(ir, sust.data_reg, 4);
    }
    ir.ImageWrite(handle, coords, color, info);
}

} // namespace Shader::Maxwell


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

#include <optional>

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

namespace Shader::Maxwell {
namespace {
enum class Blod : u64 {
    None,
    LZ,
    LB,
    LL,
    INVALIDBLOD4,
    INVALIDBLOD5,
    LBA,
    LLA,
};

enum class TextureType : u64 {
    _1D,
    ARRAY_1D,
    _2D,
    ARRAY_2D,
    _3D,
    ARRAY_3D,
    CUBE,
    ARRAY_CUBE,
};

Shader::TextureType GetType(TextureType type) {
    switch (type) {
    case TextureType::_1D:
        return Shader::TextureType::Color1D;
    case TextureType::ARRAY_1D:
        return Shader::TextureType::ColorArray1D;
    case TextureType::_2D:
        return Shader::TextureType::Color2D;
    case TextureType::ARRAY_2D:
        return Shader::TextureType::ColorArray2D;
    case TextureType::_3D:
        return Shader::TextureType::Color3D;
    case TextureType::ARRAY_3D:
        throw NotImplementedException("3D array texture type");
    case TextureType::CUBE:
        return Shader::TextureType::ColorCube;
    case TextureType::ARRAY_CUBE:
        return Shader::TextureType::ColorArrayCube;
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) {
    const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }};
    switch (type) {
    case TextureType::_1D:
        return v.F(reg);
    case TextureType::ARRAY_1D:
        return v.ir.CompositeConstruct(v.F(reg + 1), read_array());
    case TextureType::_2D:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
    case TextureType::ARRAY_2D:
        return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array());
    case TextureType::_3D:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
    case TextureType::ARRAY_3D:
        throw NotImplementedException("3D array texture type");
    case TextureType::CUBE:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
    case TextureType::ARRAY_CUBE:
        return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array());
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

IR::F32 MakeLod(TranslatorVisitor& v, IR::Reg& reg, Blod blod) {
    switch (blod) {
    case Blod::None:
        return v.ir.Imm32(0.0f);
    case Blod::LZ:
        return v.ir.Imm32(0.0f);
    case Blod::LB:
    case Blod::LL:
    case Blod::LBA:
    case Blod::LLA:
        return v.F(reg++);
    case Blod::INVALIDBLOD4:
    case Blod::INVALIDBLOD5:
        break;
    }
    throw NotImplementedException("Invalid blod {}", blod);
}

IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) {
    const IR::U32 value{v.X(reg++)};
    switch (type) {
    case TextureType::_1D:
    case TextureType::ARRAY_1D:
        return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true);
    case TextureType::_2D:
    case TextureType::ARRAY_2D:
        return v.ir.CompositeConstruct(
            v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true));
    case TextureType::_3D:
    case TextureType::ARRAY_3D:
        return v.ir.CompositeConstruct(
            v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4), true));
    case TextureType::CUBE:
    case TextureType::ARRAY_CUBE:
        throw NotImplementedException("Illegal offset on CUBE sample");
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

bool HasExplicitLod(Blod blod) {
    switch (blod) {
    case Blod::LL:
    case Blod::LLA:
    case Blod::LZ:
        return true;
    default:
        return false;
    }
}

void Impl(TranslatorVisitor& v, u64 insn, bool aoffi, Blod blod, bool lc,
          std::optional<u32> cbuf_offset) {
    union {
        u64 raw;
        BitField<35, 1, u64> ndv;
        BitField<49, 1, u64> nodep;
        BitField<50, 1, u64> dc;
        BitField<51, 3, IR::Pred> sparse_pred;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<20, 8, IR::Reg> meta_reg;
        BitField<28, 3, TextureType> type;
        BitField<31, 4, u64> mask;
    } const tex{insn};

    if (lc) {
        throw NotImplementedException("LC");
    }
    const IR::Value coords{MakeCoords(v, tex.coord_reg, tex.type)};

    IR::Reg meta_reg{tex.meta_reg};
    IR::Value handle;
    IR::Value offset;
    IR::F32 dref;
    IR::F32 lod_clamp;
    if (cbuf_offset) {
        handle = v.ir.Imm32(*cbuf_offset);
    } else {
        handle = v.X(meta_reg++);
    }
    const IR::F32 lod{MakeLod(v, meta_reg, blod)};
    if (aoffi) {
        offset = MakeOffset(v, meta_reg, tex.type);
    }
    if (tex.dc != 0) {
        dref = v.F(meta_reg++);
    }
    IR::TextureInstInfo info{};
    info.type.Assign(GetType(tex.type));
    info.is_depth.Assign(tex.dc != 0 ? 1 : 0);
    info.has_bias.Assign(blod == Blod::LB || blod == Blod::LBA ? 1 : 0);
    info.has_lod_clamp.Assign(lc ? 1 : 0);
    info.ndv_is_active.Assign(tex.ndv != 0 ? 1 : 0);

    const IR::Value sample{[&]() -> IR::Value {
        if (tex.dc == 0) {
            if (HasExplicitLod(blod)) {
                return v.ir.ImageSampleExplicitLod(handle, coords, lod, offset, info);
            } else {
                return v.ir.ImageSampleImplicitLod(handle, coords, lod, offset, lod_clamp, info);
            }
        }
        if (HasExplicitLod(blod)) {
            return v.ir.ImageSampleDrefExplicitLod(handle, coords, dref, lod, offset, info);
        } else {
            return v.ir.ImageSampleDrefImplicitLod(handle, coords, dref, lod, offset, lod_clamp,
                                                   info);
        }
    }()};

    IR::Reg dest_reg{tex.dest_reg};
    for (int element = 0; element < 4; ++element) {
        if (((tex.mask >> element) & 1) == 0) {
            continue;
        }
        IR::F32 value;
        if (tex.dc != 0) {
            value = element < 3 ? IR::F32{sample} : v.ir.Imm32(1.0f);
        } else {
            value = IR::F32{v.ir.CompositeExtract(sample, static_cast<size_t>(element))};
        }
        v.F(dest_reg, value);
        ++dest_reg;
    }
    if (tex.sparse_pred != IR::Pred::PT) {
        v.ir.SetPred(tex.sparse_pred, v.ir.LogicalNot(v.ir.GetSparseFromOp(sample)));
    }
}
} // Anonymous namespace

void TranslatorVisitor::TEX(u64 insn) {
    union {
        u64 raw;
        BitField<54, 1, u64> aoffi;
        BitField<55, 3, Blod> blod;
        BitField<58, 1, u64> lc;
        BitField<36, 13, u64> cbuf_offset;
    } const tex{insn};

    Impl(*this, insn, tex.aoffi != 0, tex.blod, tex.lc != 0, static_cast<u32>(tex.cbuf_offset * 4));
}

void TranslatorVisitor::TEX_b(u64 insn) {
    union {
        u64 raw;
        BitField<36, 1, u64> aoffi;
        BitField<37, 3, Blod> blod;
        BitField<40, 1, u64> lc;
    } const tex{insn};

    Impl(*this, insn, tex.aoffi != 0, tex.blod, tex.lc != 0, std::nullopt);
}

} // 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/modifiers.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
enum class Precision : u64 {
    F16,
    F32,
};

union Encoding {
    u64 raw;
    BitField<59, 1, Precision> precision;
    BitField<53, 4, u64> encoding;
    BitField<49, 1, u64> nodep;
    BitField<28, 8, IR::Reg> dest_reg_b;
    BitField<0, 8, IR::Reg> dest_reg_a;
    BitField<8, 8, IR::Reg> src_reg_a;
    BitField<20, 8, IR::Reg> src_reg_b;
    BitField<36, 13, u64> cbuf_offset;
    BitField<50, 3, u64> swizzle;
};

constexpr unsigned R = 1;
constexpr unsigned G = 2;
constexpr unsigned B = 4;
constexpr unsigned A = 8;

constexpr std::array RG_LUT{
    R,     //
    G,     //
    B,     //
    A,     //
    R | G, //
    R | A, //
    G | A, //
    B | A, //
};

constexpr std::array RGBA_LUT{
    R | G | B,     //
    R | G | A,     //
    R | B | A,     //
    G | B | A,     //
    R | G | B | A, //
};

void CheckAlignment(IR::Reg reg, size_t alignment) {
    if (!IR::IsAligned(reg, alignment)) {
        throw NotImplementedException("Unaligned source register {}", reg);
    }
}

template <typename... Args>
IR::Value Composite(TranslatorVisitor& v, Args... regs) {
    return v.ir.CompositeConstruct(v.F(regs)...);
}

IR::F32 ReadArray(TranslatorVisitor& v, const IR::U32& value) {
    return v.ir.ConvertUToF(32, 16, v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(16)));
}

IR::Value Sample(TranslatorVisitor& v, u64 insn) {
    const Encoding texs{insn};
    const IR::U32 handle{v.ir.Imm32(static_cast<u32>(texs.cbuf_offset * 4))};
    const IR::F32 zero{v.ir.Imm32(0.0f)};
    const IR::Reg reg_a{texs.src_reg_a};
    const IR::Reg reg_b{texs.src_reg_b};
    IR::TextureInstInfo info{};
    if (texs.precision == Precision::F16) {
        info.relaxed_precision.Assign(1);
    }
    switch (texs.encoding) {
    case 0: // 1D.LZ
        info.type.Assign(TextureType::Color1D);
        return v.ir.ImageSampleExplicitLod(handle, v.F(reg_a), zero, {}, info);
    case 1: // 2D
        info.type.Assign(TextureType::Color2D);
        return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_b), {}, {}, {}, info);
    case 2: // 2D.LZ
        info.type.Assign(TextureType::Color2D);
        return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
    case 3: // 2D.LL
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::Color2D);
        return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
                                           info);
    case 4: // 2D.DC
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::Color2D);
        info.is_depth.Assign(1);
        return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
                                               {}, {}, {}, info);
    case 5: // 2D.LL.DC
        CheckAlignment(reg_a, 2);
        CheckAlignment(reg_b, 2);
        info.type.Assign(TextureType::Color2D);
        info.is_depth.Assign(1);
        return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
                                               v.F(reg_b + 1), v.F(reg_b), {}, info);
    case 6: // 2D.LZ.DC
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::Color2D);
        info.is_depth.Assign(1);
        return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
                                               zero, {}, info);
    case 7: // ARRAY_2D
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::ColorArray2D);
        return v.ir.ImageSampleImplicitLod(
            handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
            {}, {}, {}, info);
    case 8: // ARRAY_2D.LZ
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::ColorArray2D);
        return v.ir.ImageSampleExplicitLod(
            handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
            zero, {}, info);
    case 9: // ARRAY_2D.LZ.DC
        CheckAlignment(reg_a, 2);
        CheckAlignment(reg_b, 2);
        info.type.Assign(TextureType::ColorArray2D);
        info.is_depth.Assign(1);
        return v.ir.ImageSampleDrefExplicitLod(
            handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
            v.F(reg_b + 1), zero, {}, info);
    case 10: // 3D
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::Color3D);
        return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
                                           {}, info);
    case 11: // 3D.LZ
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::Color3D);
        return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
                                           info);
    case 12: // CUBE
        CheckAlignment(reg_a, 2);
        info.type.Assign(TextureType::ColorCube);
        return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
                                           {}, info);
    case 13: // CUBE.LL
        CheckAlignment(reg_a, 2);
        CheckAlignment(reg_b, 2);
        info.type.Assign(TextureType::ColorCube);
        return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
                                           v.F(reg_b + 1), {}, info);
    default:
        throw NotImplementedException("Illegal encoding {}", texs.encoding.Value());
    }
}

unsigned Swizzle(u64 insn) {
    const Encoding texs{insn};
    const size_t encoding{texs.swizzle};
    if (texs.dest_reg_b == IR::Reg::RZ) {
        if (encoding >= RG_LUT.size()) {
            throw NotImplementedException("Illegal RG encoding {}", encoding);
        }
        return RG_LUT[encoding];
    } else {
        if (encoding >= RGBA_LUT.size()) {
            throw NotImplementedException("Illegal RGBA encoding {}", encoding);
        }
        return RGBA_LUT[encoding];
    }
}

IR::F32 Extract(TranslatorVisitor& v, const IR::Value& sample, unsigned component) {
    const bool is_shadow{sample.Type() == IR::Type::F32};
    if (is_shadow) {
        const bool is_alpha{component == 3};
        return is_alpha ? v.ir.Imm32(1.0f) : IR::F32{sample};
    } else {
        return IR::F32{v.ir.CompositeExtract(sample, component)};
    }
}

IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
    const Encoding texs{insn};
    switch (index) {
    case 0:
        return texs.dest_reg_a;
    case 1:
        CheckAlignment(texs.dest_reg_a, 2);
        return texs.dest_reg_a + 1;
    case 2:
        return texs.dest_reg_b;
    case 3:
        CheckAlignment(texs.dest_reg_b, 2);
        return texs.dest_reg_b + 1;
    }
    throw LogicError("Invalid store index {}", index);
}

void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
    const unsigned swizzle{Swizzle(insn)};
    unsigned store_index{0};
    for (unsigned component = 0; component < 4; ++component) {
        if (((swizzle >> component) & 1) == 0) {
            continue;
        }
        const IR::Reg dest{RegStoreComponent32(insn, store_index)};
        v.F(dest, Extract(v, sample, component));
        ++store_index;
    }
}

IR::U32 Pack(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) {
    return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs));
}

void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
    const unsigned swizzle{Swizzle(insn)};
    unsigned store_index{0};
    std::array<IR::F32, 4> swizzled;
    for (unsigned component = 0; component < 4; ++component) {
        if (((swizzle >> component) & 1) == 0) {
            continue;
        }
        swizzled[store_index] = Extract(v, sample, component);
        ++store_index;
    }
    const IR::F32 zero{v.ir.Imm32(0.0f)};
    const Encoding texs{insn};
    switch (store_index) {
    case 1:
        v.X(texs.dest_reg_a, Pack(v, swizzled[0], zero));
        break;
    case 2:
    case 3:
    case 4:
        v.X(texs.dest_reg_a, Pack(v, swizzled[0], swizzled[1]));
        switch (store_index) {
        case 2:
            break;
        case 3:
            v.X(texs.dest_reg_b, Pack(v, swizzled[2], zero));
            break;
        case 4:
            v.X(texs.dest_reg_b, Pack(v, swizzled[2], swizzled[3]));
            break;
        }
        break;
    }
}
} // Anonymous namespace

void TranslatorVisitor::TEXS(u64 insn) {
    const IR::Value sample{Sample(*this, insn)};
    if (Encoding{insn}.precision == Precision::F32) {
        Store32(*this, insn, sample);
    } else {
        Store16(*this, insn, sample);
    }
}

} // 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/modifiers.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {

enum class TextureType : u64 {
    _1D,
    ARRAY_1D,
    _2D,
    ARRAY_2D,
    _3D,
    ARRAY_3D,
    CUBE,
    ARRAY_CUBE,
};

enum class OffsetType : u64 {
    None = 0,
    AOFFI,
    PTP,
    Invalid,
};

enum class ComponentType : u64 {
    R = 0,
    G = 1,
    B = 2,
    A = 3,
};

Shader::TextureType GetType(TextureType type) {
    switch (type) {
    case TextureType::_1D:
        return Shader::TextureType::Color1D;
    case TextureType::ARRAY_1D:
        return Shader::TextureType::ColorArray1D;
    case TextureType::_2D:
        return Shader::TextureType::Color2D;
    case TextureType::ARRAY_2D:
        return Shader::TextureType::ColorArray2D;
    case TextureType::_3D:
        return Shader::TextureType::Color3D;
    case TextureType::ARRAY_3D:
        throw NotImplementedException("3D array texture type");
    case TextureType::CUBE:
        return Shader::TextureType::ColorCube;
    case TextureType::ARRAY_CUBE:
        return Shader::TextureType::ColorArrayCube;
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) {
    const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }};
    switch (type) {
    case TextureType::_1D:
        return v.F(reg);
    case TextureType::ARRAY_1D:
        return v.ir.CompositeConstruct(v.F(reg + 1), read_array());
    case TextureType::_2D:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
    case TextureType::ARRAY_2D:
        return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array());
    case TextureType::_3D:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
    case TextureType::ARRAY_3D:
        throw NotImplementedException("3D array texture type");
    case TextureType::CUBE:
        return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
    case TextureType::ARRAY_CUBE:
        return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array());
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) {
    const IR::U32 value{v.X(reg++)};
    switch (type) {
    case TextureType::_1D:
    case TextureType::ARRAY_1D:
        return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true);
    case TextureType::_2D:
    case TextureType::ARRAY_2D:
        return v.ir.CompositeConstruct(
            v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true));
    case TextureType::_3D:
    case TextureType::ARRAY_3D:
        return v.ir.CompositeConstruct(
            v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true),
            v.ir.BitFieldExtract(value, v.ir.Imm32(16), v.ir.Imm32(6), true));
    case TextureType::CUBE:
    case TextureType::ARRAY_CUBE:
        throw NotImplementedException("Illegal offset on CUBE sample");
    }
    throw NotImplementedException("Invalid texture type {}", type);
}

std::pair<IR::Value, IR::Value> MakeOffsetPTP(TranslatorVisitor& v, IR::Reg& reg) {
    const IR::U32 value1{v.X(reg++)};
    const IR::U32 value2{v.X(reg++)};
    const IR::U32 bitsize{v.ir.Imm32(6)};
    const auto make_vector{[&v, &bitsize](const IR::U32& value) {
        return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), bitsize, true),
                                       v.ir.BitFieldExtract(value, v.ir.Imm32(8), bitsize, true),
                                       v.ir.BitFieldExtract(value, v.ir.Imm32(16), bitsize, true),
                                       v.ir.BitFieldExtract(value, v.ir.Imm32(24), bitsize, true));
    }};
    return {make_vector(value1), make_vector(value2)};
}

void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetType offset_type,
          bool is_bindless) {
    union {
        u64 raw;
        BitField<35, 1, u64> ndv;
        BitField<49, 1, u64> nodep;
        BitField<50, 1, u64> dc;
        BitField<51, 3, IR::Pred> sparse_pred;
        BitField<0, 8, IR::Reg> dest_reg;
        BitField<8, 8, IR::Reg> coord_reg;
        BitField<20, 8, IR::Reg> meta_reg;
        BitField<28, 3, TextureType> type;
        BitField<31, 4, u64> mask;
        BitField<36, 13, u64> cbuf_offset;
    } const tld4{insn};

    const IR::Value coords{MakeCoords(v, tld4.coord_reg, tld4.type)};

    IR::Reg meta_reg{tld4.meta_reg};
    IR::Value handle;
    IR::Value offset;
    IR::Value offset2;
    IR::F32 dref;
    if (!is_bindless) {
        handle = v.ir.Imm32(static_cast<u32>(tld4.cbuf_offset.Value() * 4));
    } else {
        handle = v.X(meta_reg++);
    }
    switch (offset_type) {
    case OffsetType::None:
        break;
    case OffsetType::AOFFI:
        offset = MakeOffset(v, meta_reg, tld4.type);
        break;
    case OffsetType::PTP:
        std::tie(offset, offset2) = MakeOffsetPTP(v, meta_reg);
        break;
    default:
        throw NotImplementedException("Invalid offset type {}", offset_type);
    }
    if (tld4.dc != 0) {
        dref = v.F(meta_reg++);
    }
    IR::TextureInstInfo info{};
    info.type.Assign(GetType(tld4.type));
    info.is_depth.Assign(tld4.dc != 0 ? 1 : 0);
    info.gather_component.Assign(static_cast<u32>(component_type));
    const IR::Value sample{[&] {
        if (tld4.dc == 0) {
            return v.ir.ImageGather(handle, coords, offset, offset2, info);
        }
        return v.ir.ImageGatherDref(handle, coords, offset, offset2, dref, info);
    }()};

    IR::Reg dest_reg{tld4.dest_reg};
    for (size_t element = 0; element < 4; ++element) {
        if (((tld4.mask >> element) & 1) == 0) {
            continue;
        }
        v.F(dest_reg, IR::F32{v.ir.CompositeExtract(sample, element)});
        ++dest_reg;
    }
    if (tld4.sparse_pred != IR::Pred::PT) {
        v.ir.SetPred(tld4.sparse_pred, v.ir.LogicalNot(v.ir.GetSparseFromOp(sample)));
    }
}
} // Anonymous namespace

void TranslatorVisitor::TLD4(u64 insn) {
    union {
        u64 raw;
        BitField<56, 2, ComponentType> component;
        BitField<54, 2, OffsetType> offset;
    } const tld4{insn};
    Impl(*this, insn, tld4.component, tld4.offset, false);
}

void TranslatorVisitor::TLD4_b(u64 insn) {
    union {
        u64 raw;
        BitField<38, 2, ComponentType> component;
        BitField<36, 2, OffsetType> offset;
    } const tld4{insn};
    Impl(*this, insn, tld4.component, tld4.offset, 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/ir/modifiers.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"

namespace Shader::Maxwell {
namespace {
enum class Precision : u64 {
    F32,
    F16,
};

enum class ComponentType : u64 {
    R = 0,
    G = 1,
    B = 2,
    A = 3,
};

union Encoding {
    u64 raw;
    BitField<55, 1, Precision> precision;
    BitField<52, 2, ComponentType> component_type;
    BitField<51, 1, u64> aoffi;
    BitField<50, 1, u64> dc;
    BitField<49, 1, u64> nodep;
    BitField<28, 8, IR::Reg> dest_reg_b;
    BitField<0, 8, IR::Reg> dest_reg_a;
    BitField<8, 8, IR::Reg> src_reg_a;
    BitField<20, 8, IR::Reg> src_reg_b;
    BitField<36, 13, u64> cbuf_offset;
};

void CheckAlignment(IR::Reg reg, size_t alignment) {
    if (!IR::IsAligned(reg, alignment)) {
        throw NotImplementedException("Unaligned source register {}", reg);
    }
}

IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
    const IR::U32 value{v.X(reg)};
    return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
                                   v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true));
}

IR::Value Sample(TranslatorVisitor& v, u64 insn) {
    const Encoding tld4s{insn};
    const IR::U32 handle{v.ir.Imm32(static_cast<u32>(tld4s.cbuf_offset * 4))};
    const IR::Reg reg_a{tld4s.src_reg_a};
    const IR::Reg reg_b{tld4s.src_reg_b};
    IR::TextureInstInfo info{};
    if (tld4s.precision == Precision::F16) {
        info.relaxed_precision.Assign(1);
    }
    info.gather_component.Assign(static_cast<u32>(tld4s.component_type.Value()));
    info.type.Assign(Shader::TextureType::Color2D);
    info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
    IR::Value coords;
    if (tld4s.aoffi != 0) {
        CheckAlignment(reg_a, 2);
        coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
        IR::Value offset = MakeOffset(v, reg_b);
        if (tld4s.dc != 0) {
            CheckAlignment(reg_b, 2);
            IR::F32 dref = v.F(reg_b + 1);
            return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
        }
        return v.ir.ImageGather(handle, coords, offset, {}, info);
    }
    if (tld4s.dc != 0) {
        CheckAlignment(reg_a, 2);
        coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
        IR::F32 dref = v.F(reg_b);
        return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
    }
    coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_b));
    return v.ir.ImageGather(handle, coords, {}, {}, info);
}

IR::Reg RegStoreComponent32(u64 insn, size_t index) {
    const Encoding tlds4{insn};
    switch (index) {
    case 0:
        return tlds4.dest_reg_a;
    case 1:
        CheckAlignment(tlds4.dest_reg_a, 2);
        return tlds4.dest_reg_a + 1;
    case 2:
        return tlds4.dest_reg_b;
    case 3:
        CheckAlignment(tlds4.dest_reg_b, 2);
        return tlds4.dest_reg_b + 1;
    }
    throw LogicError("Invalid store index {}", index);
}

void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
    for (size_t component = 0; component < 4; ++component) {
        const IR::Reg dest{RegStoreComponent32(insn, component)};
        v.F(dest, IR::F32{v.ir.CompositeExtract(sample, component)});
    }
}

IR::U32 Pack(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) {
    return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs));
}

void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
    std::array<IR::F32, 4> swizzled;
    for (size_t component = 0; component < 4; ++component) {
        swizzled[component] = IR::F32{v.ir.CompositeExtract(sample, component)};
    }
    const Encoding tld4s{insn};
    v.X(tld4s.dest_reg_a, Pack(v, swizzled[0], swizzled[1]));
    v.X(tld4s.dest_reg_b, Pack(v, swizzled[2], swizzled[3]));
}
} // Anonymous namespace

void TranslatorVisitor::TLD4S(u64 insn) {
    const IR::Value sample{Sample(*this, insn)};
    if (Encoding{insn}.precision == Precision::F32) {
        Store32(*this, insn, sample);
    } else {
        Store16(*this, insn, sample);
    }
}

} // namespace Shader::Maxwell