summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/ir/basic_block.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/frontend/ir/basic_block.cpp')
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
new file mode 100644
index 000000000..7c08b25ce
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -0,0 +1,149 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <initializer_list>
7#include <map>
8#include <memory>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/value.h"
14
15namespace Shader::IR {
16
17Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
18
19Block::~Block() = default;
20
21void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
22 PrependNewInst(end(), op, args);
23}
24
25Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
26 std::initializer_list<Value> args, u32 flags) {
27 Inst* const inst{inst_pool->Create(op, flags)};
28 const auto result_it{instructions.insert(insertion_point, *inst)};
29
30 if (inst->NumArgs() != args.size()) {
31 throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
32 }
33 std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
34 inst->SetArg(index, arg);
35 ++index;
36 });
37 return result_it;
38}
39
40void Block::AddBranch(Block* block) {
41 if (std::ranges::find(imm_successors, block) != imm_successors.end()) {
42 throw LogicError("Successor already inserted");
43 }
44 if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) {
45 throw LogicError("Predecessor already inserted");
46 }
47 imm_successors.push_back(block);
48 block->imm_predecessors.push_back(this);
49}
50
51static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
52 Block* block) {
53 if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
54 return fmt::format("{{Block ${}}}", it->second);
55 }
56 return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
57}
58
59static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
60 const Inst* inst) {
61 const auto [it, is_inserted]{inst_to_index.emplace(inst, inst_index + 1)};
62 if (is_inserted) {
63 ++inst_index;
64 }
65 return it->second;
66}
67
68static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
69 const Value& arg) {
70 if (arg.IsEmpty()) {
71 return "<null>";
72 }
73 if (!arg.IsImmediate() || arg.IsIdentity()) {
74 return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
75 }
76 switch (arg.Type()) {
77 case Type::U1:
78 return fmt::format("#{}", arg.U1() ? "true" : "false");
79 case Type::U8:
80 return fmt::format("#{}", arg.U8());
81 case Type::U16:
82 return fmt::format("#{}", arg.U16());
83 case Type::U32:
84 return fmt::format("#{}", arg.U32());
85 case Type::U64:
86 return fmt::format("#{}", arg.U64());
87 case Type::F32:
88 return fmt::format("#{}", arg.F32());
89 case Type::Reg:
90 return fmt::format("{}", arg.Reg());
91 case Type::Pred:
92 return fmt::format("{}", arg.Pred());
93 case Type::Attribute:
94 return fmt::format("{}", arg.Attribute());
95 default:
96 return "<unknown immediate type>";
97 }
98}
99
100std::string DumpBlock(const Block& block) {
101 size_t inst_index{0};
102 std::map<const Inst*, size_t> inst_to_index;
103 return DumpBlock(block, {}, inst_to_index, inst_index);
104}
105
106std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
107 std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
108 std::string ret{"Block"};
109 if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
110 ret += fmt::format(" ${}", it->second);
111 }
112 ret += '\n';
113 for (const Inst& inst : block) {
114 const Opcode op{inst.GetOpcode()};
115 ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
116 if (TypeOf(op) != Type::Void) {
117 ret += fmt::format("%{:<5} = {}", InstIndex(inst_to_index, inst_index, &inst), op);
118 } else {
119 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
120 }
121 const size_t arg_count{inst.NumArgs()};
122 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
123 const Value arg{inst.Arg(arg_index)};
124 const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)};
125 ret += arg_index != 0 ? ", " : " ";
126 if (op == Opcode::Phi) {
127 ret += fmt::format("[ {}, {} ]", arg_str,
128 BlockToIndex(block_to_index, inst.PhiBlock(arg_index)));
129 } else {
130 ret += arg_str;
131 }
132 if (op != Opcode::Phi) {
133 const Type actual_type{arg.Type()};
134 const Type expected_type{ArgTypeOf(op, arg_index)};
135 if (!AreTypesCompatible(actual_type, expected_type)) {
136 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
137 }
138 }
139 }
140 if (TypeOf(op) != Type::Void) {
141 ret += fmt::format(" (uses: {})\n", inst.UseCount());
142 } else {
143 ret += '\n';
144 }
145 }
146 return ret;
147}
148
149} // namespace Shader::IR