arithmetic_integer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Copyright 2018 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/common_types.h"
  6. #include "video_core/engines/shader_bytecode.h"
  7. #include "video_core/shader/node_helper.h"
  8. #include "video_core/shader/shader_ir.h"
  9. namespace VideoCommon::Shader {
  10. using Tegra::Shader::IAdd3Height;
  11. using Tegra::Shader::Instruction;
  12. using Tegra::Shader::OpCode;
  13. using Tegra::Shader::Pred;
  14. using Tegra::Shader::Register;
  15. u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
  16. const Instruction instr = {program_code[pc]};
  17. const auto opcode = OpCode::Decode(instr);
  18. Node op_a = GetRegister(instr.gpr8);
  19. Node op_b = [&]() {
  20. if (instr.is_b_imm) {
  21. return Immediate(instr.alu.GetSignedImm20_20());
  22. } else if (instr.is_b_gpr) {
  23. return GetRegister(instr.gpr20);
  24. } else {
  25. return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
  26. }
  27. }();
  28. switch (opcode->get().GetId()) {
  29. case OpCode::Id::IADD_C:
  30. case OpCode::Id::IADD_R:
  31. case OpCode::Id::IADD_IMM: {
  32. UNIMPLEMENTED_IF_MSG(instr.alu.saturate_d, "IADD saturation not implemented");
  33. op_a = GetOperandAbsNegInteger(op_a, false, instr.alu_integer.negate_a, true);
  34. op_b = GetOperandAbsNegInteger(op_b, false, instr.alu_integer.negate_b, true);
  35. const Node value = Operation(OperationCode::IAdd, PRECISE, op_a, op_b);
  36. SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
  37. SetRegister(bb, instr.gpr0, value);
  38. break;
  39. }
  40. case OpCode::Id::IADD3_C:
  41. case OpCode::Id::IADD3_R:
  42. case OpCode::Id::IADD3_IMM: {
  43. Node op_c = GetRegister(instr.gpr39);
  44. const auto ApplyHeight = [&](IAdd3Height height, Node value) {
  45. switch (height) {
  46. case IAdd3Height::None:
  47. return value;
  48. case IAdd3Height::LowerHalfWord:
  49. return BitfieldExtract(value, 0, 16);
  50. case IAdd3Height::UpperHalfWord:
  51. return BitfieldExtract(value, 16, 16);
  52. default:
  53. UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}", static_cast<u32>(height));
  54. return Immediate(0);
  55. }
  56. };
  57. if (opcode->get().GetId() == OpCode::Id::IADD3_R) {
  58. op_a = ApplyHeight(instr.iadd3.height_a, op_a);
  59. op_b = ApplyHeight(instr.iadd3.height_b, op_b);
  60. op_c = ApplyHeight(instr.iadd3.height_c, op_c);
  61. }
  62. op_a = GetOperandAbsNegInteger(op_a, false, instr.iadd3.neg_a, true);
  63. op_b = GetOperandAbsNegInteger(op_b, false, instr.iadd3.neg_b, true);
  64. op_c = GetOperandAbsNegInteger(op_c, false, instr.iadd3.neg_c, true);
  65. const Node value = [&]() {
  66. const Node add_ab = Operation(OperationCode::IAdd, NO_PRECISE, op_a, op_b);
  67. if (opcode->get().GetId() != OpCode::Id::IADD3_R) {
  68. return Operation(OperationCode::IAdd, NO_PRECISE, add_ab, op_c);
  69. }
  70. const Node shifted = [&]() {
  71. switch (instr.iadd3.mode) {
  72. case Tegra::Shader::IAdd3Mode::RightShift:
  73. // TODO(tech4me): According to
  74. // https://envytools.readthedocs.io/en/latest/hw/graph/maxwell/cuda/int.html?highlight=iadd3
  75. // The addition between op_a and op_b should be done in uint33, more
  76. // investigation required
  77. return Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, add_ab,
  78. Immediate(16));
  79. case Tegra::Shader::IAdd3Mode::LeftShift:
  80. return Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, add_ab,
  81. Immediate(16));
  82. default:
  83. return add_ab;
  84. }
  85. }();
  86. return Operation(OperationCode::IAdd, NO_PRECISE, shifted, op_c);
  87. }();
  88. SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
  89. SetRegister(bb, instr.gpr0, value);
  90. break;
  91. }
  92. case OpCode::Id::ISCADD_C:
  93. case OpCode::Id::ISCADD_R:
  94. case OpCode::Id::ISCADD_IMM: {
  95. UNIMPLEMENTED_IF_MSG(instr.generates_cc,
  96. "Condition codes generation in ISCADD is not implemented");
  97. op_a = GetOperandAbsNegInteger(op_a, false, instr.alu_integer.negate_a, true);
  98. op_b = GetOperandAbsNegInteger(op_b, false, instr.alu_integer.negate_b, true);
  99. const Node shift = Immediate(static_cast<u32>(instr.alu_integer.shift_amount));
  100. const Node shifted_a = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, op_a, shift);
  101. const Node value = Operation(OperationCode::IAdd, NO_PRECISE, shifted_a, op_b);
  102. SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
  103. SetRegister(bb, instr.gpr0, value);
  104. break;
  105. }
  106. case OpCode::Id::POPC_C:
  107. case OpCode::Id::POPC_R:
  108. case OpCode::Id::POPC_IMM: {
  109. if (instr.popc.invert) {
  110. op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_b);
  111. }
  112. const Node value = Operation(OperationCode::IBitCount, PRECISE, op_b);
  113. SetRegister(bb, instr.gpr0, value);
  114. break;
  115. }
  116. case OpCode::Id::FLO_R:
  117. case OpCode::Id::FLO_C:
  118. case OpCode::Id::FLO_IMM: {
  119. Node value;
  120. if (instr.flo.invert) {
  121. op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, std::move(op_b));
  122. }
  123. if (instr.flo.is_signed) {
  124. value = Operation(OperationCode::IBitMSB, NO_PRECISE, std::move(op_b));
  125. } else {
  126. value = Operation(OperationCode::UBitMSB, NO_PRECISE, std::move(op_b));
  127. }
  128. if (instr.flo.sh) {
  129. value =
  130. Operation(OperationCode::UBitwiseXor, NO_PRECISE, std::move(value), Immediate(31));
  131. }
  132. SetRegister(bb, instr.gpr0, std::move(value));
  133. break;
  134. }
  135. case OpCode::Id::SEL_C:
  136. case OpCode::Id::SEL_R:
  137. case OpCode::Id::SEL_IMM: {
  138. const Node condition = GetPredicate(instr.sel.pred, instr.sel.neg_pred != 0);
  139. const Node value = Operation(OperationCode::Select, PRECISE, condition, op_a, op_b);
  140. SetRegister(bb, instr.gpr0, value);
  141. break;
  142. }
  143. case OpCode::Id::ICMP_CR:
  144. case OpCode::Id::ICMP_R:
  145. case OpCode::Id::ICMP_RC:
  146. case OpCode::Id::ICMP_IMM: {
  147. const Node zero = Immediate(0);
  148. const auto [op_rhs, test] = [&]() -> std::pair<Node, Node> {
  149. switch (opcode->get().GetId()) {
  150. case OpCode::Id::ICMP_CR:
  151. return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()),
  152. GetRegister(instr.gpr39)};
  153. case OpCode::Id::ICMP_R:
  154. return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
  155. case OpCode::Id::ICMP_RC:
  156. return {GetRegister(instr.gpr39),
  157. GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())};
  158. case OpCode::Id::ICMP_IMM:
  159. return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
  160. default:
  161. UNREACHABLE();
  162. return {zero, zero};
  163. }
  164. }();
  165. const Node op_lhs = GetRegister(instr.gpr8);
  166. const Node comparison =
  167. GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero);
  168. SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_lhs, op_rhs));
  169. break;
  170. }
  171. case OpCode::Id::LOP_C:
  172. case OpCode::Id::LOP_R:
  173. case OpCode::Id::LOP_IMM: {
  174. if (instr.alu.lop.invert_a)
  175. op_a = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_a);
  176. if (instr.alu.lop.invert_b)
  177. op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_b);
  178. WriteLogicOperation(bb, instr.gpr0, instr.alu.lop.operation, op_a, op_b,
  179. instr.alu.lop.pred_result_mode, instr.alu.lop.pred48,
  180. instr.generates_cc);
  181. break;
  182. }
  183. case OpCode::Id::LOP3_C:
  184. case OpCode::Id::LOP3_R:
  185. case OpCode::Id::LOP3_IMM: {
  186. const Node op_c = GetRegister(instr.gpr39);
  187. const Node lut = [&]() {
  188. if (opcode->get().GetId() == OpCode::Id::LOP3_R) {
  189. return Immediate(instr.alu.lop3.GetImmLut28());
  190. } else {
  191. return Immediate(instr.alu.lop3.GetImmLut48());
  192. }
  193. }();
  194. WriteLop3Instruction(bb, instr.gpr0, op_a, op_b, op_c, lut, instr.generates_cc);
  195. break;
  196. }
  197. case OpCode::Id::IMNMX_C:
  198. case OpCode::Id::IMNMX_R:
  199. case OpCode::Id::IMNMX_IMM: {
  200. UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
  201. const bool is_signed = instr.imnmx.is_signed;
  202. const Node condition = GetPredicate(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
  203. const Node min = SignedOperation(OperationCode::IMin, is_signed, NO_PRECISE, op_a, op_b);
  204. const Node max = SignedOperation(OperationCode::IMax, is_signed, NO_PRECISE, op_a, op_b);
  205. const Node value = Operation(OperationCode::Select, NO_PRECISE, condition, min, max);
  206. SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
  207. SetRegister(bb, instr.gpr0, value);
  208. break;
  209. }
  210. case OpCode::Id::LEA_R2:
  211. case OpCode::Id::LEA_R1:
  212. case OpCode::Id::LEA_IMM:
  213. case OpCode::Id::LEA_RZ:
  214. case OpCode::Id::LEA_HI: {
  215. const auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> {
  216. switch (opcode->get().GetId()) {
  217. case OpCode::Id::LEA_R2: {
  218. return {GetRegister(instr.gpr20), GetRegister(instr.gpr39),
  219. Immediate(static_cast<u32>(instr.lea.r2.entry_a))};
  220. }
  221. case OpCode::Id::LEA_R1: {
  222. const bool neg = instr.lea.r1.neg != 0;
  223. return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
  224. GetRegister(instr.gpr20),
  225. Immediate(static_cast<u32>(instr.lea.r1.entry_a))};
  226. }
  227. case OpCode::Id::LEA_IMM: {
  228. const bool neg = instr.lea.imm.neg != 0;
  229. return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)),
  230. GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
  231. Immediate(static_cast<u32>(instr.lea.imm.entry_b))};
  232. }
  233. case OpCode::Id::LEA_RZ: {
  234. const bool neg = instr.lea.rz.neg != 0;
  235. return {GetConstBuffer(instr.lea.rz.cb_index, instr.lea.rz.cb_offset),
  236. GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
  237. Immediate(static_cast<u32>(instr.lea.rz.entry_a))};
  238. }
  239. case OpCode::Id::LEA_HI:
  240. default:
  241. UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName());
  242. return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)), GetRegister(instr.gpr8),
  243. Immediate(static_cast<u32>(instr.lea.imm.entry_b))};
  244. }
  245. }();
  246. UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
  247. "Unhandled LEA Predicate");
  248. const Node shifted_c =
  249. Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, Immediate(1), op_c);
  250. const Node mul_bc = Operation(OperationCode::IMul, NO_PRECISE, op_b, shifted_c);
  251. const Node value = Operation(OperationCode::IAdd, NO_PRECISE, op_a, mul_bc);
  252. SetRegister(bb, instr.gpr0, value);
  253. break;
  254. }
  255. default:
  256. UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}", opcode->get().GetName());
  257. }
  258. return pc;
  259. }
  260. void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c,
  261. Node imm_lut, bool sets_cc) {
  262. const Node lop3_fast = [&](const Node na, const Node nb, const Node nc, const Node ttbl) {
  263. Node value = Immediate(0);
  264. const ImmediateNode imm = std::get<ImmediateNode>(*ttbl);
  265. if (imm.GetValue() & 0x01) {
  266. const Node a = Operation(OperationCode::IBitwiseNot, na);
  267. const Node b = Operation(OperationCode::IBitwiseNot, nb);
  268. const Node c = Operation(OperationCode::IBitwiseNot, nc);
  269. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
  270. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
  271. value = Operation(OperationCode::IBitwiseOr, value, r);
  272. }
  273. if (imm.GetValue() & 0x02) {
  274. const Node a = Operation(OperationCode::IBitwiseNot, na);
  275. const Node b = Operation(OperationCode::IBitwiseNot, nb);
  276. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
  277. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
  278. value = Operation(OperationCode::IBitwiseOr, value, r);
  279. }
  280. if (imm.GetValue() & 0x04) {
  281. const Node a = Operation(OperationCode::IBitwiseNot, na);
  282. const Node c = Operation(OperationCode::IBitwiseNot, nc);
  283. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
  284. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
  285. value = Operation(OperationCode::IBitwiseOr, value, r);
  286. }
  287. if (imm.GetValue() & 0x08) {
  288. const Node a = Operation(OperationCode::IBitwiseNot, na);
  289. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
  290. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
  291. value = Operation(OperationCode::IBitwiseOr, value, r);
  292. }
  293. if (imm.GetValue() & 0x10) {
  294. const Node b = Operation(OperationCode::IBitwiseNot, nb);
  295. const Node c = Operation(OperationCode::IBitwiseNot, nc);
  296. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
  297. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
  298. value = Operation(OperationCode::IBitwiseOr, value, r);
  299. }
  300. if (imm.GetValue() & 0x20) {
  301. const Node b = Operation(OperationCode::IBitwiseNot, nb);
  302. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
  303. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
  304. value = Operation(OperationCode::IBitwiseOr, value, r);
  305. }
  306. if (imm.GetValue() & 0x40) {
  307. const Node c = Operation(OperationCode::IBitwiseNot, nc);
  308. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
  309. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
  310. value = Operation(OperationCode::IBitwiseOr, value, r);
  311. }
  312. if (imm.GetValue() & 0x80) {
  313. Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
  314. r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
  315. value = Operation(OperationCode::IBitwiseOr, value, r);
  316. }
  317. return value;
  318. }(op_a, op_b, op_c, imm_lut);
  319. SetInternalFlagsFromInteger(bb, lop3_fast, sets_cc);
  320. SetRegister(bb, dest, lop3_fast);
  321. }
  322. } // namespace VideoCommon::Shader