summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-02-05 05:58:02 -0300
committerGravatar ameerj2021-07-22 21:51:21 -0400
commite81739493a0cacc1efe3295f9d287d5d31b1a989 (patch)
tree11a3d04ce9def535414a00226030798f337c053c /src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
parentshader: Initial instruction support (diff)
downloadyuzu-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.cpp146
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
13namespace Shader::Optimization {
14namespace {
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
22template <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
33template <typename ImmFn>
34bool 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
78void FoldGetRegister(IR::Inst& inst) {
79 if (inst.Arg(0).Reg() == IR::Reg::RZ) {
80 inst.ReplaceUsesWith(IR::Value{u32{0}});
81 }
82}
83
84void FoldGetPred(IR::Inst& inst) {
85 if (inst.Arg(0).Pred() == IR::Pred::PT) {
86 inst.ReplaceUsesWith(IR::Value{true});
87 }
88}
89
90template <typename T>
91void 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
104void 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
118void 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
142void ConstantPropagationPass(IR::Block& block) {
143 std::ranges::for_each(block, ConstantPropagation);
144}
145
146} // namespace Shader::Optimization