InstEmitMemory.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. using Ryujinx.Graphics.Shader.Decoders;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
  5. using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
  6. namespace Ryujinx.Graphics.Shader.Instructions
  7. {
  8. static partial class InstEmit
  9. {
  10. private enum MemoryRegion
  11. {
  12. Global,
  13. Local,
  14. Shared
  15. }
  16. public static void Ald(EmitterContext context)
  17. {
  18. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  19. Operand primVertex = context.Copy(GetSrcC(context));
  20. for (int index = 0; index < op.Count; index++)
  21. {
  22. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  23. if (rd.IsRZ)
  24. {
  25. break;
  26. }
  27. Operand src = Attribute(op.AttributeOffset + index * 4);
  28. context.Copy(Register(rd), context.LoadAttribute(src, primVertex));
  29. }
  30. }
  31. public static void Ast(EmitterContext context)
  32. {
  33. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  34. for (int index = 0; index < op.Count; index++)
  35. {
  36. if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex)
  37. {
  38. break;
  39. }
  40. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  41. Operand dest = Attribute(op.AttributeOffset + index * 4);
  42. context.Copy(dest, Register(rd));
  43. }
  44. }
  45. public static void Atoms(EmitterContext context)
  46. {
  47. OpCodeAtom op = (OpCodeAtom)context.CurrOp;
  48. Operand mem = context.ShiftRightU32(GetSrcA(context), Const(2));
  49. mem = context.IAdd(mem, Const(op.Offset));
  50. Operand value = GetSrcB(context);
  51. Operand res = EmitAtomicOp(context, Instruction.MrShared, op.AtomicOp, op.Type, mem, value);
  52. context.Copy(GetDest(context), res);
  53. }
  54. public static void Ipa(EmitterContext context)
  55. {
  56. OpCodeIpa op = (OpCodeIpa)context.CurrOp;
  57. InterpolationQualifier iq = InterpolationQualifier.None;
  58. switch (op.Mode)
  59. {
  60. case InterpolationMode.Pass: iq = InterpolationQualifier.NoPerspective; break;
  61. }
  62. Operand srcA = Attribute(op.AttributeOffset, iq);
  63. Operand srcB = GetSrcB(context);
  64. Operand res = context.FPSaturate(srcA, op.Saturate);
  65. context.Copy(GetDest(context), res);
  66. }
  67. public static void Isberd(EmitterContext context)
  68. {
  69. // This instruction performs a load from ISBE memory,
  70. // however it seems to be only used to get some vertex
  71. // input data, so we instead propagate the offset so that
  72. // it can be used on the attribute load.
  73. context.Copy(GetDest(context), GetSrcA(context));
  74. }
  75. public static void Ld(EmitterContext context)
  76. {
  77. EmitLoad(context, MemoryRegion.Local);
  78. }
  79. public static void Ldc(EmitterContext context)
  80. {
  81. OpCodeLdc op = (OpCodeLdc)context.CurrOp;
  82. if (op.Size > IntegerSize.B64)
  83. {
  84. // TODO: Warning.
  85. }
  86. bool isSmallInt = op.Size < IntegerSize.B32;
  87. int count = op.Size == IntegerSize.B64 ? 2 : 1;
  88. Operand wordOffset = context.ShiftRightU32(GetSrcA(context), Const(2));
  89. wordOffset = context.IAdd(wordOffset, Const(op.Offset));
  90. Operand bitOffset = GetBitOffset(context, GetSrcA(context));
  91. for (int index = 0; index < count; index++)
  92. {
  93. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  94. if (rd.IsRZ)
  95. {
  96. break;
  97. }
  98. Operand offset = context.IAdd(wordOffset, Const(index));
  99. Operand value = context.LoadConstant(Const(op.Slot), offset);
  100. if (isSmallInt)
  101. {
  102. value = ExtractSmallInt(context, op.Size, wordOffset, value);
  103. }
  104. context.Copy(Register(rd), value);
  105. }
  106. }
  107. public static void Ldg(EmitterContext context)
  108. {
  109. EmitLoad(context, MemoryRegion.Global);
  110. }
  111. public static void Lds(EmitterContext context)
  112. {
  113. EmitLoad(context, MemoryRegion.Shared);
  114. }
  115. public static void Out(EmitterContext context)
  116. {
  117. OpCode op = context.CurrOp;
  118. bool emit = op.RawOpCode.Extract(39);
  119. bool cut = op.RawOpCode.Extract(40);
  120. if (!(emit || cut))
  121. {
  122. // TODO: Warning.
  123. }
  124. if (emit)
  125. {
  126. context.EmitVertex();
  127. }
  128. if (cut)
  129. {
  130. context.EndPrimitive();
  131. }
  132. }
  133. public static void Red(EmitterContext context)
  134. {
  135. OpCodeRed op = (OpCodeRed)context.CurrOp;
  136. Operand offset = context.IAdd(GetSrcA(context), Const(op.Offset));
  137. Operand mem = context.ShiftRightU32(offset, Const(2));
  138. EmitAtomicOp(context, Instruction.MrGlobal, op.AtomicOp, op.Type, mem, GetDest(context));
  139. }
  140. public static void St(EmitterContext context)
  141. {
  142. EmitStore(context, MemoryRegion.Local);
  143. }
  144. public static void Stg(EmitterContext context)
  145. {
  146. EmitStore(context, MemoryRegion.Global);
  147. }
  148. public static void Sts(EmitterContext context)
  149. {
  150. EmitStore(context, MemoryRegion.Shared);
  151. }
  152. private static Operand EmitAtomicOp(
  153. EmitterContext context,
  154. Instruction mr,
  155. AtomicOp op,
  156. ReductionType type,
  157. Operand mem,
  158. Operand value)
  159. {
  160. Operand res = Const(0);
  161. switch (op)
  162. {
  163. case AtomicOp.Add:
  164. if (type == ReductionType.S32 || type == ReductionType.U32)
  165. {
  166. res = context.AtomicAdd(mr, mem, value);
  167. }
  168. else
  169. {
  170. // Not supported or invalid.
  171. }
  172. break;
  173. case AtomicOp.BitwiseAnd:
  174. if (type == ReductionType.S32 || type == ReductionType.U32)
  175. {
  176. res = context.AtomicAnd(mr, mem, value);
  177. }
  178. else
  179. {
  180. // Not supported or invalid.
  181. }
  182. break;
  183. case AtomicOp.BitwiseExclusiveOr:
  184. if (type == ReductionType.S32 || type == ReductionType.U32)
  185. {
  186. res = context.AtomicXor(mr, mem, value);
  187. }
  188. else
  189. {
  190. // Not supported or invalid.
  191. }
  192. break;
  193. case AtomicOp.BitwiseOr:
  194. if (type == ReductionType.S32 || type == ReductionType.U32)
  195. {
  196. res = context.AtomicOr(mr, mem, value);
  197. }
  198. else
  199. {
  200. // Not supported or invalid.
  201. }
  202. break;
  203. case AtomicOp.Maximum:
  204. if (type == ReductionType.S32)
  205. {
  206. res = context.AtomicMaxS32(mr, mem, value);
  207. }
  208. else if (type == ReductionType.U32)
  209. {
  210. res = context.AtomicMaxU32(mr, mem, value);
  211. }
  212. else
  213. {
  214. // Not supported or invalid.
  215. }
  216. break;
  217. case AtomicOp.Minimum:
  218. if (type == ReductionType.S32)
  219. {
  220. res = context.AtomicMinS32(mr, mem, value);
  221. }
  222. else if (type == ReductionType.U32)
  223. {
  224. res = context.AtomicMinU32(mr, mem, value);
  225. }
  226. else
  227. {
  228. // Not supported or invalid.
  229. }
  230. break;
  231. }
  232. return res;
  233. }
  234. private static void EmitLoad(EmitterContext context, MemoryRegion region)
  235. {
  236. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  237. if (op.Size > IntegerSize.B128)
  238. {
  239. // TODO: Warning.
  240. }
  241. bool isSmallInt = op.Size < IntegerSize.B32;
  242. int count = 1;
  243. switch (op.Size)
  244. {
  245. case IntegerSize.B64: count = 2; break;
  246. case IntegerSize.B128: count = 4; break;
  247. }
  248. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  249. // Word offset = byte offset / 4 (one word = 4 bytes).
  250. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  251. Operand bitOffset = GetBitOffset(context, baseOffset);
  252. for (int index = 0; index < count; index++)
  253. {
  254. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  255. if (rd.IsRZ)
  256. {
  257. break;
  258. }
  259. Operand offset = context.IAdd(wordOffset, Const(index));
  260. Operand value = null;
  261. switch (region)
  262. {
  263. case MemoryRegion.Global: value = context.LoadGlobal(offset); break;
  264. case MemoryRegion.Local: value = context.LoadLocal (offset); break;
  265. case MemoryRegion.Shared: value = context.LoadShared(offset); break;
  266. }
  267. if (isSmallInt)
  268. {
  269. value = ExtractSmallInt(context, op.Size, bitOffset, value);
  270. }
  271. context.Copy(Register(rd), value);
  272. }
  273. }
  274. private static void EmitStore(EmitterContext context, MemoryRegion region)
  275. {
  276. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  277. if (op.Size > IntegerSize.B128)
  278. {
  279. // TODO: Warning.
  280. }
  281. bool isSmallInt = op.Size < IntegerSize.B32;
  282. int count = 1;
  283. switch (op.Size)
  284. {
  285. case IntegerSize.B64: count = 2; break;
  286. case IntegerSize.B128: count = 4; break;
  287. }
  288. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  289. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  290. Operand bitOffset = GetBitOffset(context, baseOffset);
  291. for (int index = 0; index < count; index++)
  292. {
  293. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  294. Operand value = Register(rd);
  295. Operand offset = context.IAdd(wordOffset, Const(index));
  296. if (isSmallInt)
  297. {
  298. Operand word = null;
  299. switch (region)
  300. {
  301. case MemoryRegion.Global: word = context.LoadGlobal(offset); break;
  302. case MemoryRegion.Local: word = context.LoadLocal (offset); break;
  303. case MemoryRegion.Shared: word = context.LoadShared(offset); break;
  304. }
  305. value = InsertSmallInt(context, op.Size, bitOffset, word, value);
  306. }
  307. switch (region)
  308. {
  309. case MemoryRegion.Global: context.StoreGlobal(offset, value); break;
  310. case MemoryRegion.Local: context.StoreLocal (offset, value); break;
  311. case MemoryRegion.Shared: context.StoreShared(offset, value); break;
  312. }
  313. if (rd.IsRZ)
  314. {
  315. break;
  316. }
  317. }
  318. }
  319. private static Operand GetBitOffset(EmitterContext context, Operand baseOffset)
  320. {
  321. // Note: byte offset = (baseOffset & 0b11) * 8.
  322. // Addresses should be always aligned to the integer type,
  323. // so we don't need to take unaligned addresses into account.
  324. return context.ShiftLeft(context.BitwiseAnd(baseOffset, Const(3)), Const(3));
  325. }
  326. private static Operand ExtractSmallInt(
  327. EmitterContext context,
  328. IntegerSize size,
  329. Operand bitOffset,
  330. Operand value)
  331. {
  332. value = context.ShiftRightU32(value, bitOffset);
  333. switch (size)
  334. {
  335. case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break;
  336. case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break;
  337. case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break;
  338. case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break;
  339. }
  340. return value;
  341. }
  342. private static Operand InsertSmallInt(
  343. EmitterContext context,
  344. IntegerSize size,
  345. Operand bitOffset,
  346. Operand word,
  347. Operand value)
  348. {
  349. switch (size)
  350. {
  351. case IntegerSize.U8:
  352. case IntegerSize.S8:
  353. value = context.BitwiseAnd(value, Const(0xff));
  354. value = context.BitfieldInsert(word, value, bitOffset, Const(8));
  355. break;
  356. case IntegerSize.U16:
  357. case IntegerSize.S16:
  358. value = context.BitwiseAnd(value, Const(0xffff));
  359. value = context.BitfieldInsert(word, value, bitOffset, Const(16));
  360. break;
  361. }
  362. return value;
  363. }
  364. }
  365. }