summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/ir_opt/constant_propagation_pass.cpp')
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp610
1 files changed, 610 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..8dd6d6c2c
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -0,0 +1,610 @@
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 <tuple>
7#include <type_traits>
8
9#include "common/bit_cast.h"
10#include "common/bit_util.h"
11#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h"
13#include "shader_recompiler/frontend/ir/value.h"
14#include "shader_recompiler/ir_opt/passes.h"
15
16namespace Shader::Optimization {
17namespace {
18// Metaprogramming stuff to get arguments information out of a lambda
19template <typename Func>
20struct LambdaTraits : LambdaTraits<decltype(&std::remove_reference_t<Func>::operator())> {};
21
22template <typename ReturnType, typename LambdaType, typename... Args>
23struct LambdaTraits<ReturnType (LambdaType::*)(Args...) const> {
24 template <size_t I>
25 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
26
27 static constexpr size_t NUM_ARGS{sizeof...(Args)};
28};
29
30template <typename T>
31[[nodiscard]] T Arg(const IR::Value& value) {
32 if constexpr (std::is_same_v<T, bool>) {
33 return value.U1();
34 } else if constexpr (std::is_same_v<T, u32>) {
35 return value.U32();
36 } else if constexpr (std::is_same_v<T, s32>) {
37 return static_cast<s32>(value.U32());
38 } else if constexpr (std::is_same_v<T, f32>) {
39 return value.F32();
40 } else if constexpr (std::is_same_v<T, u64>) {
41 return value.U64();
42 }
43}
44
45template <typename T, typename ImmFn>
46bool FoldCommutative(IR::Inst& inst, ImmFn&& imm_fn) {
47 const IR::Value lhs{inst.Arg(0)};
48 const IR::Value rhs{inst.Arg(1)};
49
50 const bool is_lhs_immediate{lhs.IsImmediate()};
51 const bool is_rhs_immediate{rhs.IsImmediate()};
52
53 if (is_lhs_immediate && is_rhs_immediate) {
54 const auto result{imm_fn(Arg<T>(lhs), Arg<T>(rhs))};
55 inst.ReplaceUsesWith(IR::Value{result});
56 return false;
57 }
58 if (is_lhs_immediate && !is_rhs_immediate) {
59 IR::Inst* const rhs_inst{rhs.InstRecursive()};
60 if (rhs_inst->GetOpcode() == inst.GetOpcode() && rhs_inst->Arg(1).IsImmediate()) {
61 const auto combined{imm_fn(Arg<T>(lhs), Arg<T>(rhs_inst->Arg(1)))};
62 inst.SetArg(0, rhs_inst->Arg(0));
63 inst.SetArg(1, IR::Value{combined});
64 } else {
65 // Normalize
66 inst.SetArg(0, rhs);
67 inst.SetArg(1, lhs);
68 }
69 }
70 if (!is_lhs_immediate && is_rhs_immediate) {
71 const IR::Inst* const lhs_inst{lhs.InstRecursive()};
72 if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->Arg(1).IsImmediate()) {
73 const auto combined{imm_fn(Arg<T>(rhs), Arg<T>(lhs_inst->Arg(1)))};
74 inst.SetArg(0, lhs_inst->Arg(0));
75 inst.SetArg(1, IR::Value{combined});
76 }
77 }
78 return true;
79}
80
81template <typename Func>
82bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) {
83 if (!inst.AreAllArgsImmediates() || inst.HasAssociatedPseudoOperation()) {
84 return false;
85 }
86 using Indices = std::make_index_sequence<LambdaTraits<decltype(func)>::NUM_ARGS>;
87 inst.ReplaceUsesWith(EvalImmediates(inst, func, Indices{}));
88 return true;
89}
90
91void FoldGetRegister(IR::Inst& inst) {
92 if (inst.Arg(0).Reg() == IR::Reg::RZ) {
93 inst.ReplaceUsesWith(IR::Value{u32{0}});
94 }
95}
96
97void FoldGetPred(IR::Inst& inst) {
98 if (inst.Arg(0).Pred() == IR::Pred::PT) {
99 inst.ReplaceUsesWith(IR::Value{true});
100 }
101}
102
103/// Replaces the pattern generated by two XMAD multiplications
104bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
105 /*
106 * We are looking for this pattern:
107 * %rhs_bfe = BitFieldUExtract %factor_a, #0, #16
108 * %rhs_mul = IMul32 %rhs_bfe, %factor_b
109 * %lhs_bfe = BitFieldUExtract %factor_a, #16, #16
110 * %rhs_mul = IMul32 %lhs_bfe, %factor_b
111 * %lhs_shl = ShiftLeftLogical32 %rhs_mul, #16
112 * %result = IAdd32 %lhs_shl, %rhs_mul
113 *
114 * And replacing it with
115 * %result = IMul32 %factor_a, %factor_b
116 *
117 * This optimization has been proven safe by LLVM and MSVC.
118 */
119 const IR::Value lhs_arg{inst.Arg(0)};
120 const IR::Value rhs_arg{inst.Arg(1)};
121 if (lhs_arg.IsImmediate() || rhs_arg.IsImmediate()) {
122 return false;
123 }
124 IR::Inst* const lhs_shl{lhs_arg.InstRecursive()};
125 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
126 lhs_shl->Arg(1) != IR::Value{16U}) {
127 return false;
128 }
129 if (lhs_shl->Arg(0).IsImmediate()) {
130 return false;
131 }
132 IR::Inst* const lhs_mul{lhs_shl->Arg(0).InstRecursive()};
133 IR::Inst* const rhs_mul{rhs_arg.InstRecursive()};
134 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) {
135 return false;
136 }
137 if (lhs_mul->Arg(1).Resolve() != rhs_mul->Arg(1).Resolve()) {
138 return false;
139 }
140 const IR::U32 factor_b{lhs_mul->Arg(1)};
141 if (lhs_mul->Arg(0).IsImmediate() || rhs_mul->Arg(0).IsImmediate()) {
142 return false;
143 }
144 IR::Inst* const lhs_bfe{lhs_mul->Arg(0).InstRecursive()};
145 IR::Inst* const rhs_bfe{rhs_mul->Arg(0).InstRecursive()};
146 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
147 return false;
148 }
149 if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
150 return false;
151 }
152 if (lhs_bfe->Arg(1) != IR::Value{16U} || lhs_bfe->Arg(2) != IR::Value{16U}) {
153 return false;
154 }
155 if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) {
156 return false;
157 }
158 if (lhs_bfe->Arg(0).Resolve() != rhs_bfe->Arg(0).Resolve()) {
159 return false;
160 }
161 const IR::U32 factor_a{lhs_bfe->Arg(0)};
162 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
163 inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b));
164 return true;
165}
166
167template <typename T>
168void FoldAdd(IR::Block& block, IR::Inst& inst) {
169 if (inst.HasAssociatedPseudoOperation()) {
170 return;
171 }
172 if (!FoldCommutative<T>(inst, [](T a, T b) { return a + b; })) {
173 return;
174 }
175 const IR::Value rhs{inst.Arg(1)};
176 if (rhs.IsImmediate() && Arg<T>(rhs) == 0) {
177 inst.ReplaceUsesWith(inst.Arg(0));
178 return;
179 }
180 if constexpr (std::is_same_v<T, u32>) {
181 if (FoldXmadMultiply(block, inst)) {
182 return;
183 }
184 }
185}
186
187void FoldISub32(IR::Inst& inst) {
188 if (FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a - b; })) {
189 return;
190 }
191 if (inst.Arg(0).IsImmediate() || inst.Arg(1).IsImmediate()) {
192 return;
193 }
194 // ISub32 is generally used to subtract two constant buffers, compare and replace this with
195 // zero if they equal.
196 const auto equal_cbuf{[](IR::Inst* a, IR::Inst* b) {
197 return a->GetOpcode() == IR::Opcode::GetCbufU32 &&
198 b->GetOpcode() == IR::Opcode::GetCbufU32 && a->Arg(0) == b->Arg(0) &&
199 a->Arg(1) == b->Arg(1);
200 }};
201 IR::Inst* op_a{inst.Arg(0).InstRecursive()};
202 IR::Inst* op_b{inst.Arg(1).InstRecursive()};
203 if (equal_cbuf(op_a, op_b)) {
204 inst.ReplaceUsesWith(IR::Value{u32{0}});
205 return;
206 }
207 // It's also possible a value is being added to a cbuf and then subtracted
208 if (op_b->GetOpcode() == IR::Opcode::IAdd32) {
209 // Canonicalize local variables to simplify the following logic
210 std::swap(op_a, op_b);
211 }
212 if (op_b->GetOpcode() != IR::Opcode::GetCbufU32) {
213 return;
214 }
215 IR::Inst* const inst_cbuf{op_b};
216 if (op_a->GetOpcode() != IR::Opcode::IAdd32) {
217 return;
218 }
219 IR::Value add_op_a{op_a->Arg(0)};
220 IR::Value add_op_b{op_a->Arg(1)};
221 if (add_op_b.IsImmediate()) {
222 // Canonicalize
223 std::swap(add_op_a, add_op_b);
224 }
225 if (add_op_b.IsImmediate()) {
226 return;
227 }
228 IR::Inst* const add_cbuf{add_op_b.InstRecursive()};
229 if (equal_cbuf(add_cbuf, inst_cbuf)) {
230 inst.ReplaceUsesWith(add_op_a);
231 }
232}
233
234void FoldSelect(IR::Inst& inst) {
235 const IR::Value cond{inst.Arg(0)};
236 if (cond.IsImmediate()) {
237 inst.ReplaceUsesWith(cond.U1() ? inst.Arg(1) : inst.Arg(2));
238 }
239}
240
241void FoldFPMul32(IR::Inst& inst) {
242 const auto control{inst.Flags<IR::FpControl>()};
243 if (control.no_contraction) {
244 return;
245 }
246 // Fold interpolation operations
247 const IR::Value lhs_value{inst.Arg(0)};
248 const IR::Value rhs_value{inst.Arg(1)};
249 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
250 return;
251 }
252 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
253 IR::Inst* const rhs_op{rhs_value.InstRecursive()};
254 if (lhs_op->GetOpcode() != IR::Opcode::FPMul32 ||
255 rhs_op->GetOpcode() != IR::Opcode::FPRecip32) {
256 return;
257 }
258 const IR::Value recip_source{rhs_op->Arg(0)};
259 const IR::Value lhs_mul_source{lhs_op->Arg(1).Resolve()};
260 if (recip_source.IsImmediate() || lhs_mul_source.IsImmediate()) {
261 return;
262 }
263 IR::Inst* const attr_a{recip_source.InstRecursive()};
264 IR::Inst* const attr_b{lhs_mul_source.InstRecursive()};
265 if (attr_a->GetOpcode() != IR::Opcode::GetAttribute ||
266 attr_b->GetOpcode() != IR::Opcode::GetAttribute) {
267 return;
268 }
269 if (attr_a->Arg(0).Attribute() == attr_b->Arg(0).Attribute()) {
270 inst.ReplaceUsesWith(lhs_op->Arg(0));
271 }
272}
273
274void FoldLogicalAnd(IR::Inst& inst) {
275 if (!FoldCommutative<bool>(inst, [](bool a, bool b) { return a && b; })) {
276 return;
277 }
278 const IR::Value rhs{inst.Arg(1)};
279 if (rhs.IsImmediate()) {
280 if (rhs.U1()) {
281 inst.ReplaceUsesWith(inst.Arg(0));
282 } else {
283 inst.ReplaceUsesWith(IR::Value{false});
284 }
285 }
286}
287
288void FoldLogicalOr(IR::Inst& inst) {
289 if (!FoldCommutative<bool>(inst, [](bool a, bool b) { return a || b; })) {
290 return;
291 }
292 const IR::Value rhs{inst.Arg(1)};
293 if (rhs.IsImmediate()) {
294 if (rhs.U1()) {
295 inst.ReplaceUsesWith(IR::Value{true});
296 } else {
297 inst.ReplaceUsesWith(inst.Arg(0));
298 }
299 }
300}
301
302void FoldLogicalNot(IR::Inst& inst) {
303 const IR::U1 value{inst.Arg(0)};
304 if (value.IsImmediate()) {
305 inst.ReplaceUsesWith(IR::Value{!value.U1()});
306 return;
307 }
308 IR::Inst* const arg{value.InstRecursive()};
309 if (arg->GetOpcode() == IR::Opcode::LogicalNot) {
310 inst.ReplaceUsesWith(arg->Arg(0));
311 }
312}
313
314template <IR::Opcode op, typename Dest, typename Source>
315void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
316 const IR::Value value{inst.Arg(0)};
317 if (value.IsImmediate()) {
318 inst.ReplaceUsesWith(IR::Value{Common::BitCast<Dest>(Arg<Source>(value))});
319 return;
320 }
321 IR::Inst* const arg_inst{value.InstRecursive()};
322 if (arg_inst->GetOpcode() == reverse) {
323 inst.ReplaceUsesWith(arg_inst->Arg(0));
324 return;
325 }
326 if constexpr (op == IR::Opcode::BitCastF32U32) {
327 if (arg_inst->GetOpcode() == IR::Opcode::GetCbufU32) {
328 // Replace the bitcast with a typed constant buffer read
329 inst.ReplaceOpcode(IR::Opcode::GetCbufF32);
330 inst.SetArg(0, arg_inst->Arg(0));
331 inst.SetArg(1, arg_inst->Arg(1));
332 return;
333 }
334 }
335}
336
337void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
338 const IR::Value value{inst.Arg(0)};
339 if (value.IsImmediate()) {
340 return;
341 }
342 IR::Inst* const arg_inst{value.InstRecursive()};
343 if (arg_inst->GetOpcode() == reverse) {
344 inst.ReplaceUsesWith(arg_inst->Arg(0));
345 return;
346 }
347}
348
349template <typename Func, size_t... I>
350IR::Value EvalImmediates(const IR::Inst& inst, Func&& func, std::index_sequence<I...>) {
351 using Traits = LambdaTraits<decltype(func)>;
352 return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)};
353}
354
355std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert,
356 IR::Opcode construct, u32 first_index) {
357 IR::Inst* const inst{inst_value.InstRecursive()};
358 if (inst->GetOpcode() == construct) {
359 return inst->Arg(first_index);
360 }
361 if (inst->GetOpcode() != insert) {
362 return std::nullopt;
363 }
364 IR::Value value_index{inst->Arg(2)};
365 if (!value_index.IsImmediate()) {
366 return std::nullopt;
367 }
368 const u32 second_index{value_index.U32()};
369 if (first_index != second_index) {
370 IR::Value value_composite{inst->Arg(0)};
371 if (value_composite.IsImmediate()) {
372 return std::nullopt;
373 }
374 return FoldCompositeExtractImpl(value_composite, insert, construct, first_index);
375 }
376 return inst->Arg(1);
377}
378
379void FoldCompositeExtract(IR::Inst& inst, IR::Opcode construct, IR::Opcode insert) {
380 const IR::Value value_1{inst.Arg(0)};
381 const IR::Value value_2{inst.Arg(1)};
382 if (value_1.IsImmediate()) {
383 return;
384 }
385 if (!value_2.IsImmediate()) {
386 return;
387 }
388 const u32 first_index{value_2.U32()};
389 const std::optional result{FoldCompositeExtractImpl(value_1, insert, construct, first_index)};
390 if (!result) {
391 return;
392 }
393 inst.ReplaceUsesWith(*result);
394}
395
396IR::Value GetThroughCast(IR::Value value, IR::Opcode expected_cast) {
397 if (value.IsImmediate()) {
398 return value;
399 }
400 IR::Inst* const inst{value.InstRecursive()};
401 if (inst->GetOpcode() == expected_cast) {
402 return inst->Arg(0).Resolve();
403 }
404 return value;
405}
406
407void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
408 const IR::Value swizzle{inst.Arg(2)};
409 if (!swizzle.IsImmediate()) {
410 return;
411 }
412 const IR::Value value_1{GetThroughCast(inst.Arg(0).Resolve(), IR::Opcode::BitCastF32U32)};
413 const IR::Value value_2{GetThroughCast(inst.Arg(1).Resolve(), IR::Opcode::BitCastF32U32)};
414 if (value_1.IsImmediate()) {
415 return;
416 }
417 const u32 swizzle_value{swizzle.U32()};
418 if (swizzle_value != 0x99 && swizzle_value != 0xA5) {
419 return;
420 }
421 IR::Inst* const inst2{value_1.InstRecursive()};
422 if (inst2->GetOpcode() != IR::Opcode::ShuffleButterfly) {
423 return;
424 }
425 const IR::Value value_3{GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32)};
426 if (value_2 != value_3) {
427 return;
428 }
429 const IR::Value index{inst2->Arg(1)};
430 const IR::Value clamp{inst2->Arg(2)};
431 const IR::Value segmentation_mask{inst2->Arg(3)};
432 if (!index.IsImmediate() || !clamp.IsImmediate() || !segmentation_mask.IsImmediate()) {
433 return;
434 }
435 if (clamp.U32() != 3 || segmentation_mask.U32() != 28) {
436 return;
437 }
438 if (swizzle_value == 0x99) {
439 // DPdxFine
440 if (index.U32() == 1) {
441 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
442 inst.ReplaceUsesWith(ir.DPdxFine(IR::F32{inst.Arg(1)}));
443 }
444 } else if (swizzle_value == 0xA5) {
445 // DPdyFine
446 if (index.U32() == 2) {
447 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
448 inst.ReplaceUsesWith(ir.DPdyFine(IR::F32{inst.Arg(1)}));
449 }
450 }
451}
452
453void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
454 switch (inst.GetOpcode()) {
455 case IR::Opcode::GetRegister:
456 return FoldGetRegister(inst);
457 case IR::Opcode::GetPred:
458 return FoldGetPred(inst);
459 case IR::Opcode::IAdd32:
460 return FoldAdd<u32>(block, inst);
461 case IR::Opcode::ISub32:
462 return FoldISub32(inst);
463 case IR::Opcode::IMul32:
464 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a * b; });
465 return;
466 case IR::Opcode::ShiftRightArithmetic32:
467 FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return static_cast<u32>(a >> b); });
468 return;
469 case IR::Opcode::BitCastF32U32:
470 return FoldBitCast<IR::Opcode::BitCastF32U32, f32, u32>(inst, IR::Opcode::BitCastU32F32);
471 case IR::Opcode::BitCastU32F32:
472 return FoldBitCast<IR::Opcode::BitCastU32F32, u32, f32>(inst, IR::Opcode::BitCastF32U32);
473 case IR::Opcode::IAdd64:
474 return FoldAdd<u64>(block, inst);
475 case IR::Opcode::PackHalf2x16:
476 return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
477 case IR::Opcode::UnpackHalf2x16:
478 return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
479 case IR::Opcode::SelectU1:
480 case IR::Opcode::SelectU8:
481 case IR::Opcode::SelectU16:
482 case IR::Opcode::SelectU32:
483 case IR::Opcode::SelectU64:
484 case IR::Opcode::SelectF16:
485 case IR::Opcode::SelectF32:
486 case IR::Opcode::SelectF64:
487 return FoldSelect(inst);
488 case IR::Opcode::FPMul32:
489 return FoldFPMul32(inst);
490 case IR::Opcode::LogicalAnd:
491 return FoldLogicalAnd(inst);
492 case IR::Opcode::LogicalOr:
493 return FoldLogicalOr(inst);
494 case IR::Opcode::LogicalNot:
495 return FoldLogicalNot(inst);
496 case IR::Opcode::SLessThan:
497 FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a < b; });
498 return;
499 case IR::Opcode::ULessThan:
500 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a < b; });
501 return;
502 case IR::Opcode::SLessThanEqual:
503 FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a <= b; });
504 return;
505 case IR::Opcode::ULessThanEqual:
506 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a <= b; });
507 return;
508 case IR::Opcode::SGreaterThan:
509 FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a > b; });
510 return;
511 case IR::Opcode::UGreaterThan:
512 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a > b; });
513 return;
514 case IR::Opcode::SGreaterThanEqual:
515 FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a >= b; });
516 return;
517 case IR::Opcode::UGreaterThanEqual:
518 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a >= b; });
519 return;
520 case IR::Opcode::IEqual:
521 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a == b; });
522 return;
523 case IR::Opcode::INotEqual:
524 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a != b; });
525 return;
526 case IR::Opcode::BitwiseAnd32:
527 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a & b; });
528 return;
529 case IR::Opcode::BitwiseOr32:
530 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a | b; });
531 return;
532 case IR::Opcode::BitwiseXor32:
533 FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a ^ b; });
534 return;
535 case IR::Opcode::BitFieldUExtract:
536 FoldWhenAllImmediates(inst, [](u32 base, u32 shift, u32 count) {
537 if (static_cast<size_t>(shift) + static_cast<size_t>(count) > 32) {
538 throw LogicError("Undefined result in {}({}, {}, {})", IR::Opcode::BitFieldUExtract,
539 base, shift, count);
540 }
541 return (base >> shift) & ((1U << count) - 1);
542 });
543 return;
544 case IR::Opcode::BitFieldSExtract:
545 FoldWhenAllImmediates(inst, [](s32 base, u32 shift, u32 count) {
546 const size_t back_shift{static_cast<size_t>(shift) + static_cast<size_t>(count)};
547 const size_t left_shift{32 - back_shift};
548 const size_t right_shift{static_cast<size_t>(32 - count)};
549 if (back_shift > 32 || left_shift >= 32 || right_shift >= 32) {
550 throw LogicError("Undefined result in {}({}, {}, {})", IR::Opcode::BitFieldSExtract,
551 base, shift, count);
552 }
553 return static_cast<u32>((base << left_shift) >> right_shift);
554 });
555 return;
556 case IR::Opcode::BitFieldInsert:
557 FoldWhenAllImmediates(inst, [](u32 base, u32 insert, u32 offset, u32 bits) {
558 if (bits >= 32 || offset >= 32) {
559 throw LogicError("Undefined result in {}({}, {}, {}, {})",
560 IR::Opcode::BitFieldInsert, base, insert, offset, bits);
561 }
562 return (base & ~(~(~0u << bits) << offset)) | (insert << offset);
563 });
564 return;
565 case IR::Opcode::CompositeExtractU32x2:
566 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x2,
567 IR::Opcode::CompositeInsertU32x2);
568 case IR::Opcode::CompositeExtractU32x3:
569 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x3,
570 IR::Opcode::CompositeInsertU32x3);
571 case IR::Opcode::CompositeExtractU32x4:
572 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x4,
573 IR::Opcode::CompositeInsertU32x4);
574 case IR::Opcode::CompositeExtractF32x2:
575 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2,
576 IR::Opcode::CompositeInsertF32x2);
577 case IR::Opcode::CompositeExtractF32x3:
578 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x3,
579 IR::Opcode::CompositeInsertF32x3);
580 case IR::Opcode::CompositeExtractF32x4:
581 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x4,
582 IR::Opcode::CompositeInsertF32x4);
583 case IR::Opcode::CompositeExtractF16x2:
584 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x2,
585 IR::Opcode::CompositeInsertF16x2);
586 case IR::Opcode::CompositeExtractF16x3:
587 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x3,
588 IR::Opcode::CompositeInsertF16x3);
589 case IR::Opcode::CompositeExtractF16x4:
590 return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x4,
591 IR::Opcode::CompositeInsertF16x4);
592 case IR::Opcode::FSwizzleAdd:
593 return FoldFSwizzleAdd(block, inst);
594 default:
595 break;
596 }
597}
598} // Anonymous namespace
599
600void ConstantPropagationPass(IR::Program& program) {
601 const auto end{program.post_order_blocks.rend()};
602 for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
603 IR::Block* const block{*it};
604 for (IR::Inst& inst : block->Instructions()) {
605 ConstantPropagation(*block, inst);
606 }
607 }
608}
609
610} // namespace Shader::Optimization