diff options
| author | 2021-02-05 05:58:02 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:21 -0400 | |
| commit | e81739493a0cacc1efe3295f9d287d5d31b1a989 (patch) | |
| tree | 11a3d04ce9def535414a00226030798f337c053c /src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | |
| parent | shader: Initial instruction support (diff) | |
| download | yuzu-e81739493a0cacc1efe3295f9d287d5d31b1a989.tar.gz yuzu-e81739493a0cacc1efe3295f9d287d5d31b1a989.tar.xz yuzu-e81739493a0cacc1efe3295f9d287d5d31b1a989.zip | |
shader: Constant propagation and global memory to storage buffer
Diffstat (limited to 'src/shader_recompiler/ir_opt/constant_propagation_pass.cpp')
| -rw-r--r-- | src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp new file mode 100644 index 000000000..02f5b653d --- /dev/null +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | |||
| @@ -0,0 +1,146 @@ | |||
| 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 <type_traits> | ||
| 7 | |||
| 8 | #include "common/bit_util.h" | ||
| 9 | #include "shader_recompiler/exception.h" | ||
| 10 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 11 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 12 | |||
| 13 | namespace Shader::Optimization { | ||
| 14 | namespace { | ||
| 15 | [[nodiscard]] u32 BitFieldUExtract(u32 base, u32 shift, u32 count) { | ||
| 16 | if (static_cast<size_t>(shift) + static_cast<size_t>(count) > Common::BitSize<u32>()) { | ||
| 17 | throw LogicError("Undefined result in BitFieldUExtract({}, {}, {})", base, shift, count); | ||
| 18 | } | ||
| 19 | return (base >> shift) & ((1U << count) - 1); | ||
| 20 | } | ||
| 21 | |||
| 22 | template <typename T> | ||
| 23 | [[nodiscard]] T Arg(const IR::Value& value) { | ||
| 24 | if constexpr (std::is_same_v<T, bool>) { | ||
| 25 | return value.U1(); | ||
| 26 | } else if constexpr (std::is_same_v<T, u32>) { | ||
| 27 | return value.U32(); | ||
| 28 | } else if constexpr (std::is_same_v<T, u64>) { | ||
| 29 | return value.U64(); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | template <typename ImmFn> | ||
| 34 | bool FoldCommutative(IR::Inst& inst, ImmFn&& imm_fn) { | ||
| 35 | const auto arg = [](const IR::Value& value) { | ||
| 36 | if constexpr (std::is_invocable_r_v<bool, ImmFn, bool, bool>) { | ||
| 37 | return value.U1(); | ||
| 38 | } else if constexpr (std::is_invocable_r_v<u32, ImmFn, u32, u32>) { | ||
| 39 | return value.U32(); | ||
| 40 | } else if constexpr (std::is_invocable_r_v<u64, ImmFn, u64, u64>) { | ||
| 41 | return value.U64(); | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | const IR::Value lhs{inst.Arg(0)}; | ||
| 45 | const IR::Value rhs{inst.Arg(1)}; | ||
| 46 | |||
| 47 | const bool is_lhs_immediate{lhs.IsImmediate()}; | ||
| 48 | const bool is_rhs_immediate{rhs.IsImmediate()}; | ||
| 49 | |||
| 50 | if (is_lhs_immediate && is_rhs_immediate) { | ||
| 51 | const auto result{imm_fn(arg(lhs), arg(rhs))}; | ||
| 52 | inst.ReplaceUsesWith(IR::Value{result}); | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | if (is_lhs_immediate && !is_rhs_immediate) { | ||
| 56 | IR::Inst* const rhs_inst{rhs.InstRecursive()}; | ||
| 57 | if (rhs_inst->Opcode() == inst.Opcode() && rhs_inst->Arg(1).IsImmediate()) { | ||
| 58 | const auto combined{imm_fn(arg(lhs), arg(rhs_inst->Arg(1)))}; | ||
| 59 | inst.SetArg(0, rhs_inst->Arg(0)); | ||
| 60 | inst.SetArg(1, IR::Value{combined}); | ||
| 61 | } else { | ||
| 62 | // Normalize | ||
| 63 | inst.SetArg(0, rhs); | ||
| 64 | inst.SetArg(1, lhs); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | if (!is_lhs_immediate && is_rhs_immediate) { | ||
| 68 | const IR::Inst* const lhs_inst{lhs.InstRecursive()}; | ||
| 69 | if (lhs_inst->Opcode() == inst.Opcode() && lhs_inst->Arg(1).IsImmediate()) { | ||
| 70 | const auto combined{imm_fn(arg(rhs), arg(lhs_inst->Arg(1)))}; | ||
| 71 | inst.SetArg(0, lhs_inst->Arg(0)); | ||
| 72 | inst.SetArg(1, IR::Value{combined}); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | return true; | ||
| 76 | } | ||
| 77 | |||
| 78 | void FoldGetRegister(IR::Inst& inst) { | ||
| 79 | if (inst.Arg(0).Reg() == IR::Reg::RZ) { | ||
| 80 | inst.ReplaceUsesWith(IR::Value{u32{0}}); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | void FoldGetPred(IR::Inst& inst) { | ||
| 85 | if (inst.Arg(0).Pred() == IR::Pred::PT) { | ||
| 86 | inst.ReplaceUsesWith(IR::Value{true}); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | template <typename T> | ||
| 91 | void FoldAdd(IR::Inst& inst) { | ||
| 92 | if (inst.HasAssociatedPseudoOperation()) { | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | if (!FoldCommutative(inst, [](T a, T b) { return a + b; })) { | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | const IR::Value rhs{inst.Arg(1)}; | ||
| 99 | if (rhs.IsImmediate() && Arg<T>(rhs) == 0) { | ||
| 100 | inst.ReplaceUsesWith(inst.Arg(0)); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | void FoldLogicalAnd(IR::Inst& inst) { | ||
| 105 | if (!FoldCommutative(inst, [](bool a, bool b) { return a && b; })) { | ||
| 106 | return; | ||
| 107 | } | ||
| 108 | const IR::Value rhs{inst.Arg(1)}; | ||
| 109 | if (rhs.IsImmediate()) { | ||
| 110 | if (rhs.U1()) { | ||
| 111 | inst.ReplaceUsesWith(inst.Arg(0)); | ||
| 112 | } else { | ||
| 113 | inst.ReplaceUsesWith(IR::Value{false}); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | void ConstantPropagation(IR::Inst& inst) { | ||
| 119 | switch (inst.Opcode()) { | ||
| 120 | case IR::Opcode::GetRegister: | ||
| 121 | return FoldGetRegister(inst); | ||
| 122 | case IR::Opcode::GetPred: | ||
| 123 | return FoldGetPred(inst); | ||
| 124 | case IR::Opcode::IAdd32: | ||
| 125 | return FoldAdd<u32>(inst); | ||
| 126 | case IR::Opcode::IAdd64: | ||
| 127 | return FoldAdd<u64>(inst); | ||
| 128 | case IR::Opcode::BitFieldUExtract: | ||
| 129 | if (inst.AreAllArgsImmediates() && !inst.HasAssociatedPseudoOperation()) { | ||
| 130 | inst.ReplaceUsesWith(IR::Value{ | ||
| 131 | BitFieldUExtract(inst.Arg(0).U32(), inst.Arg(1).U32(), inst.Arg(2).U32())}); | ||
| 132 | } | ||
| 133 | break; | ||
| 134 | case IR::Opcode::LogicalAnd: | ||
| 135 | return FoldLogicalAnd(inst); | ||
| 136 | default: | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } // Anonymous namespace | ||
| 141 | |||
| 142 | void ConstantPropagationPass(IR::Block& block) { | ||
| 143 | std::ranges::for_each(block, ConstantPropagation); | ||
| 144 | } | ||
| 145 | |||
| 146 | } // namespace Shader::Optimization | ||