diff options
Diffstat (limited to 'src/shader_recompiler/frontend/ir/microinstruction.cpp')
| -rw-r--r-- | src/shader_recompiler/frontend/ir/microinstruction.cpp | 102 |
1 files changed, 74 insertions, 28 deletions
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index de953838c..e7ca92039 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | ||
| 6 | 7 | ||
| 7 | #include "shader_recompiler/exception.h" | 8 | #include "shader_recompiler/exception.h" |
| 8 | #include "shader_recompiler/frontend/ir/microinstruction.h" | 9 | #include "shader_recompiler/frontend/ir/microinstruction.h" |
| @@ -30,6 +31,22 @@ static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) | |||
| 30 | inst = nullptr; | 31 | inst = nullptr; |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 34 | Inst::Inst(IR::Opcode op_, u64 flags_) noexcept : op{op_}, flags{flags_} { | ||
| 35 | if (op == Opcode::Phi) { | ||
| 36 | std::construct_at(&phi_args); | ||
| 37 | } else { | ||
| 38 | std::construct_at(&args); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | Inst::~Inst() { | ||
| 43 | if (op == Opcode::Phi) { | ||
| 44 | std::destroy_at(&phi_args); | ||
| 45 | } else { | ||
| 46 | std::destroy_at(&args); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 33 | bool Inst::MayHaveSideEffects() const noexcept { | 50 | bool Inst::MayHaveSideEffects() const noexcept { |
| 34 | switch (op) { | 51 | switch (op) { |
| 35 | case Opcode::Branch: | 52 | case Opcode::Branch: |
| @@ -71,7 +88,10 @@ bool Inst::IsPseudoInstruction() const noexcept { | |||
| 71 | } | 88 | } |
| 72 | } | 89 | } |
| 73 | 90 | ||
| 74 | bool Inst::AreAllArgsImmediates() const noexcept { | 91 | bool Inst::AreAllArgsImmediates() const { |
| 92 | if (op == Opcode::Phi) { | ||
| 93 | throw LogicError("Testing for all arguments are immediates on phi instruction"); | ||
| 94 | } | ||
| 75 | return std::all_of(args.begin(), args.begin() + NumArgs(), | 95 | return std::all_of(args.begin(), args.begin() + NumArgs(), |
| 76 | [](const IR::Value& value) { return value.IsImmediate(); }); | 96 | [](const IR::Value& value) { return value.IsImmediate(); }); |
| 77 | } | 97 | } |
| @@ -101,7 +121,7 @@ Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) { | |||
| 101 | } | 121 | } |
| 102 | 122 | ||
| 103 | size_t Inst::NumArgs() const { | 123 | size_t Inst::NumArgs() const { |
| 104 | return NumArgsOf(op); | 124 | return op == Opcode::Phi ? phi_args.size() : NumArgsOf(op); |
| 105 | } | 125 | } |
| 106 | 126 | ||
| 107 | IR::Type Inst::Type() const { | 127 | IR::Type Inst::Type() const { |
| @@ -109,13 +129,23 @@ IR::Type Inst::Type() const { | |||
| 109 | } | 129 | } |
| 110 | 130 | ||
| 111 | Value Inst::Arg(size_t index) const { | 131 | Value Inst::Arg(size_t index) const { |
| 112 | if (index >= NumArgsOf(op)) { | 132 | if (op == Opcode::Phi) { |
| 113 | throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | 133 | if (index >= phi_args.size()) { |
| 134 | throw InvalidArgument("Out of bounds argument index {} in phi instruction", index); | ||
| 135 | } | ||
| 136 | return phi_args[index].second; | ||
| 137 | } else { | ||
| 138 | if (index >= NumArgsOf(op)) { | ||
| 139 | throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | ||
| 140 | } | ||
| 141 | return args[index]; | ||
| 114 | } | 142 | } |
| 115 | return args[index]; | ||
| 116 | } | 143 | } |
| 117 | 144 | ||
| 118 | void Inst::SetArg(size_t index, Value value) { | 145 | void Inst::SetArg(size_t index, Value value) { |
| 146 | if (op == Opcode::Phi) { | ||
| 147 | throw LogicError("Setting argument on a phi instruction"); | ||
| 148 | } | ||
| 119 | if (index >= NumArgsOf(op)) { | 149 | if (index >= NumArgsOf(op)) { |
| 120 | throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | 150 | throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); |
| 121 | } | 151 | } |
| @@ -128,15 +158,21 @@ void Inst::SetArg(size_t index, Value value) { | |||
| 128 | args[index] = value; | 158 | args[index] = value; |
| 129 | } | 159 | } |
| 130 | 160 | ||
| 131 | std::span<const std::pair<Block*, Value>> Inst::PhiOperands() const noexcept { | 161 | Block* Inst::PhiBlock(size_t index) const { |
| 132 | return phi_operands; | 162 | if (op != Opcode::Phi) { |
| 163 | throw LogicError("{} is not a Phi instruction", op); | ||
| 164 | } | ||
| 165 | if (index >= phi_args.size()) { | ||
| 166 | throw InvalidArgument("Out of bounds argument index {} in phi instruction"); | ||
| 167 | } | ||
| 168 | return phi_args[index].first; | ||
| 133 | } | 169 | } |
| 134 | 170 | ||
| 135 | void Inst::AddPhiOperand(Block* predecessor, const Value& value) { | 171 | void Inst::AddPhiOperand(Block* predecessor, const Value& value) { |
| 136 | if (!value.IsImmediate()) { | 172 | if (!value.IsImmediate()) { |
| 137 | Use(value); | 173 | Use(value); |
| 138 | } | 174 | } |
| 139 | phi_operands.emplace_back(predecessor, value); | 175 | phi_args.emplace_back(predecessor, value); |
| 140 | } | 176 | } |
| 141 | 177 | ||
| 142 | void Inst::Invalidate() { | 178 | void Inst::Invalidate() { |
| @@ -145,18 +181,22 @@ void Inst::Invalidate() { | |||
| 145 | } | 181 | } |
| 146 | 182 | ||
| 147 | void Inst::ClearArgs() { | 183 | void Inst::ClearArgs() { |
| 148 | for (auto& value : args) { | 184 | if (op == Opcode::Phi) { |
| 149 | if (!value.IsImmediate()) { | 185 | for (auto& pair : phi_args) { |
| 150 | UndoUse(value); | 186 | IR::Value& value{pair.second}; |
| 187 | if (!value.IsImmediate()) { | ||
| 188 | UndoUse(value); | ||
| 189 | } | ||
| 151 | } | 190 | } |
| 152 | value = {}; | 191 | phi_args.clear(); |
| 153 | } | 192 | } else { |
| 154 | for (auto& [phi_block, phi_op] : phi_operands) { | 193 | for (auto& value : args) { |
| 155 | if (!phi_op.IsImmediate()) { | 194 | if (!value.IsImmediate()) { |
| 156 | UndoUse(phi_op); | 195 | UndoUse(value); |
| 196 | } | ||
| 197 | value = {}; | ||
| 157 | } | 198 | } |
| 158 | } | 199 | } |
| 159 | phi_operands.clear(); | ||
| 160 | } | 200 | } |
| 161 | 201 | ||
| 162 | void Inst::ReplaceUsesWith(Value replacement) { | 202 | void Inst::ReplaceUsesWith(Value replacement) { |
| @@ -167,24 +207,29 @@ void Inst::ReplaceUsesWith(Value replacement) { | |||
| 167 | if (!replacement.IsImmediate()) { | 207 | if (!replacement.IsImmediate()) { |
| 168 | Use(replacement); | 208 | Use(replacement); |
| 169 | } | 209 | } |
| 170 | args[0] = replacement; | 210 | if (op == Opcode::Phi) { |
| 211 | phi_args[0].second = replacement; | ||
| 212 | } else { | ||
| 213 | args[0] = replacement; | ||
| 214 | } | ||
| 171 | } | 215 | } |
| 172 | 216 | ||
| 173 | void Inst::Use(const Value& value) { | 217 | void Inst::Use(const Value& value) { |
| 174 | ++value.Inst()->use_count; | 218 | Inst* const inst{value.Inst()}; |
| 219 | ++inst->use_count; | ||
| 175 | 220 | ||
| 176 | switch (op) { | 221 | switch (op) { |
| 177 | case Opcode::GetZeroFromOp: | 222 | case Opcode::GetZeroFromOp: |
| 178 | SetPseudoInstruction(value.Inst()->zero_inst, this); | 223 | SetPseudoInstruction(inst->zero_inst, this); |
| 179 | break; | 224 | break; |
| 180 | case Opcode::GetSignFromOp: | 225 | case Opcode::GetSignFromOp: |
| 181 | SetPseudoInstruction(value.Inst()->sign_inst, this); | 226 | SetPseudoInstruction(inst->sign_inst, this); |
| 182 | break; | 227 | break; |
| 183 | case Opcode::GetCarryFromOp: | 228 | case Opcode::GetCarryFromOp: |
| 184 | SetPseudoInstruction(value.Inst()->carry_inst, this); | 229 | SetPseudoInstruction(inst->carry_inst, this); |
| 185 | break; | 230 | break; |
| 186 | case Opcode::GetOverflowFromOp: | 231 | case Opcode::GetOverflowFromOp: |
| 187 | SetPseudoInstruction(value.Inst()->overflow_inst, this); | 232 | SetPseudoInstruction(inst->overflow_inst, this); |
| 188 | break; | 233 | break; |
| 189 | default: | 234 | default: |
| 190 | break; | 235 | break; |
| @@ -192,20 +237,21 @@ void Inst::Use(const Value& value) { | |||
| 192 | } | 237 | } |
| 193 | 238 | ||
| 194 | void Inst::UndoUse(const Value& value) { | 239 | void Inst::UndoUse(const Value& value) { |
| 195 | --value.Inst()->use_count; | 240 | Inst* const inst{value.Inst()}; |
| 241 | --inst->use_count; | ||
| 196 | 242 | ||
| 197 | switch (op) { | 243 | switch (op) { |
| 198 | case Opcode::GetZeroFromOp: | 244 | case Opcode::GetZeroFromOp: |
| 199 | RemovePseudoInstruction(value.Inst()->zero_inst, Opcode::GetZeroFromOp); | 245 | RemovePseudoInstruction(inst->zero_inst, Opcode::GetZeroFromOp); |
| 200 | break; | 246 | break; |
| 201 | case Opcode::GetSignFromOp: | 247 | case Opcode::GetSignFromOp: |
| 202 | RemovePseudoInstruction(value.Inst()->sign_inst, Opcode::GetSignFromOp); | 248 | RemovePseudoInstruction(inst->sign_inst, Opcode::GetSignFromOp); |
| 203 | break; | 249 | break; |
| 204 | case Opcode::GetCarryFromOp: | 250 | case Opcode::GetCarryFromOp: |
| 205 | RemovePseudoInstruction(value.Inst()->carry_inst, Opcode::GetCarryFromOp); | 251 | RemovePseudoInstruction(inst->carry_inst, Opcode::GetCarryFromOp); |
| 206 | break; | 252 | break; |
| 207 | case Opcode::GetOverflowFromOp: | 253 | case Opcode::GetOverflowFromOp: |
| 208 | RemovePseudoInstruction(value.Inst()->overflow_inst, Opcode::GetOverflowFromOp); | 254 | RemovePseudoInstruction(inst->overflow_inst, Opcode::GetOverflowFromOp); |
| 209 | break; | 255 | break; |
| 210 | default: | 256 | default: |
| 211 | break; | 257 | break; |