lower_int64_to_int32.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <utility>
  5. #include "shader_recompiler/exception.h"
  6. #include "shader_recompiler/frontend/ir/basic_block.h"
  7. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  8. #include "shader_recompiler/frontend/ir/program.h"
  9. #include "shader_recompiler/frontend/ir/value.h"
  10. #include "shader_recompiler/ir_opt/passes.h"
  11. namespace Shader::Optimization {
  12. namespace {
  13. std::pair<IR::U32, IR::U32> Unpack(IR::IREmitter& ir, const IR::Value& packed) {
  14. if (packed.IsImmediate()) {
  15. const u64 value{packed.U64()};
  16. return {
  17. ir.Imm32(static_cast<u32>(value)),
  18. ir.Imm32(static_cast<u32>(value >> 32)),
  19. };
  20. } else {
  21. return std::pair<IR::U32, IR::U32>{
  22. ir.CompositeExtract(packed, 0u),
  23. ir.CompositeExtract(packed, 1u),
  24. };
  25. }
  26. }
  27. void IAdd64To32(IR::Block& block, IR::Inst& inst) {
  28. if (inst.HasAssociatedPseudoOperation()) {
  29. throw NotImplementedException("IAdd64 emulation with pseudo instructions");
  30. }
  31. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  32. const auto [a_lo, a_hi]{Unpack(ir, inst.Arg(0))};
  33. const auto [b_lo, b_hi]{Unpack(ir, inst.Arg(1))};
  34. const IR::U32 ret_lo{ir.IAdd(a_lo, b_lo)};
  35. const IR::U32 carry{ir.Select(ir.GetCarryFromOp(ret_lo), ir.Imm32(1u), ir.Imm32(0u))};
  36. const IR::U32 ret_hi{ir.IAdd(ir.IAdd(a_hi, b_hi), carry)};
  37. inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
  38. }
  39. void ISub64To32(IR::Block& block, IR::Inst& inst) {
  40. if (inst.HasAssociatedPseudoOperation()) {
  41. throw NotImplementedException("ISub64 emulation with pseudo instructions");
  42. }
  43. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  44. const auto [a_lo, a_hi]{Unpack(ir, inst.Arg(0))};
  45. const auto [b_lo, b_hi]{Unpack(ir, inst.Arg(1))};
  46. const IR::U32 ret_lo{ir.ISub(a_lo, b_lo)};
  47. const IR::U1 underflow{ir.IGreaterThan(ret_lo, a_lo, false)};
  48. const IR::U32 underflow_bit{ir.Select(underflow, ir.Imm32(1u), ir.Imm32(0u))};
  49. const IR::U32 ret_hi{ir.ISub(ir.ISub(a_hi, b_hi), underflow_bit)};
  50. inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
  51. }
  52. void INeg64To32(IR::Block& block, IR::Inst& inst) {
  53. if (inst.HasAssociatedPseudoOperation()) {
  54. throw NotImplementedException("INeg64 emulation with pseudo instructions");
  55. }
  56. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  57. auto [lo, hi]{Unpack(ir, inst.Arg(0))};
  58. lo = ir.BitwiseNot(lo);
  59. hi = ir.BitwiseNot(hi);
  60. lo = ir.IAdd(lo, ir.Imm32(1));
  61. const IR::U32 carry{ir.Select(ir.GetCarryFromOp(lo), ir.Imm32(1u), ir.Imm32(0u))};
  62. hi = ir.IAdd(hi, carry);
  63. inst.ReplaceUsesWith(ir.CompositeConstruct(lo, hi));
  64. }
  65. void ShiftLeftLogical64To32(IR::Block& block, IR::Inst& inst) {
  66. if (inst.HasAssociatedPseudoOperation()) {
  67. throw NotImplementedException("ShiftLeftLogical64 emulation with pseudo instructions");
  68. }
  69. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  70. const auto [lo, hi]{Unpack(ir, inst.Arg(0))};
  71. const IR::U32 shift{inst.Arg(1)};
  72. const IR::U32 shifted_lo{ir.ShiftLeftLogical(lo, shift)};
  73. const IR::U32 shifted_hi{ir.ShiftLeftLogical(hi, shift)};
  74. const IR::U32 inv_shift{ir.ISub(shift, ir.Imm32(32))};
  75. const IR::U1 is_long{ir.IGreaterThanEqual(inv_shift, ir.Imm32(0), true)};
  76. const IR::U1 is_zero{ir.IEqual(shift, ir.Imm32(0))};
  77. const IR::U32 long_ret_lo{ir.Imm32(0)};
  78. const IR::U32 long_ret_hi{ir.ShiftLeftLogical(lo, inv_shift)};
  79. const IR::U32 shift_complement{ir.ISub(ir.Imm32(32), shift)};
  80. const IR::U32 lo_extract{ir.BitFieldExtract(lo, shift_complement, shift, false)};
  81. const IR::U32 short_ret_lo{shifted_lo};
  82. const IR::U32 short_ret_hi{ir.BitwiseOr(shifted_hi, lo_extract)};
  83. const IR::U32 zero_ret_lo{lo};
  84. const IR::U32 zero_ret_hi{hi};
  85. const IR::U32 non_zero_lo{ir.Select(is_long, long_ret_lo, short_ret_lo)};
  86. const IR::U32 non_zero_hi{ir.Select(is_long, long_ret_hi, short_ret_hi)};
  87. const IR::U32 ret_lo{ir.Select(is_zero, zero_ret_lo, non_zero_lo)};
  88. const IR::U32 ret_hi{ir.Select(is_zero, zero_ret_hi, non_zero_hi)};
  89. inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
  90. }
  91. void ShiftRightLogical64To32(IR::Block& block, IR::Inst& inst) {
  92. if (inst.HasAssociatedPseudoOperation()) {
  93. throw NotImplementedException("ShiftRightLogical64 emulation with pseudo instructions");
  94. }
  95. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  96. const auto [lo, hi]{Unpack(ir, inst.Arg(0))};
  97. const IR::U32 shift{inst.Arg(1)};
  98. const IR::U32 shifted_lo{ir.ShiftRightLogical(lo, shift)};
  99. const IR::U32 shifted_hi{ir.ShiftRightLogical(hi, shift)};
  100. const IR::U32 inv_shift{ir.ISub(shift, ir.Imm32(32))};
  101. const IR::U1 is_long{ir.IGreaterThanEqual(inv_shift, ir.Imm32(0), true)};
  102. const IR::U1 is_zero{ir.IEqual(shift, ir.Imm32(0))};
  103. const IR::U32 long_ret_hi{ir.Imm32(0)};
  104. const IR::U32 long_ret_lo{ir.ShiftRightLogical(hi, inv_shift)};
  105. const IR::U32 shift_complement{ir.ISub(ir.Imm32(32), shift)};
  106. const IR::U32 short_hi_extract{ir.BitFieldExtract(hi, ir.Imm32(0), shift)};
  107. const IR::U32 short_ret_hi{shifted_hi};
  108. const IR::U32 short_ret_lo{
  109. ir.BitFieldInsert(shifted_lo, short_hi_extract, shift_complement, shift)};
  110. const IR::U32 zero_ret_lo{lo};
  111. const IR::U32 zero_ret_hi{hi};
  112. const IR::U32 non_zero_lo{ir.Select(is_long, long_ret_lo, short_ret_lo)};
  113. const IR::U32 non_zero_hi{ir.Select(is_long, long_ret_hi, short_ret_hi)};
  114. const IR::U32 ret_lo{ir.Select(is_zero, zero_ret_lo, non_zero_lo)};
  115. const IR::U32 ret_hi{ir.Select(is_zero, zero_ret_hi, non_zero_hi)};
  116. inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
  117. }
  118. void ShiftRightArithmetic64To32(IR::Block& block, IR::Inst& inst) {
  119. if (inst.HasAssociatedPseudoOperation()) {
  120. throw NotImplementedException("ShiftRightArithmetic64 emulation with pseudo instructions");
  121. }
  122. IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
  123. const auto [lo, hi]{Unpack(ir, inst.Arg(0))};
  124. const IR::U32 shift{inst.Arg(1)};
  125. const IR::U32 shifted_lo{ir.ShiftRightLogical(lo, shift)};
  126. const IR::U32 shifted_hi{ir.ShiftRightArithmetic(hi, shift)};
  127. const IR::U32 sign_extension{ir.ShiftRightArithmetic(hi, ir.Imm32(31))};
  128. const IR::U32 inv_shift{ir.ISub(shift, ir.Imm32(32))};
  129. const IR::U1 is_long{ir.IGreaterThanEqual(inv_shift, ir.Imm32(0), true)};
  130. const IR::U1 is_zero{ir.IEqual(shift, ir.Imm32(0))};
  131. const IR::U32 long_ret_hi{sign_extension};
  132. const IR::U32 long_ret_lo{ir.ShiftRightArithmetic(hi, inv_shift)};
  133. const IR::U32 shift_complement{ir.ISub(ir.Imm32(32), shift)};
  134. const IR::U32 short_hi_extract(ir.BitFieldExtract(hi, ir.Imm32(0), shift));
  135. const IR::U32 short_ret_hi{shifted_hi};
  136. const IR::U32 short_ret_lo{
  137. ir.BitFieldInsert(shifted_lo, short_hi_extract, shift_complement, shift)};
  138. const IR::U32 zero_ret_lo{lo};
  139. const IR::U32 zero_ret_hi{hi};
  140. const IR::U32 non_zero_lo{ir.Select(is_long, long_ret_lo, short_ret_lo)};
  141. const IR::U32 non_zero_hi{ir.Select(is_long, long_ret_hi, short_ret_hi)};
  142. const IR::U32 ret_lo{ir.Select(is_zero, zero_ret_lo, non_zero_lo)};
  143. const IR::U32 ret_hi{ir.Select(is_zero, zero_ret_hi, non_zero_hi)};
  144. inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
  145. }
  146. void Lower(IR::Block& block, IR::Inst& inst) {
  147. switch (inst.GetOpcode()) {
  148. case IR::Opcode::PackUint2x32:
  149. case IR::Opcode::UnpackUint2x32:
  150. return inst.ReplaceOpcode(IR::Opcode::Identity);
  151. case IR::Opcode::IAdd64:
  152. return IAdd64To32(block, inst);
  153. case IR::Opcode::ISub64:
  154. return ISub64To32(block, inst);
  155. case IR::Opcode::INeg64:
  156. return INeg64To32(block, inst);
  157. case IR::Opcode::ShiftLeftLogical64:
  158. return ShiftLeftLogical64To32(block, inst);
  159. case IR::Opcode::ShiftRightLogical64:
  160. return ShiftRightLogical64To32(block, inst);
  161. case IR::Opcode::ShiftRightArithmetic64:
  162. return ShiftRightArithmetic64To32(block, inst);
  163. case IR::Opcode::SharedAtomicExchange64:
  164. return inst.ReplaceOpcode(IR::Opcode::SharedAtomicExchange32x2);
  165. case IR::Opcode::GlobalAtomicIAdd64:
  166. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicIAdd32x2);
  167. case IR::Opcode::GlobalAtomicSMin64:
  168. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMin32x2);
  169. case IR::Opcode::GlobalAtomicUMin64:
  170. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMin32x2);
  171. case IR::Opcode::GlobalAtomicSMax64:
  172. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMax32x2);
  173. case IR::Opcode::GlobalAtomicUMax64:
  174. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMax32x2);
  175. case IR::Opcode::GlobalAtomicAnd64:
  176. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicAnd32x2);
  177. case IR::Opcode::GlobalAtomicOr64:
  178. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicOr32x2);
  179. case IR::Opcode::GlobalAtomicXor64:
  180. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicXor32x2);
  181. case IR::Opcode::GlobalAtomicExchange64:
  182. return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicExchange32x2);
  183. default:
  184. break;
  185. }
  186. }
  187. } // Anonymous namespace
  188. void LowerInt64ToInt32(IR::Program& program) {
  189. const auto end{program.post_order_blocks.rend()};
  190. for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
  191. IR::Block* const block{*it};
  192. for (IR::Inst& inst : block->Instructions()) {
  193. Lower(*block, inst);
  194. }
  195. }
  196. }
  197. } // namespace Shader::Optimization