constant_propagation_pass.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <functional>
  5. #include <tuple>
  6. #include <type_traits>
  7. #include "common/bit_cast.h"
  8. #include "shader_recompiler/environment.h"
  9. #include "shader_recompiler/exception.h"
  10. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  11. #include "shader_recompiler/frontend/ir/value.h"
  12. #include "shader_recompiler/ir_opt/passes.h"
  13. namespace Shader::Optimization {
  14. namespace {
  15. // Metaprogramming stuff to get arguments information out of a lambda
  16. template <typename Func>
  17. struct LambdaTraits : LambdaTraits<decltype(&std::remove_reference_t<Func>::operator())> {};
  18. template <typename ReturnType, typename LambdaType, typename... Args>
  19. struct LambdaTraits<ReturnType (LambdaType::*)(Args...) const> {
  20. template <size_t I>
  21. using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
  22. static constexpr size_t NUM_ARGS{sizeof...(Args)};
  23. };
  24. template <typename T>
  25. [[nodiscard]] T Arg(const IR::Value& value) {
  26. if constexpr (std::is_same_v<T, bool>) {
  27. return value.U1();
  28. } else if constexpr (std::is_same_v<T, u32>) {
  29. return value.U32();
  30. } else if constexpr (std::is_same_v<T, s32>) {
  31. return static_cast<s32>(value.U32());
  32. } else if constexpr (std::is_same_v<T, f32>) {
  33. return value.F32();
  34. } else if constexpr (std::is_same_v<T, u64>) {
  35. return value.U64();
  36. }
  37. }
  38. template <typename T, typename ImmFn>
  39. bool FoldCommutative(IR::Inst& inst, ImmFn&& imm_fn) {
  40. const IR::Value lhs{inst.Arg(0)};
  41. const IR::Value rhs{inst.Arg(1)};
  42. const bool is_lhs_immediate{lhs.IsImmediate()};
  43. const bool is_rhs_immediate{rhs.IsImmediate()};
  44. if (is_lhs_immediate && is_rhs_immediate) {
  45. const auto result{imm_fn(Arg<T>(lhs), Arg<T>(rhs))};
  46. inst.ReplaceUsesWith(IR::Value{result});
  47. return false;
  48. }
  49. if (is_lhs_immediate && !is_rhs_immediate) {
  50. IR::Inst* const rhs_inst{rhs.InstRecursive()};
  51. if (rhs_inst->GetOpcode() == inst.GetOpcode() && rhs_inst->Arg(1).IsImmediate()) {
  52. const auto combined{imm_fn(Arg<T>(lhs), Arg<T>(rhs_inst->Arg(1)))};
  53. inst.SetArg(0, rhs_inst->Arg(0));
  54. inst.SetArg(1, IR::Value{combined});
  55. } else {
  56. // Normalize
  57. inst.SetArg(0, rhs);
  58. inst.SetArg(1, lhs);
  59. }
  60. }
  61. if (!is_lhs_immediate && is_rhs_immediate) {
  62. const IR::Inst* const lhs_inst{lhs.InstRecursive()};
  63. if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->Arg(1).IsImmediate()) {
  64. const auto combined{imm_fn(Arg<T>(rhs), Arg<T>(lhs_inst->Arg(1)))};
  65. inst.SetArg(0, lhs_inst->Arg(0));
  66. inst.SetArg(1, IR::Value{combined});
  67. }
  68. }
  69. return true;
  70. }
  71. template <typename Func>
  72. bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) {
  73. if (!inst.AreAllArgsImmediates() || inst.HasAssociatedPseudoOperation()) {
  74. return false;
  75. }
  76. using Indices = std::make_index_sequence<LambdaTraits<decltype(func)>::NUM_ARGS>;
  77. inst.ReplaceUsesWith(EvalImmediates(inst, func, Indices{}));
  78. return true;
  79. }
  80. /// Return true when all values in a range are equal
  81. template <typename Range>
  82. bool AreEqual(const Range& range) {
  83. auto resolver{[](const auto& value) { return value.Resolve(); }};
  84. auto equal{[](const IR::Value& lhs, const IR::Value& rhs) {
  85. if (lhs == rhs) {
  86. return true;
  87. }
  88. // Not equal, but try to match if they read the same constant buffer
  89. if (!lhs.IsImmediate() && !rhs.IsImmediate() &&
  90. lhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
  91. rhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
  92. lhs.Inst()->Arg(0) == rhs.Inst()->Arg(0) && lhs.Inst()->Arg(1) == rhs.Inst()->Arg(1)) {
  93. return true;
  94. }
  95. return false;
  96. }};
  97. return std::ranges::adjacent_find(range, std::not_fn(equal), resolver) == std::end(range);
  98. }
  99. void FoldGetRegister(IR::Inst& inst) {
  100. if (inst.Arg(0).Reg() == IR::Reg::RZ) {
  101. inst.ReplaceUsesWith(IR::Value{u32{0}});
  102. }
  103. }
  104. void FoldGetPred(IR::Inst& inst) {
  105. if (inst.Arg(0).Pred() == IR::Pred::PT) {
  106. inst.ReplaceUsesWith(IR::Value{true});
  107. }
  108. }
  109. /// Replaces the XMAD pattern generated by an integer FMA
  110. bool FoldXmadMultiplyAdd(IR::Block& block, IR::Inst& inst) {
  111. /*
  112. * We are looking for this specific pattern:
  113. * %6 = BitFieldUExtract %op_b, #0, #16
  114. * %7 = BitFieldUExtract %op_a', #16, #16
  115. * %8 = IMul32 %6, %7
  116. * %10 = BitFieldUExtract %op_a', #0, #16
  117. * %11 = BitFieldInsert %8, %10, #16, #16
  118. * %15 = BitFieldUExtract %op_b, #0, #16
  119. * %16 = BitFieldUExtract %op_a, #0, #16
  120. * %17 = IMul32 %15, %16
  121. * %18 = IAdd32 %17, %op_c
  122. * %22 = BitFieldUExtract %op_b, #16, #16
  123. * %23 = BitFieldUExtract %11, #16, #16
  124. * %24 = IMul32 %22, %23
  125. * %25 = ShiftLeftLogical32 %24, #16
  126. * %26 = ShiftLeftLogical32 %11, #16
  127. * %27 = IAdd32 %26, %18
  128. * %result = IAdd32 %25, %27
  129. *
  130. * And replace it with:
  131. * %temp = IMul32 %op_a, %op_b
  132. * %result = IAdd32 %temp, %op_c
  133. *
  134. * This optimization has been proven safe by Nvidia's compiler logic being reversed.
  135. * (If Nvidia generates this code from 'fma(a, b, c)', we can do the same in the reverse order.)
  136. */
  137. const IR::Value zero{0u};
  138. const IR::Value sixteen{16u};
  139. IR::Inst* const _25{inst.Arg(0).TryInstRecursive()};
  140. IR::Inst* const _27{inst.Arg(1).TryInstRecursive()};
  141. if (!_25 || !_27) {
  142. return false;
  143. }
  144. if (_27->GetOpcode() != IR::Opcode::IAdd32) {
  145. return false;
  146. }
  147. if (_25->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _25->Arg(1) != sixteen) {
  148. return false;
  149. }
  150. IR::Inst* const _24{_25->Arg(0).TryInstRecursive()};
  151. if (!_24 || _24->GetOpcode() != IR::Opcode::IMul32) {
  152. return false;
  153. }
  154. IR::Inst* const _22{_24->Arg(0).TryInstRecursive()};
  155. IR::Inst* const _23{_24->Arg(1).TryInstRecursive()};
  156. if (!_22 || !_23) {
  157. return false;
  158. }
  159. if (_22->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  160. return false;
  161. }
  162. if (_23->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  163. return false;
  164. }
  165. if (_22->Arg(1) != sixteen || _22->Arg(2) != sixteen) {
  166. return false;
  167. }
  168. if (_23->Arg(1) != sixteen || _23->Arg(2) != sixteen) {
  169. return false;
  170. }
  171. IR::Inst* const _11{_23->Arg(0).TryInstRecursive()};
  172. if (!_11 || _11->GetOpcode() != IR::Opcode::BitFieldInsert) {
  173. return false;
  174. }
  175. if (_11->Arg(2) != sixteen || _11->Arg(3) != sixteen) {
  176. return false;
  177. }
  178. IR::Inst* const _8{_11->Arg(0).TryInstRecursive()};
  179. IR::Inst* const _10{_11->Arg(1).TryInstRecursive()};
  180. if (!_8 || !_10) {
  181. return false;
  182. }
  183. if (_8->GetOpcode() != IR::Opcode::IMul32) {
  184. return false;
  185. }
  186. if (_10->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  187. return false;
  188. }
  189. IR::Inst* const _6{_8->Arg(0).TryInstRecursive()};
  190. IR::Inst* const _7{_8->Arg(1).TryInstRecursive()};
  191. if (!_6 || !_7) {
  192. return false;
  193. }
  194. if (_6->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  195. return false;
  196. }
  197. if (_7->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  198. return false;
  199. }
  200. if (_6->Arg(1) != zero || _6->Arg(2) != sixteen) {
  201. return false;
  202. }
  203. if (_7->Arg(1) != sixteen || _7->Arg(2) != sixteen) {
  204. return false;
  205. }
  206. IR::Inst* const _26{_27->Arg(0).TryInstRecursive()};
  207. IR::Inst* const _18{_27->Arg(1).TryInstRecursive()};
  208. if (!_26 || !_18) {
  209. return false;
  210. }
  211. if (_26->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _26->Arg(1) != sixteen) {
  212. return false;
  213. }
  214. if (_26->Arg(0).InstRecursive() != _11) {
  215. return false;
  216. }
  217. if (_18->GetOpcode() != IR::Opcode::IAdd32) {
  218. return false;
  219. }
  220. IR::Inst* const _17{_18->Arg(0).TryInstRecursive()};
  221. if (!_17 || _17->GetOpcode() != IR::Opcode::IMul32) {
  222. return false;
  223. }
  224. IR::Inst* const _15{_17->Arg(0).TryInstRecursive()};
  225. IR::Inst* const _16{_17->Arg(1).TryInstRecursive()};
  226. if (!_15 || !_16) {
  227. return false;
  228. }
  229. if (_15->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  230. return false;
  231. }
  232. if (_16->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  233. return false;
  234. }
  235. if (_15->Arg(1) != zero || _16->Arg(1) != zero || _10->Arg(1) != zero) {
  236. return false;
  237. }
  238. if (_15->Arg(2) != sixteen || _16->Arg(2) != sixteen || _10->Arg(2) != sixteen) {
  239. return false;
  240. }
  241. const std::array<IR::Value, 3> op_as{
  242. _7->Arg(0).Resolve(),
  243. _16->Arg(0).Resolve(),
  244. _10->Arg(0).Resolve(),
  245. };
  246. const std::array<IR::Value, 3> op_bs{
  247. _22->Arg(0).Resolve(),
  248. _6->Arg(0).Resolve(),
  249. _15->Arg(0).Resolve(),
  250. };
  251. const IR::U32 op_c{_18->Arg(1)};
  252. if (!AreEqual(op_as) || !AreEqual(op_bs)) {
  253. return false;
  254. }
  255. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  256. inst.ReplaceUsesWith(ir.IAdd(ir.IMul(IR::U32{op_as[0]}, IR::U32{op_bs[1]}), op_c));
  257. return true;
  258. }
  259. /// Replaces the pattern generated by two XMAD multiplications
  260. bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
  261. /*
  262. * We are looking for this pattern:
  263. * %rhs_bfe = BitFieldUExtract %factor_a, #0, #16
  264. * %rhs_mul = IMul32 %rhs_bfe, %factor_b
  265. * %lhs_bfe = BitFieldUExtract %factor_a, #16, #16
  266. * %rhs_mul = IMul32 %lhs_bfe, %factor_b
  267. * %lhs_shl = ShiftLeftLogical32 %rhs_mul, #16
  268. * %result = IAdd32 %lhs_shl, %rhs_mul
  269. *
  270. * And replacing it with
  271. * %result = IMul32 %factor_a, %factor_b
  272. *
  273. * This optimization has been proven safe by LLVM and MSVC.
  274. */
  275. IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
  276. IR::Inst* const rhs_mul{inst.Arg(1).TryInstRecursive()};
  277. if (!lhs_shl || !rhs_mul) {
  278. return false;
  279. }
  280. if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
  281. lhs_shl->Arg(1) != IR::Value{16U}) {
  282. return false;
  283. }
  284. IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
  285. if (!lhs_mul) {
  286. return false;
  287. }
  288. if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) {
  289. return false;
  290. }
  291. const IR::U32 factor_b{lhs_mul->Arg(1)};
  292. if (factor_b.Resolve() != rhs_mul->Arg(1).Resolve()) {
  293. return false;
  294. }
  295. IR::Inst* const lhs_bfe{lhs_mul->Arg(0).TryInstRecursive()};
  296. IR::Inst* const rhs_bfe{rhs_mul->Arg(0).TryInstRecursive()};
  297. if (!lhs_bfe || !rhs_bfe) {
  298. return false;
  299. }
  300. if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  301. return false;
  302. }
  303. if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
  304. return false;
  305. }
  306. if (lhs_bfe->Arg(1) != IR::Value{16U} || lhs_bfe->Arg(2) != IR::Value{16U}) {
  307. return false;
  308. }
  309. if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) {
  310. return false;
  311. }
  312. const IR::U32 factor_a{lhs_bfe->Arg(0)};
  313. if (factor_a.Resolve() != rhs_bfe->Arg(0).Resolve()) {
  314. return false;
  315. }
  316. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  317. inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b));
  318. return true;
  319. }
  320. template <typename T>
  321. void FoldAdd(IR::Block& block, IR::Inst& inst) {
  322. if (inst.HasAssociatedPseudoOperation()) {
  323. return;
  324. }
  325. if (!FoldCommutative<T>(inst, [](T a, T b) { return a + b; })) {
  326. return;
  327. }
  328. const IR::Value rhs{inst.Arg(1)};
  329. if (rhs.IsImmediate() && Arg<T>(rhs) == 0) {
  330. inst.ReplaceUsesWith(inst.Arg(0));
  331. return;
  332. }
  333. if constexpr (std::is_same_v<T, u32>) {
  334. if (FoldXmadMultiply(block, inst)) {
  335. return;
  336. }
  337. if (FoldXmadMultiplyAdd(block, inst)) {
  338. return;
  339. }
  340. }
  341. }
  342. void FoldISub32(IR::Inst& inst) {
  343. if (FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a - b; })) {
  344. return;
  345. }
  346. if (inst.Arg(0).IsImmediate() || inst.Arg(1).IsImmediate()) {
  347. return;
  348. }
  349. // ISub32 is generally used to subtract two constant buffers, compare and replace this with
  350. // zero if they equal.
  351. const auto equal_cbuf{[](IR::Inst* a, IR::Inst* b) {
  352. return a->GetOpcode() == IR::Opcode::GetCbufU32 &&
  353. b->GetOpcode() == IR::Opcode::GetCbufU32 && a->Arg(0) == b->Arg(0) &&
  354. a->Arg(1) == b->Arg(1);
  355. }};
  356. IR::Inst* op_a{inst.Arg(0).InstRecursive()};
  357. IR::Inst* op_b{inst.Arg(1).InstRecursive()};
  358. if (equal_cbuf(op_a, op_b)) {
  359. inst.ReplaceUsesWith(IR::Value{u32{0}});
  360. return;
  361. }
  362. // It's also possible a value is being added to a cbuf and then subtracted
  363. if (op_b->GetOpcode() == IR::Opcode::IAdd32) {
  364. // Canonicalize local variables to simplify the following logic
  365. std::swap(op_a, op_b);
  366. }
  367. if (op_b->GetOpcode() != IR::Opcode::GetCbufU32) {
  368. return;
  369. }
  370. IR::Inst* const inst_cbuf{op_b};
  371. if (op_a->GetOpcode() != IR::Opcode::IAdd32) {
  372. return;
  373. }
  374. IR::Value add_op_a{op_a->Arg(0)};
  375. IR::Value add_op_b{op_a->Arg(1)};
  376. if (add_op_b.IsImmediate()) {
  377. // Canonicalize
  378. std::swap(add_op_a, add_op_b);
  379. }
  380. if (add_op_b.IsImmediate()) {
  381. return;
  382. }
  383. IR::Inst* const add_cbuf{add_op_b.InstRecursive()};
  384. if (equal_cbuf(add_cbuf, inst_cbuf)) {
  385. inst.ReplaceUsesWith(add_op_a);
  386. }
  387. }
  388. void FoldSelect(IR::Inst& inst) {
  389. const IR::Value cond{inst.Arg(0)};
  390. if (cond.IsImmediate()) {
  391. inst.ReplaceUsesWith(cond.U1() ? inst.Arg(1) : inst.Arg(2));
  392. }
  393. }
  394. void FoldFPMul32(IR::Inst& inst) {
  395. const auto control{inst.Flags<IR::FpControl>()};
  396. if (control.no_contraction) {
  397. return;
  398. }
  399. // Fold interpolation operations
  400. const IR::Value lhs_value{inst.Arg(0)};
  401. const IR::Value rhs_value{inst.Arg(1)};
  402. if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
  403. return;
  404. }
  405. IR::Inst* const lhs_op{lhs_value.InstRecursive()};
  406. IR::Inst* const rhs_op{rhs_value.InstRecursive()};
  407. if (lhs_op->GetOpcode() != IR::Opcode::FPMul32 ||
  408. rhs_op->GetOpcode() != IR::Opcode::FPRecip32) {
  409. return;
  410. }
  411. const IR::Value recip_source{rhs_op->Arg(0)};
  412. const IR::Value lhs_mul_source{lhs_op->Arg(1).Resolve()};
  413. if (recip_source.IsImmediate() || lhs_mul_source.IsImmediate()) {
  414. return;
  415. }
  416. IR::Inst* const attr_a{recip_source.InstRecursive()};
  417. IR::Inst* const attr_b{lhs_mul_source.InstRecursive()};
  418. if (attr_a->GetOpcode() != IR::Opcode::GetAttribute ||
  419. attr_b->GetOpcode() != IR::Opcode::GetAttribute) {
  420. return;
  421. }
  422. if (attr_a->Arg(0).Attribute() == attr_b->Arg(0).Attribute()) {
  423. inst.ReplaceUsesWith(lhs_op->Arg(0));
  424. }
  425. }
  426. void FoldLogicalAnd(IR::Inst& inst) {
  427. if (!FoldCommutative<bool>(inst, [](bool a, bool b) { return a && b; })) {
  428. return;
  429. }
  430. const IR::Value rhs{inst.Arg(1)};
  431. if (rhs.IsImmediate()) {
  432. if (rhs.U1()) {
  433. inst.ReplaceUsesWith(inst.Arg(0));
  434. } else {
  435. inst.ReplaceUsesWith(IR::Value{false});
  436. }
  437. }
  438. }
  439. void FoldLogicalOr(IR::Inst& inst) {
  440. if (!FoldCommutative<bool>(inst, [](bool a, bool b) { return a || b; })) {
  441. return;
  442. }
  443. const IR::Value rhs{inst.Arg(1)};
  444. if (rhs.IsImmediate()) {
  445. if (rhs.U1()) {
  446. inst.ReplaceUsesWith(IR::Value{true});
  447. } else {
  448. inst.ReplaceUsesWith(inst.Arg(0));
  449. }
  450. }
  451. }
  452. void FoldLogicalNot(IR::Inst& inst) {
  453. const IR::U1 value{inst.Arg(0)};
  454. if (value.IsImmediate()) {
  455. inst.ReplaceUsesWith(IR::Value{!value.U1()});
  456. return;
  457. }
  458. IR::Inst* const arg{value.InstRecursive()};
  459. if (arg->GetOpcode() == IR::Opcode::LogicalNot) {
  460. inst.ReplaceUsesWith(arg->Arg(0));
  461. }
  462. }
  463. template <IR::Opcode op, typename Dest, typename Source>
  464. void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
  465. const IR::Value value{inst.Arg(0)};
  466. if (value.IsImmediate()) {
  467. inst.ReplaceUsesWith(IR::Value{Common::BitCast<Dest>(Arg<Source>(value))});
  468. return;
  469. }
  470. IR::Inst* const arg_inst{value.InstRecursive()};
  471. if (arg_inst->GetOpcode() == reverse) {
  472. inst.ReplaceUsesWith(arg_inst->Arg(0));
  473. return;
  474. }
  475. if constexpr (op == IR::Opcode::BitCastF32U32) {
  476. if (arg_inst->GetOpcode() == IR::Opcode::GetCbufU32) {
  477. // Replace the bitcast with a typed constant buffer read
  478. inst.ReplaceOpcode(IR::Opcode::GetCbufF32);
  479. inst.SetArg(0, arg_inst->Arg(0));
  480. inst.SetArg(1, arg_inst->Arg(1));
  481. return;
  482. }
  483. }
  484. if constexpr (op == IR::Opcode::BitCastU32F32) {
  485. // Workaround for new NVIDIA driver bug, where:
  486. // uint attr = ftou(itof(gl_InstanceID));
  487. // always returned 0.
  488. // We can instead manually optimize this and work around the driver bug:
  489. // uint attr = uint(gl_InstanceID);
  490. if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
  491. const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
  492. switch (attr) {
  493. case IR::Attribute::PrimitiveId:
  494. case IR::Attribute::InstanceId:
  495. case IR::Attribute::VertexId:
  496. case IR::Attribute::BaseVertex:
  497. case IR::Attribute::BaseInstance:
  498. case IR::Attribute::DrawID:
  499. break;
  500. default:
  501. return;
  502. }
  503. // Replace the bitcasts with an integer attribute get
  504. inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
  505. inst.SetArg(0, arg_inst->Arg(0));
  506. inst.SetArg(1, arg_inst->Arg(1));
  507. return;
  508. }
  509. }
  510. }
  511. void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
  512. const IR::Value value{inst.Arg(0)};
  513. if (value.IsImmediate()) {
  514. return;
  515. }
  516. IR::Inst* const arg_inst{value.InstRecursive()};
  517. if (arg_inst->GetOpcode() == reverse) {
  518. inst.ReplaceUsesWith(arg_inst->Arg(0));
  519. return;
  520. }
  521. }
  522. template <typename Func, size_t... I>
  523. IR::Value EvalImmediates(const IR::Inst& inst, Func&& func, std::index_sequence<I...>) {
  524. using Traits = LambdaTraits<decltype(func)>;
  525. return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)};
  526. }
  527. std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert,
  528. IR::Opcode construct, u32 first_index) {
  529. IR::Inst* const inst{inst_value.InstRecursive()};
  530. if (inst->GetOpcode() == construct) {
  531. return inst->Arg(first_index);
  532. }
  533. if (inst->GetOpcode() != insert) {
  534. return std::nullopt;
  535. }
  536. IR::Value value_index{inst->Arg(2)};
  537. if (!value_index.IsImmediate()) {
  538. return std::nullopt;
  539. }
  540. const u32 second_index{value_index.U32()};
  541. if (first_index != second_index) {
  542. IR::Value value_composite{inst->Arg(0)};
  543. if (value_composite.IsImmediate()) {
  544. return std::nullopt;
  545. }
  546. return FoldCompositeExtractImpl(value_composite, insert, construct, first_index);
  547. }
  548. return inst->Arg(1);
  549. }
  550. void FoldCompositeExtract(IR::Inst& inst, IR::Opcode construct, IR::Opcode insert) {
  551. const IR::Value value_1{inst.Arg(0)};
  552. const IR::Value value_2{inst.Arg(1)};
  553. if (value_1.IsImmediate()) {
  554. return;
  555. }
  556. if (!value_2.IsImmediate()) {
  557. return;
  558. }
  559. const u32 first_index{value_2.U32()};
  560. const std::optional result{FoldCompositeExtractImpl(value_1, insert, construct, first_index)};
  561. if (!result) {
  562. return;
  563. }
  564. inst.ReplaceUsesWith(*result);
  565. }
  566. IR::Value GetThroughCast(IR::Value value, IR::Opcode expected_cast) {
  567. if (value.IsImmediate()) {
  568. return value;
  569. }
  570. IR::Inst* const inst{value.InstRecursive()};
  571. if (inst->GetOpcode() == expected_cast) {
  572. return inst->Arg(0).Resolve();
  573. }
  574. return value;
  575. }
  576. void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
  577. const IR::Value swizzle{inst.Arg(2)};
  578. if (!swizzle.IsImmediate()) {
  579. return;
  580. }
  581. const IR::Value value_1{GetThroughCast(inst.Arg(0).Resolve(), IR::Opcode::BitCastF32U32)};
  582. const IR::Value value_2{GetThroughCast(inst.Arg(1).Resolve(), IR::Opcode::BitCastF32U32)};
  583. if (value_1.IsImmediate()) {
  584. return;
  585. }
  586. const u32 swizzle_value{swizzle.U32()};
  587. if (swizzle_value != 0x99 && swizzle_value != 0xA5) {
  588. return;
  589. }
  590. IR::Inst* const inst2{value_1.InstRecursive()};
  591. if (inst2->GetOpcode() != IR::Opcode::ShuffleButterfly) {
  592. return;
  593. }
  594. const IR::Value value_3{GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32)};
  595. if (value_2 != value_3) {
  596. return;
  597. }
  598. const IR::Value index{inst2->Arg(1)};
  599. const IR::Value clamp{inst2->Arg(2)};
  600. const IR::Value segmentation_mask{inst2->Arg(3)};
  601. if (!index.IsImmediate() || !clamp.IsImmediate() || !segmentation_mask.IsImmediate()) {
  602. return;
  603. }
  604. if (clamp.U32() != 3 || segmentation_mask.U32() != 28) {
  605. return;
  606. }
  607. if (swizzle_value == 0x99) {
  608. // DPdxFine
  609. if (index.U32() == 1) {
  610. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  611. inst.ReplaceUsesWith(ir.DPdxFine(IR::F32{inst.Arg(1)}));
  612. }
  613. } else if (swizzle_value == 0xA5) {
  614. // DPdyFine
  615. if (index.U32() == 2) {
  616. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  617. inst.ReplaceUsesWith(ir.DPdyFine(IR::F32{inst.Arg(1)}));
  618. }
  619. }
  620. }
  621. void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
  622. const IR::Value bank{inst.Arg(0)};
  623. const IR::Value offset{inst.Arg(1)};
  624. if (!bank.IsImmediate() || !offset.IsImmediate()) {
  625. return;
  626. }
  627. const auto bank_value = bank.U32();
  628. const auto offset_value = offset.U32();
  629. auto replacement = env.GetReplaceConstBuffer(bank_value, offset_value);
  630. if (!replacement) {
  631. return;
  632. }
  633. const auto new_attribute = [replacement]() {
  634. switch (*replacement) {
  635. case ReplaceConstant::BaseInstance:
  636. return IR::Attribute::BaseInstance;
  637. case ReplaceConstant::BaseVertex:
  638. return IR::Attribute::BaseVertex;
  639. case ReplaceConstant::DrawID:
  640. return IR::Attribute::DrawID;
  641. default:
  642. throw NotImplementedException("Not implemented replacement variable {}", *replacement);
  643. }
  644. }();
  645. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  646. if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
  647. inst.ReplaceUsesWith(ir.GetAttributeU32(new_attribute));
  648. } else {
  649. inst.ReplaceUsesWith(ir.GetAttribute(new_attribute));
  650. }
  651. }
  652. void FoldDriverConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst, u32 which_bank,
  653. u32 offset_start = 0, u32 offset_end = std::numeric_limits<u16>::max()) {
  654. const IR::Value bank{inst.Arg(0)};
  655. const IR::Value offset{inst.Arg(1)};
  656. if (!bank.IsImmediate() || !offset.IsImmediate()) {
  657. return;
  658. }
  659. const auto bank_value = bank.U32();
  660. if (bank_value != which_bank) {
  661. return;
  662. }
  663. const auto offset_value = offset.U32();
  664. if (offset_value < offset_start || offset_value >= offset_end) {
  665. return;
  666. }
  667. IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
  668. if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
  669. inst.ReplaceUsesWith(IR::Value{env.ReadCbufValue(bank_value, offset_value)});
  670. } else {
  671. inst.ReplaceUsesWith(
  672. IR::Value{Common::BitCast<f32>(env.ReadCbufValue(bank_value, offset_value))});
  673. }
  674. }
  675. void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
  676. switch (inst.GetOpcode()) {
  677. case IR::Opcode::GetRegister:
  678. return FoldGetRegister(inst);
  679. case IR::Opcode::GetPred:
  680. return FoldGetPred(inst);
  681. case IR::Opcode::IAdd32:
  682. return FoldAdd<u32>(block, inst);
  683. case IR::Opcode::ISub32:
  684. return FoldISub32(inst);
  685. case IR::Opcode::IMul32:
  686. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a * b; });
  687. return;
  688. case IR::Opcode::ShiftRightArithmetic32:
  689. FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return static_cast<u32>(a >> b); });
  690. return;
  691. case IR::Opcode::BitCastF32U32:
  692. return FoldBitCast<IR::Opcode::BitCastF32U32, f32, u32>(inst, IR::Opcode::BitCastU32F32);
  693. case IR::Opcode::BitCastU32F32:
  694. return FoldBitCast<IR::Opcode::BitCastU32F32, u32, f32>(inst, IR::Opcode::BitCastF32U32);
  695. case IR::Opcode::IAdd64:
  696. return FoldAdd<u64>(block, inst);
  697. case IR::Opcode::PackHalf2x16:
  698. return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
  699. case IR::Opcode::UnpackHalf2x16:
  700. return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
  701. case IR::Opcode::PackFloat2x16:
  702. return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16);
  703. case IR::Opcode::UnpackFloat2x16:
  704. return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16);
  705. case IR::Opcode::SelectU1:
  706. case IR::Opcode::SelectU8:
  707. case IR::Opcode::SelectU16:
  708. case IR::Opcode::SelectU32:
  709. case IR::Opcode::SelectU64:
  710. case IR::Opcode::SelectF16:
  711. case IR::Opcode::SelectF32:
  712. case IR::Opcode::SelectF64:
  713. return FoldSelect(inst);
  714. case IR::Opcode::FPMul32:
  715. return FoldFPMul32(inst);
  716. case IR::Opcode::LogicalAnd:
  717. return FoldLogicalAnd(inst);
  718. case IR::Opcode::LogicalOr:
  719. return FoldLogicalOr(inst);
  720. case IR::Opcode::LogicalNot:
  721. return FoldLogicalNot(inst);
  722. case IR::Opcode::SLessThan:
  723. FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a < b; });
  724. return;
  725. case IR::Opcode::ULessThan:
  726. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a < b; });
  727. return;
  728. case IR::Opcode::SLessThanEqual:
  729. FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a <= b; });
  730. return;
  731. case IR::Opcode::ULessThanEqual:
  732. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a <= b; });
  733. return;
  734. case IR::Opcode::SGreaterThan:
  735. FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a > b; });
  736. return;
  737. case IR::Opcode::UGreaterThan:
  738. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a > b; });
  739. return;
  740. case IR::Opcode::SGreaterThanEqual:
  741. FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a >= b; });
  742. return;
  743. case IR::Opcode::UGreaterThanEqual:
  744. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a >= b; });
  745. return;
  746. case IR::Opcode::IEqual:
  747. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a == b; });
  748. return;
  749. case IR::Opcode::INotEqual:
  750. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a != b; });
  751. return;
  752. case IR::Opcode::BitwiseAnd32:
  753. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a & b; });
  754. return;
  755. case IR::Opcode::BitwiseOr32:
  756. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a | b; });
  757. return;
  758. case IR::Opcode::BitwiseXor32:
  759. FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a ^ b; });
  760. return;
  761. case IR::Opcode::BitFieldUExtract:
  762. FoldWhenAllImmediates(inst, [](u32 base, u32 shift, u32 count) {
  763. if (static_cast<size_t>(shift) + static_cast<size_t>(count) > 32) {
  764. throw LogicError("Undefined result in {}({}, {}, {})", IR::Opcode::BitFieldUExtract,
  765. base, shift, count);
  766. }
  767. return (base >> shift) & ((1U << count) - 1);
  768. });
  769. return;
  770. case IR::Opcode::BitFieldSExtract:
  771. FoldWhenAllImmediates(inst, [](s32 base, u32 shift, u32 count) {
  772. const size_t back_shift{static_cast<size_t>(shift) + static_cast<size_t>(count)};
  773. const size_t left_shift{32 - back_shift};
  774. const size_t right_shift{static_cast<size_t>(32 - count)};
  775. if (back_shift > 32 || left_shift >= 32 || right_shift >= 32) {
  776. throw LogicError("Undefined result in {}({}, {}, {})", IR::Opcode::BitFieldSExtract,
  777. base, shift, count);
  778. }
  779. return static_cast<u32>((base << left_shift) >> right_shift);
  780. });
  781. return;
  782. case IR::Opcode::BitFieldInsert:
  783. FoldWhenAllImmediates(inst, [](u32 base, u32 insert, u32 offset, u32 bits) {
  784. if (bits >= 32 || offset >= 32) {
  785. throw LogicError("Undefined result in {}({}, {}, {}, {})",
  786. IR::Opcode::BitFieldInsert, base, insert, offset, bits);
  787. }
  788. return (base & ~(~(~0u << bits) << offset)) | (insert << offset);
  789. });
  790. return;
  791. case IR::Opcode::CompositeExtractU32x2:
  792. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x2,
  793. IR::Opcode::CompositeInsertU32x2);
  794. case IR::Opcode::CompositeExtractU32x3:
  795. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x3,
  796. IR::Opcode::CompositeInsertU32x3);
  797. case IR::Opcode::CompositeExtractU32x4:
  798. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructU32x4,
  799. IR::Opcode::CompositeInsertU32x4);
  800. case IR::Opcode::CompositeExtractF32x2:
  801. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2,
  802. IR::Opcode::CompositeInsertF32x2);
  803. case IR::Opcode::CompositeExtractF32x3:
  804. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x3,
  805. IR::Opcode::CompositeInsertF32x3);
  806. case IR::Opcode::CompositeExtractF32x4:
  807. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x4,
  808. IR::Opcode::CompositeInsertF32x4);
  809. case IR::Opcode::CompositeExtractF16x2:
  810. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x2,
  811. IR::Opcode::CompositeInsertF16x2);
  812. case IR::Opcode::CompositeExtractF16x3:
  813. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x3,
  814. IR::Opcode::CompositeInsertF16x3);
  815. case IR::Opcode::CompositeExtractF16x4:
  816. return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF16x4,
  817. IR::Opcode::CompositeInsertF16x4);
  818. case IR::Opcode::FSwizzleAdd:
  819. return FoldFSwizzleAdd(block, inst);
  820. case IR::Opcode::GetCbufF32:
  821. case IR::Opcode::GetCbufU32:
  822. if (env.HasHLEMacroState()) {
  823. FoldConstBuffer(env, block, inst);
  824. }
  825. if (env.IsPropietaryDriver()) {
  826. FoldDriverConstBuffer(env, block, inst, 1);
  827. }
  828. break;
  829. default:
  830. break;
  831. }
  832. }
  833. } // Anonymous namespace
  834. void ConstantPropagationPass(Environment& env, IR::Program& program) {
  835. const auto end{program.post_order_blocks.rend()};
  836. for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
  837. IR::Block* const block{*it};
  838. for (IR::Inst& inst : block->Instructions()) {
  839. ConstantPropagation(env, *block, inst);
  840. }
  841. }
  842. }
  843. } // namespace Shader::Optimization