MacroJitCompiler.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. using Ryujinx.Graphics.Device;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Reflection.Emit;
  5. namespace Ryujinx.Graphics.Gpu.Engine.MME
  6. {
  7. /// <summary>
  8. /// Represents a Macro Just-in-Time compiler.
  9. /// </summary>R
  10. class MacroJitCompiler
  11. {
  12. private readonly DynamicMethod _meth;
  13. private readonly ILGenerator _ilGen;
  14. private readonly LocalBuilder[] _gprs;
  15. private readonly LocalBuilder _carry;
  16. private readonly LocalBuilder _methAddr;
  17. private readonly LocalBuilder _methIncr;
  18. /// <summary>
  19. /// Creates a new instance of the Macro Just-in-Time compiler.
  20. /// </summary>
  21. public MacroJitCompiler()
  22. {
  23. _meth = new DynamicMethod("Macro", typeof(void), new Type[] { typeof(MacroJitContext), typeof(IDeviceState), typeof(int) });
  24. _ilGen = _meth.GetILGenerator();
  25. _gprs = new LocalBuilder[8];
  26. for (int i = 1; i < 8; i++)
  27. {
  28. _gprs[i] = _ilGen.DeclareLocal(typeof(int));
  29. }
  30. _carry = _ilGen.DeclareLocal(typeof(int));
  31. _methAddr = _ilGen.DeclareLocal(typeof(int));
  32. _methIncr = _ilGen.DeclareLocal(typeof(int));
  33. _ilGen.Emit(OpCodes.Ldarg_2);
  34. _ilGen.Emit(OpCodes.Stloc, _gprs[1]);
  35. }
  36. public delegate void MacroExecute(MacroJitContext context, IDeviceState state, int arg0);
  37. /// <summary>
  38. /// Translates a new piece of GPU Macro code into host executable code.
  39. /// </summary>
  40. /// <param name="code">Code to be translated</param>
  41. /// <returns>Delegate of the host compiled code</returns>
  42. public MacroExecute Compile(ReadOnlySpan<int> code)
  43. {
  44. Dictionary<int, Label> labels = new Dictionary<int, Label>();
  45. int lastTarget = 0;
  46. int i;
  47. // Collect all branch targets.
  48. for (i = 0; i < code.Length; i++)
  49. {
  50. int opCode = code[i];
  51. if ((opCode & 7) == 7)
  52. {
  53. int target = i + (opCode >> 14);
  54. if (!labels.ContainsKey(target))
  55. {
  56. labels.Add(target, _ilGen.DefineLabel());
  57. }
  58. if (lastTarget < target)
  59. {
  60. lastTarget = target;
  61. }
  62. }
  63. bool exit = (opCode & 0x80) != 0;
  64. if (exit && i >= lastTarget)
  65. {
  66. break;
  67. }
  68. }
  69. // Code generation.
  70. for (i = 0; i < code.Length; i++)
  71. {
  72. if (labels.TryGetValue(i, out Label label))
  73. {
  74. _ilGen.MarkLabel(label);
  75. }
  76. Emit(code, i, labels);
  77. int opCode = code[i];
  78. bool exit = (opCode & 0x80) != 0;
  79. if (exit)
  80. {
  81. Emit(code, i + 1, labels);
  82. _ilGen.Emit(OpCodes.Ret);
  83. if (i >= lastTarget)
  84. {
  85. break;
  86. }
  87. }
  88. }
  89. if (i == code.Length)
  90. {
  91. _ilGen.Emit(OpCodes.Ret);
  92. }
  93. return _meth.CreateDelegate<MacroExecute>();
  94. }
  95. /// <summary>
  96. /// Emits IL equivalent to the Macro instruction at a given offset.
  97. /// </summary>
  98. /// <param name="code">GPU Macro code</param>
  99. /// <param name="offset">Offset, in words, where the instruction is located</param>
  100. /// <param name="labels">Labels for Macro branch targets, used by branch instructions</param>
  101. private void Emit(ReadOnlySpan<int> code, int offset, Dictionary<int, Label> labels)
  102. {
  103. int opCode = code[offset];
  104. if ((opCode & 7) < 7)
  105. {
  106. // Operation produces a value.
  107. AssignmentOperation asgOp = (AssignmentOperation)((opCode >> 4) & 7);
  108. EmitAluOp(opCode);
  109. switch (asgOp)
  110. {
  111. // Fetch parameter and ignore result.
  112. case AssignmentOperation.IgnoreAndFetch:
  113. _ilGen.Emit(OpCodes.Pop);
  114. EmitFetchParam();
  115. EmitStoreDstGpr(opCode);
  116. break;
  117. // Move result.
  118. case AssignmentOperation.Move:
  119. EmitStoreDstGpr(opCode);
  120. break;
  121. // Move result and use as Method Address.
  122. case AssignmentOperation.MoveAndSetMaddr:
  123. _ilGen.Emit(OpCodes.Dup);
  124. EmitStoreDstGpr(opCode);
  125. EmitStoreMethAddr();
  126. break;
  127. // Fetch parameter and send result.
  128. case AssignmentOperation.FetchAndSend:
  129. EmitFetchParam();
  130. EmitStoreDstGpr(opCode);
  131. EmitSend();
  132. break;
  133. // Move and send result.
  134. case AssignmentOperation.MoveAndSend:
  135. _ilGen.Emit(OpCodes.Dup);
  136. EmitStoreDstGpr(opCode);
  137. EmitSend();
  138. break;
  139. // Fetch parameter and use result as Method Address.
  140. case AssignmentOperation.FetchAndSetMaddr:
  141. EmitFetchParam();
  142. EmitStoreDstGpr(opCode);
  143. EmitStoreMethAddr();
  144. break;
  145. // Move result and use as Method Address, then fetch and send parameter.
  146. case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
  147. _ilGen.Emit(OpCodes.Dup);
  148. EmitStoreDstGpr(opCode);
  149. EmitStoreMethAddr();
  150. EmitFetchParam();
  151. EmitSend();
  152. break;
  153. // Move result and use as Method Address, then send bits 17:12 of result.
  154. case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
  155. _ilGen.Emit(OpCodes.Dup);
  156. _ilGen.Emit(OpCodes.Dup);
  157. EmitStoreDstGpr(opCode);
  158. EmitStoreMethAddr();
  159. _ilGen.Emit(OpCodes.Ldc_I4, 12);
  160. _ilGen.Emit(OpCodes.Shr_Un);
  161. _ilGen.Emit(OpCodes.Ldc_I4, 0x3f);
  162. _ilGen.Emit(OpCodes.And);
  163. EmitSend();
  164. break;
  165. }
  166. }
  167. else
  168. {
  169. // Branch.
  170. bool onNotZero = ((opCode >> 4) & 1) != 0;
  171. EmitLoadGprA(opCode);
  172. Label lblSkip = _ilGen.DefineLabel();
  173. if (onNotZero)
  174. {
  175. _ilGen.Emit(OpCodes.Brfalse, lblSkip);
  176. }
  177. else
  178. {
  179. _ilGen.Emit(OpCodes.Brtrue, lblSkip);
  180. }
  181. bool noDelays = (opCode & 0x20) != 0;
  182. if (!noDelays)
  183. {
  184. Emit(code, offset + 1, labels);
  185. }
  186. int target = offset + (opCode >> 14);
  187. _ilGen.Emit(OpCodes.Br, labels[target]);
  188. _ilGen.MarkLabel(lblSkip);
  189. }
  190. }
  191. /// <summary>
  192. /// Emits IL for a Arithmetic and Logic Unit instruction.
  193. /// </summary>
  194. /// <param name="opCode">Instruction to be translated</param>
  195. /// <exception cref="InvalidOperationException">Throw when the instruction encoding is invalid</exception>
  196. private void EmitAluOp(int opCode)
  197. {
  198. AluOperation op = (AluOperation)(opCode & 7);
  199. switch (op)
  200. {
  201. case AluOperation.AluReg:
  202. EmitAluOp((AluRegOperation)((opCode >> 17) & 0x1f), opCode);
  203. break;
  204. case AluOperation.AddImmediate:
  205. EmitLoadGprA(opCode);
  206. EmitLoadImm(opCode);
  207. _ilGen.Emit(OpCodes.Add);
  208. break;
  209. case AluOperation.BitfieldReplace:
  210. case AluOperation.BitfieldExtractLslImm:
  211. case AluOperation.BitfieldExtractLslReg:
  212. int bfSrcBit = (opCode >> 17) & 0x1f;
  213. int bfSize = (opCode >> 22) & 0x1f;
  214. int bfDstBit = (opCode >> 27) & 0x1f;
  215. int bfMask = (1 << bfSize) - 1;
  216. switch (op)
  217. {
  218. case AluOperation.BitfieldReplace:
  219. EmitLoadGprB(opCode);
  220. _ilGen.Emit(OpCodes.Ldc_I4, bfSrcBit);
  221. _ilGen.Emit(OpCodes.Shr_Un);
  222. _ilGen.Emit(OpCodes.Ldc_I4, bfMask);
  223. _ilGen.Emit(OpCodes.And);
  224. _ilGen.Emit(OpCodes.Ldc_I4, bfDstBit);
  225. _ilGen.Emit(OpCodes.Shl);
  226. EmitLoadGprA(opCode);
  227. _ilGen.Emit(OpCodes.Ldc_I4, ~(bfMask << bfDstBit));
  228. _ilGen.Emit(OpCodes.And);
  229. _ilGen.Emit(OpCodes.Or);
  230. break;
  231. case AluOperation.BitfieldExtractLslImm:
  232. EmitLoadGprB(opCode);
  233. EmitLoadGprA(opCode);
  234. _ilGen.Emit(OpCodes.Shr_Un);
  235. _ilGen.Emit(OpCodes.Ldc_I4, bfMask);
  236. _ilGen.Emit(OpCodes.And);
  237. _ilGen.Emit(OpCodes.Ldc_I4, bfDstBit);
  238. _ilGen.Emit(OpCodes.Shl);
  239. break;
  240. case AluOperation.BitfieldExtractLslReg:
  241. EmitLoadGprB(opCode);
  242. _ilGen.Emit(OpCodes.Ldc_I4, bfSrcBit);
  243. _ilGen.Emit(OpCodes.Shr_Un);
  244. _ilGen.Emit(OpCodes.Ldc_I4, bfMask);
  245. _ilGen.Emit(OpCodes.And);
  246. EmitLoadGprA(opCode);
  247. _ilGen.Emit(OpCodes.Shl);
  248. break;
  249. }
  250. break;
  251. case AluOperation.ReadImmediate:
  252. _ilGen.Emit(OpCodes.Ldarg_1);
  253. EmitLoadGprA(opCode);
  254. EmitLoadImm(opCode);
  255. _ilGen.Emit(OpCodes.Add);
  256. _ilGen.Emit(OpCodes.Call, typeof(MacroJitContext).GetMethod(nameof(MacroJitContext.Read)));
  257. break;
  258. default:
  259. throw new InvalidOperationException($"Invalid operation \"{op}\" on instruction 0x{opCode:X8}.");
  260. }
  261. }
  262. /// <summary>
  263. /// Emits IL for a binary Arithmetic and Logic Unit instruction.
  264. /// </summary>
  265. /// <param name="aluOp">Arithmetic and Logic Unit instruction</param>
  266. /// <param name="opCode">Raw instruction</param>
  267. /// <exception cref="InvalidOperationException">Throw when the instruction encoding is invalid</exception>
  268. private void EmitAluOp(AluRegOperation aluOp, int opCode)
  269. {
  270. switch (aluOp)
  271. {
  272. case AluRegOperation.Add:
  273. EmitLoadGprA(opCode);
  274. _ilGen.Emit(OpCodes.Conv_U8);
  275. EmitLoadGprB(opCode);
  276. _ilGen.Emit(OpCodes.Conv_U8);
  277. _ilGen.Emit(OpCodes.Add);
  278. _ilGen.Emit(OpCodes.Dup);
  279. _ilGen.Emit(OpCodes.Ldc_I8, 0xffffffffL);
  280. _ilGen.Emit(OpCodes.Cgt_Un);
  281. _ilGen.Emit(OpCodes.Stloc, _carry);
  282. _ilGen.Emit(OpCodes.Conv_U4);
  283. break;
  284. case AluRegOperation.AddWithCarry:
  285. EmitLoadGprA(opCode);
  286. _ilGen.Emit(OpCodes.Conv_U8);
  287. EmitLoadGprB(opCode);
  288. _ilGen.Emit(OpCodes.Conv_U8);
  289. _ilGen.Emit(OpCodes.Ldloc_S, _carry);
  290. _ilGen.Emit(OpCodes.Conv_U8);
  291. _ilGen.Emit(OpCodes.Add);
  292. _ilGen.Emit(OpCodes.Add);
  293. _ilGen.Emit(OpCodes.Dup);
  294. _ilGen.Emit(OpCodes.Ldc_I8, 0xffffffffL);
  295. _ilGen.Emit(OpCodes.Cgt_Un);
  296. _ilGen.Emit(OpCodes.Stloc, _carry);
  297. _ilGen.Emit(OpCodes.Conv_U4);
  298. break;
  299. case AluRegOperation.Subtract:
  300. EmitLoadGprA(opCode);
  301. _ilGen.Emit(OpCodes.Conv_U8);
  302. EmitLoadGprB(opCode);
  303. _ilGen.Emit(OpCodes.Conv_U8);
  304. _ilGen.Emit(OpCodes.Sub);
  305. _ilGen.Emit(OpCodes.Dup);
  306. _ilGen.Emit(OpCodes.Ldc_I8, 0x100000000L);
  307. _ilGen.Emit(OpCodes.Clt_Un);
  308. _ilGen.Emit(OpCodes.Stloc, _carry);
  309. _ilGen.Emit(OpCodes.Conv_U4);
  310. break;
  311. case AluRegOperation.SubtractWithBorrow:
  312. EmitLoadGprA(opCode);
  313. _ilGen.Emit(OpCodes.Conv_U8);
  314. EmitLoadGprB(opCode);
  315. _ilGen.Emit(OpCodes.Conv_U8);
  316. _ilGen.Emit(OpCodes.Ldc_I4_1);
  317. _ilGen.Emit(OpCodes.Ldloc_S, _carry);
  318. _ilGen.Emit(OpCodes.Sub);
  319. _ilGen.Emit(OpCodes.Conv_U8);
  320. _ilGen.Emit(OpCodes.Sub);
  321. _ilGen.Emit(OpCodes.Sub);
  322. _ilGen.Emit(OpCodes.Dup);
  323. _ilGen.Emit(OpCodes.Ldc_I8, 0x100000000L);
  324. _ilGen.Emit(OpCodes.Clt_Un);
  325. _ilGen.Emit(OpCodes.Stloc, _carry);
  326. _ilGen.Emit(OpCodes.Conv_U4);
  327. break;
  328. case AluRegOperation.BitwiseExclusiveOr:
  329. EmitLoadGprA(opCode);
  330. EmitLoadGprB(opCode);
  331. _ilGen.Emit(OpCodes.Xor);
  332. break;
  333. case AluRegOperation.BitwiseOr:
  334. EmitLoadGprA(opCode);
  335. EmitLoadGprB(opCode);
  336. _ilGen.Emit(OpCodes.Or);
  337. break;
  338. case AluRegOperation.BitwiseAnd:
  339. EmitLoadGprA(opCode);
  340. EmitLoadGprB(opCode);
  341. _ilGen.Emit(OpCodes.And);
  342. break;
  343. case AluRegOperation.BitwiseAndNot:
  344. EmitLoadGprA(opCode);
  345. EmitLoadGprB(opCode);
  346. _ilGen.Emit(OpCodes.Not);
  347. _ilGen.Emit(OpCodes.And);
  348. break;
  349. case AluRegOperation.BitwiseNotAnd:
  350. EmitLoadGprA(opCode);
  351. EmitLoadGprB(opCode);
  352. _ilGen.Emit(OpCodes.And);
  353. _ilGen.Emit(OpCodes.Not);
  354. break;
  355. default:
  356. throw new InvalidOperationException($"Invalid operation \"{aluOp}\" on instruction 0x{opCode:X8}.");
  357. }
  358. }
  359. /// <summary>
  360. /// Loads a immediate value on the IL evaluation stack.
  361. /// </summary>
  362. /// <param name="opCode">Instruction from where the immediate should be extracted</param>
  363. private void EmitLoadImm(int opCode)
  364. {
  365. // Note: The immediate is signed, the sign-extension is intended here.
  366. _ilGen.Emit(OpCodes.Ldc_I4, opCode >> 14);
  367. }
  368. /// <summary>
  369. /// Loads a value from the General Purpose register specified as first operand on the IL evaluation stack.
  370. /// </summary>
  371. /// <param name="opCode">Instruction from where the register number should be extracted</param>
  372. private void EmitLoadGprA(int opCode)
  373. {
  374. EmitLoadGpr((opCode >> 11) & 7);
  375. }
  376. /// <summary>
  377. /// Loads a value from the General Purpose register specified as second operand on the IL evaluation stack.
  378. /// </summary>
  379. /// <param name="opCode">Instruction from where the register number should be extracted</param>
  380. private void EmitLoadGprB(int opCode)
  381. {
  382. EmitLoadGpr((opCode >> 14) & 7);
  383. }
  384. /// <summary>
  385. /// Loads a value a General Purpose register on the IL evaluation stack.
  386. /// </summary>
  387. /// <remarks>
  388. /// Register number 0 has a hardcoded value of 0.
  389. /// </remarks>
  390. /// <param name="index">Register number</param>
  391. private void EmitLoadGpr(int index)
  392. {
  393. if (index == 0)
  394. {
  395. _ilGen.Emit(OpCodes.Ldc_I4_0);
  396. }
  397. else
  398. {
  399. _ilGen.Emit(OpCodes.Ldloc_S, _gprs[index]);
  400. }
  401. }
  402. /// <summary>
  403. /// Emits a call to the method that fetches an argument from the arguments FIFO.
  404. /// The argument is pushed into the IL evaluation stack.
  405. /// </summary>
  406. private void EmitFetchParam()
  407. {
  408. _ilGen.Emit(OpCodes.Ldarg_0);
  409. _ilGen.Emit(OpCodes.Call, typeof(MacroJitContext).GetMethod(nameof(MacroJitContext.FetchParam)));
  410. }
  411. /// <summary>
  412. /// Stores the value on the top of the IL evaluation stack into a General Purpose register.
  413. /// </summary>
  414. /// <remarks>
  415. /// Register number 0 does not exist, reads are hardcoded to 0, and writes are simply discarded.
  416. /// </remarks>
  417. /// <param name="opCode">Instruction from where the register number should be extracted</param>
  418. private void EmitStoreDstGpr(int opCode)
  419. {
  420. int index = (opCode >> 8) & 7;
  421. if (index == 0)
  422. {
  423. _ilGen.Emit(OpCodes.Pop);
  424. }
  425. else
  426. {
  427. _ilGen.Emit(OpCodes.Stloc_S, _gprs[index]);
  428. }
  429. }
  430. /// <summary>
  431. /// Stores the value on the top of the IL evaluation stack as method address.
  432. /// This will be used on subsequent send calls as the destination method address.
  433. /// Additionally, the 6 bits starting at bit 12 will be used as increment value,
  434. /// added to the method address after each sent value.
  435. /// </summary>
  436. private void EmitStoreMethAddr()
  437. {
  438. _ilGen.Emit(OpCodes.Dup);
  439. _ilGen.Emit(OpCodes.Ldc_I4, 0xfff);
  440. _ilGen.Emit(OpCodes.And);
  441. _ilGen.Emit(OpCodes.Stloc_S, _methAddr);
  442. _ilGen.Emit(OpCodes.Ldc_I4, 12);
  443. _ilGen.Emit(OpCodes.Shr_Un);
  444. _ilGen.Emit(OpCodes.Ldc_I4, 0x3f);
  445. _ilGen.Emit(OpCodes.And);
  446. _ilGen.Emit(OpCodes.Stloc_S, _methIncr);
  447. }
  448. /// <summary>
  449. /// Sends the value on the top of the IL evaluation stack to the GPU,
  450. /// using the current method address.
  451. /// </summary>
  452. private void EmitSend()
  453. {
  454. _ilGen.Emit(OpCodes.Ldarg_1);
  455. _ilGen.Emit(OpCodes.Ldloc_S, _methAddr);
  456. _ilGen.Emit(OpCodes.Call, typeof(MacroJitContext).GetMethod(nameof(MacroJitContext.Send)));
  457. _ilGen.Emit(OpCodes.Ldloc_S, _methAddr);
  458. _ilGen.Emit(OpCodes.Ldloc_S, _methIncr);
  459. _ilGen.Emit(OpCodes.Add);
  460. _ilGen.Emit(OpCodes.Stloc_S, _methAddr);
  461. }
  462. }
  463. }