summaryrefslogtreecommitdiffstats
path: root/src/video_core/shader/decode/xmad.cpp
blob: 61d23649d12b34e67c10407e49c654afb283ddab (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
// 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/node_helper.h"
#include "video_core/shader/shader_ir.h"

namespace VideoCommon::Shader {

using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
using Tegra::Shader::PredCondition;

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

    UNIMPLEMENTED_IF(instr.xmad.sign_a);
    UNIMPLEMENTED_IF(instr.xmad.sign_b);
    UNIMPLEMENTED_IF_MSG(instr.generates_cc,
                         "Condition codes generation in XMAD is not implemented");

    Node op_a = GetRegister(instr.gpr8);

    // TODO(bunnei): Needs to be fixed once op_a or op_b is signed
    UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b);
    const bool is_signed_a = instr.xmad.sign_a == 1;
    const bool is_signed_b = instr.xmad.sign_b == 1;
    const bool is_signed_c = is_signed_a;

    auto [is_merge, is_psl, is_high_b, mode, op_b,
          op_c] = [&]() -> std::tuple<bool, bool, bool, Tegra::Shader::XmadMode, Node, Node> {
        switch (opcode->get().GetId()) {
        case OpCode::Id::XMAD_CR:
            return {instr.xmad.merge_56,
                    instr.xmad.product_shift_left_second,
                    instr.xmad.high_b,
                    instr.xmad.mode_cbf,
                    GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
                    GetRegister(instr.gpr39)};
        case OpCode::Id::XMAD_RR:
            return {instr.xmad.merge_37, instr.xmad.product_shift_left, instr.xmad.high_b_rr,
                    instr.xmad.mode,     GetRegister(instr.gpr20),      GetRegister(instr.gpr39)};
        case OpCode::Id::XMAD_RC:
            return {false,
                    false,
                    instr.xmad.high_b,
                    instr.xmad.mode_cbf,
                    GetRegister(instr.gpr39),
                    GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
        case OpCode::Id::XMAD_IMM:
            return {instr.xmad.merge_37,
                    instr.xmad.product_shift_left,
                    false,
                    instr.xmad.mode,
                    Immediate(static_cast<u32>(instr.xmad.imm20_16)),
                    GetRegister(instr.gpr39)};
        default:
            UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
            return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)};
        }
    }();

    op_a = SignedOperation(OperationCode::IBitfieldExtract, is_signed_a, std::move(op_a),
                           instr.xmad.high_a ? Immediate(16) : Immediate(0), Immediate(16));

    const Node original_b = op_b;
    op_b = SignedOperation(OperationCode::IBitfieldExtract, is_signed_b, std::move(op_b),
                           is_high_b ? Immediate(16) : Immediate(0), Immediate(16));

    // we already check sign_a and sign_b is difference or not before so just use one in here.
    Node product = SignedOperation(OperationCode::IMul, is_signed_a, op_a, op_b);
    if (is_psl) {
        product =
            SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_a, product, Immediate(16));
    }
    SetTemporary(bb, 0, product);
    product = GetTemporary(0);

    const Node original_c = op_c;
    const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error
    op_c = [&]() {
        switch (set_mode) {
        case Tegra::Shader::XmadMode::None:
            return original_c;
        case Tegra::Shader::XmadMode::CLo:
            return BitfieldExtract(original_c, 0, 16);
        case Tegra::Shader::XmadMode::CHi:
            return BitfieldExtract(original_c, 16, 16);
        case Tegra::Shader::XmadMode::CBcc: {
            const Node shifted_b = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_b,
                                                   original_b, Immediate(16));
            return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b);
        }
        case Tegra::Shader::XmadMode::CSfu: {
            const Node comp_a = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_a,
                                                              op_a, Immediate(0));
            const Node comp_b = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_b,
                                                              op_b, Immediate(0));
            const Node comp =
                Operation(OperationCode::LogicalOr, std::move(comp_a), std::move(comp_b));

            const Node comp_minus_a = GetPredicateComparisonInteger(
                PredCondition::NotEqual, is_signed_a,
                SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a,
                                Immediate(0x80000000)),
                Immediate(0));
            const Node comp_minus_b = GetPredicateComparisonInteger(
                PredCondition::NotEqual, is_signed_b,
                SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b,
                                Immediate(0x80000000)),
                Immediate(0));

            Node new_c = Operation(
                OperationCode::Select, comp_minus_a,
                SignedOperation(OperationCode::IAdd, is_signed_c, original_c, Immediate(-65536)),
                original_c);
            new_c = Operation(
                OperationCode::Select, comp_minus_b,
                SignedOperation(OperationCode::IAdd, is_signed_c, new_c, Immediate(-65536)), new_c);

            return Operation(OperationCode::Select, comp, original_c, new_c);
        }
        default:
            UNREACHABLE();
            return Immediate(0);
        }
    }();

    SetTemporary(bb, 1, op_c);
    op_c = GetTemporary(1);

    // TODO(Rodrigo): Use an appropiate sign for this operation
    Node sum = Operation(OperationCode::IAdd, product, op_c);
    SetTemporary(bb, 2, sum);
    sum = GetTemporary(2);
    if (is_merge) {
        const Node a = BitfieldExtract(sum, 0, 16);
        const Node b =
            Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, original_b, Immediate(16));
        sum = Operation(OperationCode::IBitwiseOr, NO_PRECISE, a, b);
    }

    SetInternalFlagsFromInteger(bb, sum, instr.generates_cc);
    SetRegister(bb, instr.gpr0, sum);

    return pc;
}

} // namespace VideoCommon::Shader