summaryrefslogtreecommitdiff
path: root/src/video_core/shader/decode.cpp
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2018-12-20 19:09:21 -0300
committerGravatar ReinUsesLisp2019-01-15 17:54:49 -0300
commit15a0e1481d9a1efb3e3aa61cbaf2fa1ba0392d71 (patch)
treea365dda91981122a62024f371f4f00d5b2035ad6 /src/video_core/shader/decode.cpp
parentshader_bytecode: Fixup encoding (diff)
downloadyuzu-15a0e1481d9a1efb3e3aa61cbaf2fa1ba0392d71.tar.gz
yuzu-15a0e1481d9a1efb3e3aa61cbaf2fa1ba0392d71.tar.xz
yuzu-15a0e1481d9a1efb3e3aa61cbaf2fa1ba0392d71.zip
shader_ir: Initial implementation
Diffstat (limited to 'src/video_core/shader/decode.cpp')
-rw-r--r--src/video_core/shader/decode.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
new file mode 100644
index 000000000..c973328ab
--- /dev/null
+++ b/src/video_core/shader/decode.cpp
@@ -0,0 +1,199 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <set>
7
8#include <fmt/format.h>
9
10#include "common/common_types.h"
11#include "video_core/engines/shader_bytecode.h"
12#include "video_core/engines/shader_header.h"
13#include "video_core/shader/shader_ir.h"
14
15namespace VideoCommon::Shader {
16
17using Tegra::Shader::Instruction;
18using Tegra::Shader::OpCode;
19
20/// Merges exit method of two parallel branches.
21constexpr ExitMethod ParallelExit(ExitMethod a, ExitMethod b) {
22 if (a == ExitMethod::Undetermined) {
23 return b;
24 }
25 if (b == ExitMethod::Undetermined) {
26 return a;
27 }
28 if (a == b) {
29 return a;
30 }
31 return ExitMethod::Conditional;
32}
33
34/**
35 * Returns whether the instruction at the specified offset is a 'sched' instruction.
36 * Sched instructions always appear before a sequence of 3 instructions.
37 */
38constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
39 constexpr u32 SchedPeriod = 4;
40 u32 absolute_offset = offset - main_offset;
41
42 return (absolute_offset % SchedPeriod) == 0;
43}
44
45void ShaderIR::Decode() {
46 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
47
48 std::set<u32> labels;
49 const ExitMethod exit_method = Scan(main_offset, MAX_PROGRAM_LENGTH, labels);
50 if (exit_method != ExitMethod::AlwaysEnd) {
51 UNREACHABLE_MSG("Program does not always end");
52 }
53
54 if (labels.empty()) {
55 basic_blocks.insert({main_offset, DecodeRange(main_offset, MAX_PROGRAM_LENGTH)});
56 return;
57 }
58
59 labels.insert(main_offset);
60
61 for (const u32 label : labels) {
62 const auto next_it = labels.lower_bound(label + 1);
63 const u32 next_label = next_it == labels.end() ? MAX_PROGRAM_LENGTH : *next_it;
64
65 basic_blocks.insert({label, DecodeRange(label, next_label)});
66 }
67}
68
69ExitMethod ShaderIR::Scan(u32 begin, u32 end, std::set<u32>& labels) {
70 const auto [iter, inserted] =
71 exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined);
72 ExitMethod& exit_method = iter->second;
73 if (!inserted)
74 return exit_method;
75
76 for (u32 offset = begin; offset != end && offset != MAX_PROGRAM_LENGTH; ++offset) {
77 coverage_begin = std::min(coverage_begin, offset);
78 coverage_end = std::max(coverage_end, offset + 1);
79
80 const Instruction instr = {program_code[offset]};
81 const auto opcode = OpCode::Decode(instr);
82 if (!opcode)
83 continue;
84 switch (opcode->get().GetId()) {
85 case OpCode::Id::EXIT: {
86 // The EXIT instruction can be predicated, which means that the shader can conditionally
87 // end on this instruction. We have to consider the case where the condition is not met
88 // and check the exit method of that other basic block.
89 using Tegra::Shader::Pred;
90 if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
91 return exit_method = ExitMethod::AlwaysEnd;
92 } else {
93 const ExitMethod not_met = Scan(offset + 1, end, labels);
94 return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met);
95 }
96 }
97 case OpCode::Id::BRA: {
98 const u32 target = offset + instr.bra.GetBranchTarget();
99 labels.insert(target);
100 const ExitMethod no_jmp = Scan(offset + 1, end, labels);
101 const ExitMethod jmp = Scan(target, end, labels);
102 return exit_method = ParallelExit(no_jmp, jmp);
103 }
104 case OpCode::Id::SSY:
105 case OpCode::Id::PBK: {
106 // The SSY and PBK use a similar encoding as the BRA instruction.
107 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
108 "Constant buffer branching is not supported");
109 const u32 target = offset + instr.bra.GetBranchTarget();
110 labels.insert(target);
111 // Continue scanning for an exit method.
112 break;
113 }
114 }
115 }
116 return exit_method = ExitMethod::AlwaysReturn;
117}
118
119BasicBlock ShaderIR::DecodeRange(u32 begin, u32 end) {
120 BasicBlock basic_block;
121 for (u32 pc = begin; pc < (begin > end ? MAX_PROGRAM_LENGTH : end);) {
122 pc = DecodeInstr(basic_block, pc);
123 }
124 return std::move(basic_block);
125}
126
127u32 ShaderIR::DecodeInstr(BasicBlock& bb, u32 pc) {
128 // Ignore sched instructions when generating code.
129 if (IsSchedInstruction(pc, main_offset)) {
130 return pc + 1;
131 }
132
133 const Instruction instr = {program_code[pc]};
134 const auto opcode = OpCode::Decode(instr);
135
136 // Decoding failure
137 if (!opcode) {
138 UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value);
139 return pc + 1;
140 }
141
142 bb.push_back(
143 Comment(fmt::format("{}: {} (0x{:016x})", pc, opcode->get().GetName(), instr.value)));
144
145 using Tegra::Shader::Pred;
146 UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute,
147 "NeverExecute predicate not implemented");
148
149 static const std::map<OpCode::Type, u32 (ShaderIR::*)(BasicBlock & code, u32 pc)> decoders = {
150 {OpCode::Type::Arithmetic, &ShaderIR::DecodeArithmetic},
151 {OpCode::Type::ArithmeticImmediate, &ShaderIR::DecodeArithmeticImmediate},
152 {OpCode::Type::Bfe, &ShaderIR::DecodeBfe},
153 {OpCode::Type::Bfi, &ShaderIR::DecodeBfi},
154 {OpCode::Type::Shift, &ShaderIR::DecodeShift},
155 {OpCode::Type::ArithmeticInteger, &ShaderIR::DecodeArithmeticInteger},
156 {OpCode::Type::ArithmeticIntegerImmediate, &ShaderIR::DecodeArithmeticIntegerImmediate},
157 {OpCode::Type::ArithmeticHalf, &ShaderIR::DecodeArithmeticHalf},
158 {OpCode::Type::ArithmeticHalfImmediate, &ShaderIR::DecodeArithmeticHalfImmediate},
159 {OpCode::Type::Ffma, &ShaderIR::DecodeFfma},
160 {OpCode::Type::Hfma2, &ShaderIR::DecodeHfma2},
161 {OpCode::Type::Conversion, &ShaderIR::DecodeConversion},
162 {OpCode::Type::Memory, &ShaderIR::DecodeMemory},
163 {OpCode::Type::FloatSetPredicate, &ShaderIR::DecodeFloatSetPredicate},
164 {OpCode::Type::IntegerSetPredicate, &ShaderIR::DecodeIntegerSetPredicate},
165 {OpCode::Type::HalfSetPredicate, &ShaderIR::DecodeHalfSetPredicate},
166 {OpCode::Type::PredicateSetRegister, &ShaderIR::DecodePredicateSetRegister},
167 {OpCode::Type::PredicateSetPredicate, &ShaderIR::DecodePredicateSetPredicate},
168 {OpCode::Type::RegisterSetPredicate, &ShaderIR::DecodeRegisterSetPredicate},
169 {OpCode::Type::FloatSet, &ShaderIR::DecodeFloatSet},
170 {OpCode::Type::IntegerSet, &ShaderIR::DecodeIntegerSet},
171 {OpCode::Type::HalfSet, &ShaderIR::DecodeHalfSet},
172 {OpCode::Type::Xmad, &ShaderIR::DecodeXmad},
173 };
174
175 std::vector<Node> code;
176 if (const auto decoder = decoders.find(opcode->get().GetType()); decoder != decoders.end()) {
177 pc = (this->*decoder->second)(code, pc);
178 } else {
179 pc = DecodeOther(code, pc);
180 }
181
182 // Some instructions (like SSY) don't have a predicate field, they are always unconditionally
183 // executed.
184 const bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->get().GetId());
185 const auto pred_index = static_cast<u32>(instr.pred.pred_index);
186
187 if (can_be_predicated && pred_index != static_cast<u32>(Pred::UnusedIndex)) {
188 bb.push_back(
189 Conditional(GetPredicate(pred_index, instr.negate_pred != 0), std::move(code)));
190 } else {
191 for (auto& node : code) {
192 bb.push_back(std::move(node));
193 }
194 }
195
196 return pc + 1;
197}
198
199} // namespace VideoCommon::Shader \ No newline at end of file