summaryrefslogtreecommitdiffstats
path: root/src/video_core/shader/decode/arithmetic_integer.cpp
blob: 145bbcfc8f9a0fa82da65b8e7899382affd271cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/shader_ir.h"

namespace VideoCommon::Shader {

using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
using Tegra::Shader::Register;

u32 ShaderIR::DecodeArithmeticInteger(BasicBlock& bb, u32 pc) {
    const Instruction instr = {program_code[pc]};
    const auto opcode = OpCode::Decode(instr);

    Node op_a = GetRegister(instr.gpr8);
    Node op_b = [&]() {
        if (instr.is_b_imm) {
            return Immediate(instr.alu.GetSignedImm20_20());
        } else if (instr.is_b_gpr) {
            return GetRegister(instr.gpr20);
        } else {
            return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset);
        }
    }();

    switch (opcode->get().GetId()) {
    case OpCode::Id::IADD_C:
    case OpCode::Id::IADD_R:
    case OpCode::Id::IADD_IMM: {
        UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                             "Condition codes generation in IADD is not implemented");
        UNIMPLEMENTED_IF_MSG(instr.alu.saturate_d, "IADD saturation not implemented");

        op_a = GetOperandAbsNegInteger(op_a, false, instr.alu_integer.negate_a, true);
        op_b = GetOperandAbsNegInteger(op_b, false, instr.alu_integer.negate_b, true);

        SetRegister(bb, instr.gpr0, Operation(OperationCode::IAdd, PRECISE, op_a, op_b));
        break;
    }
    case OpCode::Id::ISCADD_C:
    case OpCode::Id::ISCADD_R:
    case OpCode::Id::ISCADD_IMM: {
        UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                             "Condition codes generation in ISCADD is not implemented");

        op_a = GetOperandAbsNegInteger(op_a, false, instr.alu_integer.negate_a, true);
        op_b = GetOperandAbsNegInteger(op_b, false, instr.alu_integer.negate_b, true);

        const Node shift = Immediate(static_cast<u32>(instr.alu_integer.shift_amount));
        const Node shifted_a = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, op_a, shift);
        const Node value = Operation(OperationCode::IAdd, NO_PRECISE, shifted_a, op_b);
        SetRegister(bb, instr.gpr0, value);
        break;
    }
    case OpCode::Id::SEL_C:
    case OpCode::Id::SEL_R:
    case OpCode::Id::SEL_IMM: {
        const Node condition = GetPredicate(instr.sel.pred, instr.sel.neg_pred != 0);
        const Node value = Operation(OperationCode::Select, PRECISE, condition, op_a, op_b);
        SetRegister(bb, instr.gpr0, value);
        break;
    }
    case OpCode::Id::LOP_C:
    case OpCode::Id::LOP_R:
    case OpCode::Id::LOP_IMM: {
        UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                             "Condition codes generation in LOP is not implemented");

        if (instr.alu.lop.invert_a)
            op_a = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_a);
        if (instr.alu.lop.invert_b)
            op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_b);

        WriteLogicOperation(bb, instr.gpr0, instr.alu.lop.operation, op_a, op_b,
                            instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
        break;
    }
    case OpCode::Id::LOP3_C:
    case OpCode::Id::LOP3_R:
    case OpCode::Id::LOP3_IMM: {
        UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                             "Condition codes generation in LOP3 is not implemented");

        const Node op_c = GetRegister(instr.gpr39);
        const Node lut = [&]() {
            if (opcode->get().GetId() == OpCode::Id::LOP3_R) {
                return Immediate(instr.alu.lop3.GetImmLut28());
            } else {
                return Immediate(instr.alu.lop3.GetImmLut48());
            }
        }();

        WriteLop3Instruction(bb, instr.gpr0, op_a, op_b, op_c, lut);
        break;
    }
    case OpCode::Id::IMNMX_C:
    case OpCode::Id::IMNMX_R:
    case OpCode::Id::IMNMX_IMM: {
        UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
        UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                             "Condition codes generation in IMNMX is not implemented");

        const bool is_signed = instr.imnmx.is_signed;

        const Node condition = GetPredicate(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
        const Node min = SignedOperation(OperationCode::IMin, is_signed, NO_PRECISE, op_a, op_b);
        const Node max = SignedOperation(OperationCode::IMax, is_signed, NO_PRECISE, op_a, op_b);
        const Node value = Operation(OperationCode::Select, NO_PRECISE, condition, min, max);
        SetRegister(bb, instr.gpr0, value);
        break;
    }
    default:
        UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}", opcode->get().GetName());
    }

    return pc;
}

void ShaderIR::WriteLop3Instruction(BasicBlock& bb, Register dest, Node op_a, Node op_b, Node op_c,
                                    Node imm_lut) {
    constexpr u32 lop_iterations = 32;
    const Node one = Immediate(1);
    const Node two = Immediate(2);

    Node value{};
    for (u32 i = 0; i < lop_iterations; ++i) {
        const Node shift_amount = Immediate(i);

        const Node a = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_c, shift_amount);
        const Node pack_0 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, one);

        const Node b = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_b, shift_amount);
        const Node c = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, b, one);
        const Node pack_1 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, c, one);

        const Node d = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_a, shift_amount);
        const Node e = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, d, one);
        const Node pack_2 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, e, two);

        const Node pack_01 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_0, pack_1);
        const Node pack_012 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_01, pack_2);

        const Node shifted_bit =
            Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, imm_lut, pack_012);
        const Node bit = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, shifted_bit, one);

        const Node right =
            Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, bit, shift_amount);

        if (i > 0) {
            value = Operation(OperationCode::IBitwiseOr, NO_PRECISE, value, right);
        } else {
            value = right;
        }
    }

    SetRegister(bb, dest, value);
}

} // namespace VideoCommon::Shader