InstEmitMemory.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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. Local,
  13. Shared
  14. }
  15. public static void Ald(EmitterContext context)
  16. {
  17. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  18. Operand primVertex = context.Copy(GetSrcC(context));
  19. for (int index = 0; index < op.Count; index++)
  20. {
  21. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  22. if (rd.IsRZ)
  23. {
  24. break;
  25. }
  26. Operand src = Attribute(op.AttributeOffset + index * 4);
  27. context.Copy(Register(rd), context.LoadAttribute(src, primVertex));
  28. }
  29. }
  30. public static void Ast(EmitterContext context)
  31. {
  32. OpCodeAttribute op = (OpCodeAttribute)context.CurrOp;
  33. for (int index = 0; index < op.Count; index++)
  34. {
  35. if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex)
  36. {
  37. break;
  38. }
  39. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  40. Operand dest = Attribute(op.AttributeOffset + index * 4);
  41. context.Copy(dest, Register(rd));
  42. }
  43. }
  44. public static void Atoms(EmitterContext context)
  45. {
  46. OpCodeAtom op = (OpCodeAtom)context.CurrOp;
  47. Operand offset = context.ShiftRightU32(GetSrcA(context), Const(2));
  48. offset = context.IAdd(offset, Const(op.Offset));
  49. Operand value = GetSrcB(context);
  50. Operand res = EmitAtomicOp(
  51. context,
  52. Instruction.MrShared,
  53. op.AtomicOp,
  54. op.Type,
  55. offset,
  56. Const(0),
  57. value);
  58. context.Copy(GetDest(context), res);
  59. }
  60. public static void Bar(EmitterContext context)
  61. {
  62. OpCodeBarrier op = (OpCodeBarrier)context.CurrOp;
  63. // TODO: Support other modes.
  64. if (op.Mode == BarrierMode.Sync)
  65. {
  66. context.Barrier();
  67. }
  68. }
  69. public static void Ipa(EmitterContext context)
  70. {
  71. OpCodeIpa op = (OpCodeIpa)context.CurrOp;
  72. InterpolationQualifier iq = InterpolationQualifier.None;
  73. switch (op.Mode)
  74. {
  75. case InterpolationMode.Pass: iq = InterpolationQualifier.NoPerspective; break;
  76. }
  77. Operand srcA = Attribute(op.AttributeOffset, iq);
  78. Operand srcB = GetSrcB(context);
  79. Operand res = context.FPSaturate(srcA, op.Saturate);
  80. context.Copy(GetDest(context), res);
  81. }
  82. public static void Isberd(EmitterContext context)
  83. {
  84. // This instruction performs a load from ISBE memory,
  85. // however it seems to be only used to get some vertex
  86. // input data, so we instead propagate the offset so that
  87. // it can be used on the attribute load.
  88. context.Copy(GetDest(context), GetSrcA(context));
  89. }
  90. public static void Ld(EmitterContext context)
  91. {
  92. EmitLoad(context, MemoryRegion.Local);
  93. }
  94. public static void Ldc(EmitterContext context)
  95. {
  96. OpCodeLdc op = (OpCodeLdc)context.CurrOp;
  97. if (op.Size > IntegerSize.B64)
  98. {
  99. // TODO: Warning.
  100. }
  101. bool isSmallInt = op.Size < IntegerSize.B32;
  102. int count = op.Size == IntegerSize.B64 ? 2 : 1;
  103. Operand wordOffset = context.ShiftRightU32(GetSrcA(context), Const(2));
  104. wordOffset = context.IAdd(wordOffset, Const(op.Offset));
  105. Operand bitOffset = GetBitOffset(context, GetSrcA(context));
  106. for (int index = 0; index < count; index++)
  107. {
  108. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  109. if (rd.IsRZ)
  110. {
  111. break;
  112. }
  113. Operand offset = context.IAdd(wordOffset, Const(index));
  114. Operand value = context.LoadConstant(Const(op.Slot), offset);
  115. if (isSmallInt)
  116. {
  117. value = ExtractSmallInt(context, op.Size, wordOffset, value);
  118. }
  119. context.Copy(Register(rd), value);
  120. }
  121. }
  122. public static void Ldg(EmitterContext context)
  123. {
  124. EmitLoadGlobal(context);
  125. }
  126. public static void Lds(EmitterContext context)
  127. {
  128. EmitLoad(context, MemoryRegion.Shared);
  129. }
  130. public static void Membar(EmitterContext context)
  131. {
  132. OpCodeMemoryBarrier op = (OpCodeMemoryBarrier)context.CurrOp;
  133. if (op.Level == BarrierLevel.Cta)
  134. {
  135. context.GroupMemoryBarrier();
  136. }
  137. else
  138. {
  139. context.MemoryBarrier();
  140. }
  141. }
  142. public static void Out(EmitterContext context)
  143. {
  144. OpCode op = context.CurrOp;
  145. bool emit = op.RawOpCode.Extract(39);
  146. bool cut = op.RawOpCode.Extract(40);
  147. if (!(emit || cut))
  148. {
  149. // TODO: Warning.
  150. }
  151. if (emit)
  152. {
  153. context.EmitVertex();
  154. }
  155. if (cut)
  156. {
  157. context.EndPrimitive();
  158. }
  159. }
  160. public static void Red(EmitterContext context)
  161. {
  162. OpCodeRed op = (OpCodeRed)context.CurrOp;
  163. (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
  164. EmitAtomicOp(
  165. context,
  166. Instruction.MrGlobal,
  167. op.AtomicOp,
  168. op.Type,
  169. addrLow,
  170. addrHigh,
  171. GetDest(context));
  172. }
  173. public static void St(EmitterContext context)
  174. {
  175. EmitStore(context, MemoryRegion.Local);
  176. }
  177. public static void Stg(EmitterContext context)
  178. {
  179. EmitStoreGlobal(context);
  180. }
  181. public static void Sts(EmitterContext context)
  182. {
  183. EmitStore(context, MemoryRegion.Shared);
  184. }
  185. private static Operand EmitAtomicOp(
  186. EmitterContext context,
  187. Instruction mr,
  188. AtomicOp op,
  189. ReductionType type,
  190. Operand addrLow,
  191. Operand addrHigh,
  192. Operand value)
  193. {
  194. Operand res = Const(0);
  195. switch (op)
  196. {
  197. case AtomicOp.Add:
  198. if (type == ReductionType.S32 || type == ReductionType.U32)
  199. {
  200. res = context.AtomicAdd(mr, addrLow, addrHigh, value);
  201. }
  202. else
  203. {
  204. // Not supported or invalid.
  205. }
  206. break;
  207. case AtomicOp.BitwiseAnd:
  208. if (type == ReductionType.S32 || type == ReductionType.U32)
  209. {
  210. res = context.AtomicAnd(mr, addrLow, addrHigh, value);
  211. }
  212. else
  213. {
  214. // Not supported or invalid.
  215. }
  216. break;
  217. case AtomicOp.BitwiseExclusiveOr:
  218. if (type == ReductionType.S32 || type == ReductionType.U32)
  219. {
  220. res = context.AtomicXor(mr, addrLow, addrHigh, value);
  221. }
  222. else
  223. {
  224. // Not supported or invalid.
  225. }
  226. break;
  227. case AtomicOp.BitwiseOr:
  228. if (type == ReductionType.S32 || type == ReductionType.U32)
  229. {
  230. res = context.AtomicOr(mr, addrLow, addrHigh, value);
  231. }
  232. else
  233. {
  234. // Not supported or invalid.
  235. }
  236. break;
  237. case AtomicOp.Maximum:
  238. if (type == ReductionType.S32)
  239. {
  240. res = context.AtomicMaxS32(mr, addrLow, addrHigh, value);
  241. }
  242. else if (type == ReductionType.U32)
  243. {
  244. res = context.AtomicMaxU32(mr, addrLow, addrHigh, value);
  245. }
  246. else
  247. {
  248. // Not supported or invalid.
  249. }
  250. break;
  251. case AtomicOp.Minimum:
  252. if (type == ReductionType.S32)
  253. {
  254. res = context.AtomicMinS32(mr, addrLow, addrHigh, value);
  255. }
  256. else if (type == ReductionType.U32)
  257. {
  258. res = context.AtomicMinU32(mr, addrLow, addrHigh, value);
  259. }
  260. else
  261. {
  262. // Not supported or invalid.
  263. }
  264. break;
  265. }
  266. return res;
  267. }
  268. private static void EmitLoad(EmitterContext context, MemoryRegion region)
  269. {
  270. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  271. if (op.Size > IntegerSize.B128)
  272. {
  273. // TODO: Warning.
  274. }
  275. bool isSmallInt = op.Size < IntegerSize.B32;
  276. int count = 1;
  277. switch (op.Size)
  278. {
  279. case IntegerSize.B64: count = 2; break;
  280. case IntegerSize.B128: count = 4; break;
  281. }
  282. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  283. // Word offset = byte offset / 4 (one word = 4 bytes).
  284. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  285. Operand bitOffset = GetBitOffset(context, baseOffset);
  286. for (int index = 0; index < count; index++)
  287. {
  288. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  289. if (rd.IsRZ)
  290. {
  291. break;
  292. }
  293. Operand offset = context.IAdd(wordOffset, Const(index));
  294. Operand value = null;
  295. switch (region)
  296. {
  297. case MemoryRegion.Local: value = context.LoadLocal (offset); break;
  298. case MemoryRegion.Shared: value = context.LoadShared(offset); break;
  299. }
  300. if (isSmallInt)
  301. {
  302. value = ExtractSmallInt(context, op.Size, bitOffset, value);
  303. }
  304. context.Copy(Register(rd), value);
  305. }
  306. }
  307. private static void EmitLoadGlobal(EmitterContext context)
  308. {
  309. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  310. bool isSmallInt = op.Size < IntegerSize.B32;
  311. int count = GetVectorCount(op.Size);
  312. (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
  313. Operand bitOffset = GetBitOffset(context, addrLow);
  314. for (int index = 0; index < count; index++)
  315. {
  316. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  317. if (rd.IsRZ)
  318. {
  319. break;
  320. }
  321. Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh);
  322. if (isSmallInt)
  323. {
  324. value = ExtractSmallInt(context, op.Size, bitOffset, value);
  325. }
  326. context.Copy(Register(rd), value);
  327. }
  328. }
  329. private static void EmitStore(EmitterContext context, MemoryRegion region)
  330. {
  331. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  332. if (op.Size > IntegerSize.B128)
  333. {
  334. // TODO: Warning.
  335. }
  336. bool isSmallInt = op.Size < IntegerSize.B32;
  337. int count = 1;
  338. switch (op.Size)
  339. {
  340. case IntegerSize.B64: count = 2; break;
  341. case IntegerSize.B128: count = 4; break;
  342. }
  343. Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset));
  344. Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2));
  345. Operand bitOffset = GetBitOffset(context, baseOffset);
  346. for (int index = 0; index < count; index++)
  347. {
  348. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  349. Operand value = Register(rd);
  350. Operand offset = context.IAdd(wordOffset, Const(index));
  351. if (isSmallInt)
  352. {
  353. Operand word = null;
  354. switch (region)
  355. {
  356. case MemoryRegion.Local: word = context.LoadLocal (offset); break;
  357. case MemoryRegion.Shared: word = context.LoadShared(offset); break;
  358. }
  359. value = InsertSmallInt(context, op.Size, bitOffset, word, value);
  360. }
  361. switch (region)
  362. {
  363. case MemoryRegion.Local: context.StoreLocal (offset, value); break;
  364. case MemoryRegion.Shared: context.StoreShared(offset, value); break;
  365. }
  366. if (rd.IsRZ)
  367. {
  368. break;
  369. }
  370. }
  371. }
  372. private static void EmitStoreGlobal(EmitterContext context)
  373. {
  374. OpCodeMemory op = (OpCodeMemory)context.CurrOp;
  375. bool isSmallInt = op.Size < IntegerSize.B32;
  376. int count = GetVectorCount(op.Size);
  377. (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, op.Offset);
  378. Operand bitOffset = GetBitOffset(context, addrLow);
  379. for (int index = 0; index < count; index++)
  380. {
  381. Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr);
  382. Operand value = Register(rd);
  383. if (isSmallInt)
  384. {
  385. Operand word = context.LoadGlobal(addrLow, addrHigh);
  386. value = InsertSmallInt(context, op.Size, bitOffset, word, value);
  387. }
  388. context.StoreGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh, value);
  389. if (rd.IsRZ)
  390. {
  391. break;
  392. }
  393. }
  394. }
  395. private static int GetVectorCount(IntegerSize size)
  396. {
  397. switch (size)
  398. {
  399. case IntegerSize.B64:
  400. return 2;
  401. case IntegerSize.B128:
  402. case IntegerSize.UB128:
  403. return 4;
  404. }
  405. return 1;
  406. }
  407. private static (Operand, Operand) Get40BitsAddress(
  408. EmitterContext context,
  409. Register ra,
  410. bool extended,
  411. int offset)
  412. {
  413. Operand addrLow = GetSrcA(context);
  414. Operand addrHigh;
  415. if (extended && !ra.IsRZ)
  416. {
  417. addrHigh = Register(ra.Index + 1, RegisterType.Gpr);
  418. }
  419. else
  420. {
  421. addrHigh = Const(0);
  422. }
  423. Operand offs = Const(offset);
  424. addrLow = context.IAdd(addrLow, offs);
  425. if (extended)
  426. {
  427. Operand carry = context.ICompareLessUnsigned(addrLow, offs);
  428. addrHigh = context.IAdd(addrHigh, context.ConditionalSelect(carry, Const(1), Const(0)));
  429. }
  430. return (addrLow, addrHigh);
  431. }
  432. private static Operand GetBitOffset(EmitterContext context, Operand baseOffset)
  433. {
  434. // Note: bit offset = (baseOffset & 0b11) * 8.
  435. // Addresses should be always aligned to the integer type,
  436. // so we don't need to take unaligned addresses into account.
  437. return context.ShiftLeft(context.BitwiseAnd(baseOffset, Const(3)), Const(3));
  438. }
  439. private static Operand ExtractSmallInt(
  440. EmitterContext context,
  441. IntegerSize size,
  442. Operand bitOffset,
  443. Operand value)
  444. {
  445. value = context.ShiftRightU32(value, bitOffset);
  446. switch (size)
  447. {
  448. case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break;
  449. case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break;
  450. case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break;
  451. case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break;
  452. }
  453. return value;
  454. }
  455. private static Operand InsertSmallInt(
  456. EmitterContext context,
  457. IntegerSize size,
  458. Operand bitOffset,
  459. Operand word,
  460. Operand value)
  461. {
  462. switch (size)
  463. {
  464. case IntegerSize.U8:
  465. case IntegerSize.S8:
  466. value = context.BitwiseAnd(value, Const(0xff));
  467. value = context.BitfieldInsert(word, value, bitOffset, Const(8));
  468. break;
  469. case IntegerSize.U16:
  470. case IntegerSize.S16:
  471. value = context.BitwiseAnd(value, Const(0xffff));
  472. value = context.BitfieldInsert(word, value, bitOffset, Const(16));
  473. break;
  474. }
  475. return value;
  476. }
  477. }
  478. }