memory.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // Copyright 2018 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <vector>
  6. #include <fmt/format.h>
  7. #include "common/alignment.h"
  8. #include "common/assert.h"
  9. #include "common/common_types.h"
  10. #include "common/logging/log.h"
  11. #include "video_core/engines/shader_bytecode.h"
  12. #include "video_core/shader/node_helper.h"
  13. #include "video_core/shader/shader_ir.h"
  14. namespace VideoCommon::Shader {
  15. using Tegra::Shader::AtomicOp;
  16. using Tegra::Shader::AtomicType;
  17. using Tegra::Shader::Attribute;
  18. using Tegra::Shader::GlobalAtomicType;
  19. using Tegra::Shader::Instruction;
  20. using Tegra::Shader::OpCode;
  21. using Tegra::Shader::Register;
  22. using Tegra::Shader::StoreType;
  23. namespace {
  24. Node GetAtomOperation(AtomicOp op, bool is_signed, Node memory, Node data) {
  25. const OperationCode operation_code = [op] {
  26. switch (op) {
  27. case AtomicOp::Add:
  28. return OperationCode::AtomicIAdd;
  29. case AtomicOp::Min:
  30. return OperationCode::AtomicIMin;
  31. case AtomicOp::Max:
  32. return OperationCode::AtomicIMax;
  33. case AtomicOp::And:
  34. return OperationCode::AtomicIAnd;
  35. case AtomicOp::Or:
  36. return OperationCode::AtomicIOr;
  37. case AtomicOp::Xor:
  38. return OperationCode::AtomicIXor;
  39. case AtomicOp::Exch:
  40. return OperationCode::AtomicIExchange;
  41. default:
  42. UNIMPLEMENTED_MSG("op={}", static_cast<int>(op));
  43. return OperationCode::AtomicIAdd;
  44. }
  45. }();
  46. return SignedOperation(operation_code, is_signed, std::move(memory), std::move(data));
  47. }
  48. bool IsUnaligned(Tegra::Shader::UniformType uniform_type) {
  49. return uniform_type == Tegra::Shader::UniformType::UnsignedByte ||
  50. uniform_type == Tegra::Shader::UniformType::UnsignedShort;
  51. }
  52. u32 GetUnalignedMask(Tegra::Shader::UniformType uniform_type) {
  53. switch (uniform_type) {
  54. case Tegra::Shader::UniformType::UnsignedByte:
  55. return 0b11;
  56. case Tegra::Shader::UniformType::UnsignedShort:
  57. return 0b10;
  58. default:
  59. UNREACHABLE();
  60. return 0;
  61. }
  62. }
  63. u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) {
  64. switch (uniform_type) {
  65. case Tegra::Shader::UniformType::UnsignedByte:
  66. return 8;
  67. case Tegra::Shader::UniformType::UnsignedShort:
  68. return 16;
  69. case Tegra::Shader::UniformType::Single:
  70. return 32;
  71. case Tegra::Shader::UniformType::Double:
  72. return 64;
  73. case Tegra::Shader::UniformType::Quad:
  74. case Tegra::Shader::UniformType::UnsignedQuad:
  75. return 128;
  76. default:
  77. UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type));
  78. return 32;
  79. }
  80. }
  81. Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) {
  82. Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask));
  83. offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
  84. return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset),
  85. Immediate(size));
  86. }
  87. Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) {
  88. Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask));
  89. offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
  90. return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value),
  91. std::move(offset), Immediate(size));
  92. }
  93. Node Sign16Extend(Node value) {
  94. Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15));
  95. Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15));
  96. Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0));
  97. return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend));
  98. }
  99. } // Anonymous namespace
  100. u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
  101. const Instruction instr = {program_code[pc]};
  102. const auto opcode = OpCode::Decode(instr);
  103. switch (opcode->get().GetId()) {
  104. case OpCode::Id::LD_A: {
  105. // Note: Shouldn't this be interp mode flat? As in no interpolation made.
  106. UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
  107. "Indirect attribute loads are not supported");
  108. UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
  109. "Unaligned attribute loads are not supported");
  110. UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() &&
  111. instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word,
  112. "Non-32 bits PHYS reads are not implemented");
  113. const Node buffer{GetRegister(instr.gpr39)};
  114. u64 next_element = instr.attribute.fmt20.element;
  115. auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
  116. const auto LoadNextElement = [&](u32 reg_offset) {
  117. const Node attribute{instr.attribute.fmt20.IsPhysical()
  118. ? GetPhysicalInputAttribute(instr.gpr8, buffer)
  119. : GetInputAttribute(static_cast<Attribute::Index>(next_index),
  120. next_element, buffer)};
  121. SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
  122. // Load the next attribute element into the following register. If the element
  123. // to load goes beyond the vec4 size, load the first element of the next
  124. // attribute.
  125. next_element = (next_element + 1) % 4;
  126. next_index = next_index + (next_element == 0 ? 1 : 0);
  127. };
  128. const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
  129. for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
  130. LoadNextElement(reg_offset);
  131. }
  132. break;
  133. }
  134. case OpCode::Id::LD_C: {
  135. UNIMPLEMENTED_IF(instr.ld_c.unknown != 0);
  136. Node index = GetRegister(instr.gpr8);
  137. const Node op_a =
  138. GetConstBufferIndirect(instr.cbuf36.index, instr.cbuf36.GetOffset() + 0, index);
  139. switch (instr.ld_c.type.Value()) {
  140. case Tegra::Shader::UniformType::Single:
  141. SetRegister(bb, instr.gpr0, op_a);
  142. break;
  143. case Tegra::Shader::UniformType::Double: {
  144. const Node op_b =
  145. GetConstBufferIndirect(instr.cbuf36.index, instr.cbuf36.GetOffset() + 4, index);
  146. SetTemporary(bb, 0, op_a);
  147. SetTemporary(bb, 1, op_b);
  148. SetRegister(bb, instr.gpr0, GetTemporary(0));
  149. SetRegister(bb, instr.gpr0.Value() + 1, GetTemporary(1));
  150. break;
  151. }
  152. default:
  153. UNIMPLEMENTED_MSG("Unhandled type: {}", static_cast<unsigned>(instr.ld_c.type.Value()));
  154. }
  155. break;
  156. }
  157. case OpCode::Id::LD_L:
  158. LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown));
  159. [[fallthrough]];
  160. case OpCode::Id::LD_S: {
  161. const auto GetAddress = [&](s32 offset) {
  162. ASSERT(offset % 4 == 0);
  163. const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset);
  164. return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset);
  165. };
  166. const auto GetMemory = [&](s32 offset) {
  167. return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset))
  168. : GetLocalMemory(GetAddress(offset));
  169. };
  170. switch (instr.ldst_sl.type.Value()) {
  171. case StoreType::Signed16:
  172. SetRegister(bb, instr.gpr0,
  173. Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16)));
  174. break;
  175. case StoreType::Bits32:
  176. case StoreType::Bits64:
  177. case StoreType::Bits128: {
  178. const u32 count = [&] {
  179. switch (instr.ldst_sl.type.Value()) {
  180. case StoreType::Bits32:
  181. return 1;
  182. case StoreType::Bits64:
  183. return 2;
  184. case StoreType::Bits128:
  185. return 4;
  186. default:
  187. UNREACHABLE();
  188. return 0;
  189. }
  190. }();
  191. for (u32 i = 0; i < count; ++i) {
  192. SetTemporary(bb, i, GetMemory(i * 4));
  193. }
  194. for (u32 i = 0; i < count; ++i) {
  195. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  196. }
  197. break;
  198. }
  199. default:
  200. UNIMPLEMENTED_MSG("{} Unhandled type: {}", opcode->get().GetName(),
  201. static_cast<u32>(instr.ldst_sl.type.Value()));
  202. }
  203. break;
  204. }
  205. case OpCode::Id::LD:
  206. case OpCode::Id::LDG: {
  207. const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
  208. switch (opcode->get().GetId()) {
  209. case OpCode::Id::LD:
  210. UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended LD is not implemented");
  211. return instr.generic.type;
  212. case OpCode::Id::LDG:
  213. return instr.ldg.type;
  214. default:
  215. UNREACHABLE();
  216. return {};
  217. }
  218. }();
  219. const auto [real_address_base, base_address, descriptor] =
  220. TrackGlobalMemory(bb, instr, true, false);
  221. const u32 size = GetMemorySize(type);
  222. const u32 count = Common::AlignUp(size, 32) / 32;
  223. if (!real_address_base || !base_address) {
  224. // Tracking failed, load zeroes.
  225. for (u32 i = 0; i < count; ++i) {
  226. SetRegister(bb, instr.gpr0.Value() + i, Immediate(0.0f));
  227. }
  228. break;
  229. }
  230. for (u32 i = 0; i < count; ++i) {
  231. const Node it_offset = Immediate(i * 4);
  232. const Node real_address = Operation(OperationCode::UAdd, real_address_base, it_offset);
  233. Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
  234. // To handle unaligned loads get the bytes used to dereference global memory and extract
  235. // those bytes from the loaded u32.
  236. if (IsUnaligned(type)) {
  237. gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size);
  238. }
  239. SetTemporary(bb, i, gmem);
  240. }
  241. for (u32 i = 0; i < count; ++i) {
  242. SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
  243. }
  244. break;
  245. }
  246. case OpCode::Id::ST_A: {
  247. UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
  248. "Indirect attribute loads are not supported");
  249. UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
  250. "Unaligned attribute loads are not supported");
  251. u64 element = instr.attribute.fmt20.element;
  252. auto index = static_cast<u64>(instr.attribute.fmt20.index.Value());
  253. const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
  254. for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
  255. Node dest;
  256. if (instr.attribute.fmt20.patch) {
  257. const u32 offset = static_cast<u32>(index) * 4 + static_cast<u32>(element);
  258. dest = MakeNode<PatchNode>(offset);
  259. } else {
  260. dest = GetOutputAttribute(static_cast<Attribute::Index>(index), element,
  261. GetRegister(instr.gpr39));
  262. }
  263. const auto src = GetRegister(instr.gpr0.Value() + reg_offset);
  264. bb.push_back(Operation(OperationCode::Assign, dest, src));
  265. // Load the next attribute element into the following register. If the element to load
  266. // goes beyond the vec4 size, load the first element of the next attribute.
  267. element = (element + 1) % 4;
  268. index = index + (element == 0 ? 1 : 0);
  269. }
  270. break;
  271. }
  272. case OpCode::Id::ST_L:
  273. LOG_DEBUG(HW_GPU, "ST_L cache management mode: {}",
  274. static_cast<u64>(instr.st_l.cache_management.Value()));
  275. [[fallthrough]];
  276. case OpCode::Id::ST_S: {
  277. const auto GetAddress = [&](s32 offset) {
  278. ASSERT(offset % 4 == 0);
  279. const Node immediate = Immediate(static_cast<s32>(instr.smem_imm) + offset);
  280. return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate);
  281. };
  282. const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L;
  283. const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory;
  284. const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory;
  285. switch (instr.ldst_sl.type.Value()) {
  286. case StoreType::Bits128:
  287. (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3));
  288. (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2));
  289. [[fallthrough]];
  290. case StoreType::Bits64:
  291. (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1));
  292. [[fallthrough]];
  293. case StoreType::Bits32:
  294. (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
  295. break;
  296. case StoreType::Signed16: {
  297. Node address = GetAddress(0);
  298. Node memory = (this->*get_memory)(address);
  299. (this->*set_memory)(
  300. bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16));
  301. break;
  302. }
  303. default:
  304. UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(),
  305. static_cast<u32>(instr.ldst_sl.type.Value()));
  306. }
  307. break;
  308. }
  309. case OpCode::Id::ST:
  310. case OpCode::Id::STG: {
  311. const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
  312. switch (opcode->get().GetId()) {
  313. case OpCode::Id::ST:
  314. UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended ST is not implemented");
  315. return instr.generic.type;
  316. case OpCode::Id::STG:
  317. return instr.stg.type;
  318. default:
  319. UNREACHABLE();
  320. return {};
  321. }
  322. }();
  323. // For unaligned reads we have to read memory too.
  324. const bool is_read = IsUnaligned(type);
  325. const auto [real_address_base, base_address, descriptor] =
  326. TrackGlobalMemory(bb, instr, is_read, true);
  327. if (!real_address_base || !base_address) {
  328. // Tracking failed, skip the store.
  329. break;
  330. }
  331. const u32 size = GetMemorySize(type);
  332. const u32 count = Common::AlignUp(size, 32) / 32;
  333. for (u32 i = 0; i < count; ++i) {
  334. const Node it_offset = Immediate(i * 4);
  335. const Node real_address = Operation(OperationCode::UAdd, real_address_base, it_offset);
  336. const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
  337. Node value = GetRegister(instr.gpr0.Value() + i);
  338. if (IsUnaligned(type)) {
  339. const u32 mask = GetUnalignedMask(type);
  340. value = InsertUnaligned(gmem, std::move(value), real_address, mask, size);
  341. }
  342. bb.push_back(Operation(OperationCode::Assign, gmem, value));
  343. }
  344. break;
  345. }
  346. case OpCode::Id::ATOM: {
  347. UNIMPLEMENTED_IF_MSG(instr.atom.operation == AtomicOp::Inc ||
  348. instr.atom.operation == AtomicOp::Dec ||
  349. instr.atom.operation == AtomicOp::SafeAdd,
  350. "operation={}", static_cast<int>(instr.atom.operation.Value()));
  351. UNIMPLEMENTED_IF_MSG(instr.atom.type == GlobalAtomicType::S64 ||
  352. instr.atom.type == GlobalAtomicType::U64,
  353. "type={}", static_cast<int>(instr.atom.type.Value()));
  354. const auto [real_address, base_address, descriptor] =
  355. TrackGlobalMemory(bb, instr, true, true);
  356. if (!real_address || !base_address) {
  357. // Tracking failed, skip atomic.
  358. break;
  359. }
  360. const bool is_signed =
  361. instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64;
  362. Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
  363. Node value = GetAtomOperation(static_cast<AtomicOp>(instr.atom.operation), is_signed, gmem,
  364. GetRegister(instr.gpr20));
  365. SetRegister(bb, instr.gpr0, std::move(value));
  366. break;
  367. }
  368. case OpCode::Id::ATOMS: {
  369. UNIMPLEMENTED_IF_MSG(instr.atoms.operation == AtomicOp::Inc ||
  370. instr.atoms.operation == AtomicOp::Dec,
  371. "operation={}", static_cast<int>(instr.atoms.operation.Value()));
  372. UNIMPLEMENTED_IF_MSG(instr.atoms.type == AtomicType::S64 ||
  373. instr.atoms.type == AtomicType::U64,
  374. "type={}", static_cast<int>(instr.atoms.type.Value()));
  375. const bool is_signed =
  376. instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64;
  377. const s32 offset = instr.atoms.GetImmediateOffset();
  378. Node address = GetRegister(instr.gpr8);
  379. address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset));
  380. Node value =
  381. GetAtomOperation(static_cast<AtomicOp>(instr.atoms.operation), is_signed,
  382. GetSharedMemory(std::move(address)), GetRegister(instr.gpr20));
  383. SetRegister(bb, instr.gpr0, std::move(value));
  384. break;
  385. }
  386. case OpCode::Id::AL2P: {
  387. // Ignore al2p.direction since we don't care about it.
  388. // Calculate emulation fake physical address.
  389. const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))};
  390. const Node reg{GetRegister(instr.gpr8)};
  391. const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)};
  392. // Set the fake address to target register.
  393. SetRegister(bb, instr.gpr0, fake_address);
  394. // Signal the shader IR to declare all possible attributes and varyings
  395. uses_physical_attributes = true;
  396. break;
  397. }
  398. default:
  399. UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
  400. }
  401. return pc;
  402. }
  403. std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock& bb,
  404. Instruction instr,
  405. bool is_read, bool is_write) {
  406. const auto addr_register{GetRegister(instr.gmem.gpr)};
  407. const auto immediate_offset{static_cast<u32>(instr.gmem.offset)};
  408. const auto [base_address, index, offset] =
  409. TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
  410. ASSERT_OR_EXECUTE_MSG(base_address != nullptr,
  411. { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
  412. "Global memory tracking failed");
  413. bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
  414. const GlobalMemoryBase descriptor{index, offset};
  415. const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor);
  416. auto& usage = entry->second;
  417. usage.is_written |= is_write;
  418. usage.is_read |= is_read;
  419. const auto real_address =
  420. Operation(OperationCode::UAdd, NO_PRECISE, Immediate(immediate_offset), addr_register);
  421. return {real_address, base_address, descriptor};
  422. }
  423. } // namespace VideoCommon::Shader