ILEmitterCtx.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.Instructions;
  3. using ChocolArm64.State;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. namespace ChocolArm64.Translation
  9. {
  10. class ILEmitterCtx
  11. {
  12. private TranslatorCache _cache;
  13. private Dictionary<long, ILLabel> _labels;
  14. private long _subPosition;
  15. private int _opcIndex;
  16. private Block _currBlock;
  17. public Block CurrBlock => _currBlock;
  18. public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex];
  19. private Dictionary<Block, ILBlock> _visitedBlocks;
  20. private Queue<Block> _branchTargets;
  21. private List<ILBlock> _ilBlocks;
  22. private ILBlock _ilBlock;
  23. private OpCode64 _optOpLastCompare;
  24. private OpCode64 _optOpLastFlagSet;
  25. //This is the index of the temporary register, used to store temporary
  26. //values needed by some functions, since IL doesn't have a swap instruction.
  27. //You can use any value here as long it doesn't conflict with the indices
  28. //for the other registers. Any value >= 64 or < 0 will do.
  29. private const int IntTmpIndex = -1;
  30. private const int RorTmpIndex = -2;
  31. private const int CmpOptTmp1Index = -3;
  32. private const int CmpOptTmp2Index = -4;
  33. private const int VecTmp1Index = -5;
  34. private const int VecTmp2Index = -6;
  35. public ILEmitterCtx(TranslatorCache cache, Block graph)
  36. {
  37. _cache = cache ?? throw new ArgumentNullException(nameof(cache));
  38. _currBlock = graph ?? throw new ArgumentNullException(nameof(graph));
  39. _labels = new Dictionary<long, ILLabel>();
  40. _visitedBlocks = new Dictionary<Block, ILBlock>();
  41. _visitedBlocks.Add(graph, new ILBlock());
  42. _branchTargets = new Queue<Block>();
  43. _ilBlocks = new List<ILBlock>();
  44. _subPosition = graph.Position;
  45. ResetBlockState();
  46. AdvanceOpCode();
  47. }
  48. public ILBlock[] GetILBlocks()
  49. {
  50. EmitAllOpCodes();
  51. return _ilBlocks.ToArray();
  52. }
  53. private void EmitAllOpCodes()
  54. {
  55. do
  56. {
  57. EmitOpCode();
  58. }
  59. while (AdvanceOpCode());
  60. }
  61. private void EmitOpCode()
  62. {
  63. if (_currBlock == null)
  64. {
  65. return;
  66. }
  67. if (_opcIndex == 0)
  68. {
  69. MarkLabel(GetLabel(_currBlock.Position));
  70. EmitSynchronization();
  71. }
  72. CurrOp.Emitter(this);
  73. _ilBlock.Add(new ILBarrier());
  74. }
  75. private void EmitSynchronization()
  76. {
  77. EmitLdarg(TranslatedSub.StateArgIdx);
  78. EmitLdc_I4(_currBlock.OpCodes.Count);
  79. EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
  80. EmitLdc_I4(0);
  81. ILLabel lblContinue = new ILLabel();
  82. Emit(OpCodes.Bne_Un_S, lblContinue);
  83. EmitLdc_I8(0);
  84. Emit(OpCodes.Ret);
  85. MarkLabel(lblContinue);
  86. }
  87. private bool AdvanceOpCode()
  88. {
  89. if (_currBlock == null)
  90. {
  91. return false;
  92. }
  93. while (++_opcIndex >= _currBlock.OpCodes.Count)
  94. {
  95. if (!AdvanceBlock())
  96. {
  97. return false;
  98. }
  99. ResetBlockState();
  100. }
  101. return true;
  102. }
  103. private bool AdvanceBlock()
  104. {
  105. if (_currBlock.Branch != null)
  106. {
  107. if (_visitedBlocks.TryAdd(_currBlock.Branch, _ilBlock.Branch))
  108. {
  109. _branchTargets.Enqueue(_currBlock.Branch);
  110. }
  111. }
  112. if (_currBlock.Next != null)
  113. {
  114. if (_visitedBlocks.TryAdd(_currBlock.Next, _ilBlock.Next))
  115. {
  116. _currBlock = _currBlock.Next;
  117. return true;
  118. }
  119. else
  120. {
  121. Emit(OpCodes.Br, GetLabel(_currBlock.Next.Position));
  122. }
  123. }
  124. return _branchTargets.TryDequeue(out _currBlock);
  125. }
  126. private void ResetBlockState()
  127. {
  128. _ilBlock = _visitedBlocks[_currBlock];
  129. _ilBlocks.Add(_ilBlock);
  130. _ilBlock.Next = GetOrCreateILBlock(_currBlock.Next);
  131. _ilBlock.Branch = GetOrCreateILBlock(_currBlock.Branch);
  132. _opcIndex = -1;
  133. _optOpLastFlagSet = null;
  134. _optOpLastCompare = null;
  135. }
  136. private ILBlock GetOrCreateILBlock(Block block)
  137. {
  138. if (block == null)
  139. {
  140. return null;
  141. }
  142. if (_visitedBlocks.TryGetValue(block, out ILBlock ilBlock))
  143. {
  144. return ilBlock;
  145. }
  146. return new ILBlock();
  147. }
  148. public bool TryOptEmitSubroutineCall()
  149. {
  150. if (_currBlock.Next == null)
  151. {
  152. return false;
  153. }
  154. if (CurrOp.Emitter != InstEmit.Bl)
  155. {
  156. return false;
  157. }
  158. if (!_cache.TryGetSubroutine(((OpCodeBImmAl64)CurrOp).Imm, out TranslatedSub subroutine))
  159. {
  160. return false;
  161. }
  162. for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++)
  163. {
  164. EmitLdarg(index);
  165. }
  166. foreach (Register reg in subroutine.SubArgs)
  167. {
  168. switch (reg.Type)
  169. {
  170. case RegisterType.Flag: Ldloc(reg.Index, IoType.Flag); break;
  171. case RegisterType.Int: Ldloc(reg.Index, IoType.Int); break;
  172. case RegisterType.Vector: Ldloc(reg.Index, IoType.Vector); break;
  173. }
  174. }
  175. EmitCall(subroutine.Method);
  176. subroutine.AddCaller(_subPosition);
  177. return true;
  178. }
  179. public void TryOptMarkCondWithoutCmp()
  180. {
  181. _optOpLastCompare = CurrOp;
  182. InstEmitAluHelper.EmitDataLoadOpers(this);
  183. Stloc(CmpOptTmp2Index, IoType.Int);
  184. Stloc(CmpOptTmp1Index, IoType.Int);
  185. }
  186. private Dictionary<Cond, OpCode> _branchOps = new Dictionary<Cond, OpCode>()
  187. {
  188. { Cond.Eq, OpCodes.Beq },
  189. { Cond.Ne, OpCodes.Bne_Un },
  190. { Cond.GeUn, OpCodes.Bge_Un },
  191. { Cond.LtUn, OpCodes.Blt_Un },
  192. { Cond.GtUn, OpCodes.Bgt_Un },
  193. { Cond.LeUn, OpCodes.Ble_Un },
  194. { Cond.Ge, OpCodes.Bge },
  195. { Cond.Lt, OpCodes.Blt },
  196. { Cond.Gt, OpCodes.Bgt },
  197. { Cond.Le, OpCodes.Ble }
  198. };
  199. public void EmitCondBranch(ILLabel target, Cond cond)
  200. {
  201. OpCode ilOp;
  202. int intCond = (int)cond;
  203. if (_optOpLastCompare != null &&
  204. _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
  205. {
  206. Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
  207. Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize);
  208. ilOp = _branchOps[cond];
  209. }
  210. else if (intCond < 14)
  211. {
  212. int condTrue = intCond >> 1;
  213. switch (condTrue)
  214. {
  215. case 0: EmitLdflg((int)PState.ZBit); break;
  216. case 1: EmitLdflg((int)PState.CBit); break;
  217. case 2: EmitLdflg((int)PState.NBit); break;
  218. case 3: EmitLdflg((int)PState.VBit); break;
  219. case 4:
  220. EmitLdflg((int)PState.CBit);
  221. EmitLdflg((int)PState.ZBit);
  222. Emit(OpCodes.Not);
  223. Emit(OpCodes.And);
  224. break;
  225. case 5:
  226. case 6:
  227. EmitLdflg((int)PState.NBit);
  228. EmitLdflg((int)PState.VBit);
  229. Emit(OpCodes.Ceq);
  230. if (condTrue == 6)
  231. {
  232. EmitLdflg((int)PState.ZBit);
  233. Emit(OpCodes.Not);
  234. Emit(OpCodes.And);
  235. }
  236. break;
  237. }
  238. ilOp = (intCond & 1) != 0
  239. ? OpCodes.Brfalse
  240. : OpCodes.Brtrue;
  241. }
  242. else
  243. {
  244. ilOp = OpCodes.Br;
  245. }
  246. Emit(ilOp, target);
  247. }
  248. public void EmitCast(IntType intType)
  249. {
  250. switch (intType)
  251. {
  252. case IntType.UInt8: Emit(OpCodes.Conv_U1); break;
  253. case IntType.UInt16: Emit(OpCodes.Conv_U2); break;
  254. case IntType.UInt32: Emit(OpCodes.Conv_U4); break;
  255. case IntType.UInt64: Emit(OpCodes.Conv_U8); break;
  256. case IntType.Int8: Emit(OpCodes.Conv_I1); break;
  257. case IntType.Int16: Emit(OpCodes.Conv_I2); break;
  258. case IntType.Int32: Emit(OpCodes.Conv_I4); break;
  259. case IntType.Int64: Emit(OpCodes.Conv_I8); break;
  260. }
  261. bool sz64 = CurrOp.RegisterSize != RegisterSize.Int32;
  262. if (sz64 == (intType == IntType.UInt64 ||
  263. intType == IntType.Int64))
  264. {
  265. return;
  266. }
  267. if (sz64)
  268. {
  269. Emit(intType >= IntType.Int8
  270. ? OpCodes.Conv_I8
  271. : OpCodes.Conv_U8);
  272. }
  273. else
  274. {
  275. Emit(OpCodes.Conv_U4);
  276. }
  277. }
  278. public void EmitLsl(int amount) => EmitILShift(amount, OpCodes.Shl);
  279. public void EmitLsr(int amount) => EmitILShift(amount, OpCodes.Shr_Un);
  280. public void EmitAsr(int amount) => EmitILShift(amount, OpCodes.Shr);
  281. private void EmitILShift(int amount, OpCode ilOp)
  282. {
  283. if (amount > 0)
  284. {
  285. EmitLdc_I4(amount);
  286. Emit(ilOp);
  287. }
  288. }
  289. public void EmitRor(int amount)
  290. {
  291. if (amount > 0)
  292. {
  293. Stloc(RorTmpIndex, IoType.Int);
  294. Ldloc(RorTmpIndex, IoType.Int);
  295. EmitLdc_I4(amount);
  296. Emit(OpCodes.Shr_Un);
  297. Ldloc(RorTmpIndex, IoType.Int);
  298. EmitLdc_I4(CurrOp.GetBitsCount() - amount);
  299. Emit(OpCodes.Shl);
  300. Emit(OpCodes.Or);
  301. }
  302. }
  303. public ILLabel GetLabel(long position)
  304. {
  305. if (!_labels.TryGetValue(position, out ILLabel output))
  306. {
  307. output = new ILLabel();
  308. _labels.Add(position, output);
  309. }
  310. return output;
  311. }
  312. public void MarkLabel(ILLabel label)
  313. {
  314. _ilBlock.Add(label);
  315. }
  316. public void Emit(OpCode ilOp)
  317. {
  318. _ilBlock.Add(new ILOpCode(ilOp));
  319. }
  320. public void Emit(OpCode ilOp, ILLabel label)
  321. {
  322. _ilBlock.Add(new ILOpCodeBranch(ilOp, label));
  323. }
  324. public void Emit(string text)
  325. {
  326. _ilBlock.Add(new ILOpCodeLog(text));
  327. }
  328. public void EmitLdarg(int index)
  329. {
  330. _ilBlock.Add(new ILOpCodeLoad(index, IoType.Arg));
  331. }
  332. public void EmitLdintzr(int index)
  333. {
  334. if (index != CpuThreadState.ZrIndex)
  335. {
  336. EmitLdint(index);
  337. }
  338. else
  339. {
  340. EmitLdc_I(0);
  341. }
  342. }
  343. public void EmitStintzr(int index)
  344. {
  345. if (index != CpuThreadState.ZrIndex)
  346. {
  347. EmitStint(index);
  348. }
  349. else
  350. {
  351. Emit(OpCodes.Pop);
  352. }
  353. }
  354. public void EmitLoadState()
  355. {
  356. if (_ilBlock.Next == null)
  357. {
  358. throw new InvalidOperationException("Can't load state for next block, because there's no next block.");
  359. }
  360. _ilBlock.Add(new ILOpCodeLoadState(_ilBlock.Next));
  361. }
  362. public void EmitStoreState()
  363. {
  364. _ilBlock.Add(new ILOpCodeStoreState(_ilBlock));
  365. }
  366. public void EmitLdtmp() => EmitLdint(IntTmpIndex);
  367. public void EmitSttmp() => EmitStint(IntTmpIndex);
  368. public void EmitLdvectmp() => EmitLdvec(VecTmp1Index);
  369. public void EmitStvectmp() => EmitStvec(VecTmp1Index);
  370. public void EmitLdvectmp2() => EmitLdvec(VecTmp2Index);
  371. public void EmitStvectmp2() => EmitStvec(VecTmp2Index);
  372. public void EmitLdint(int index) => Ldloc(index, IoType.Int);
  373. public void EmitStint(int index) => Stloc(index, IoType.Int);
  374. public void EmitLdvec(int index) => Ldloc(index, IoType.Vector);
  375. public void EmitStvec(int index) => Stloc(index, IoType.Vector);
  376. public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
  377. public void EmitStflg(int index)
  378. {
  379. _optOpLastFlagSet = CurrOp;
  380. Stloc(index, IoType.Flag);
  381. }
  382. private void Ldloc(int index, IoType ioType)
  383. {
  384. _ilBlock.Add(new ILOpCodeLoad(index, ioType, CurrOp.RegisterSize));
  385. }
  386. private void Ldloc(int index, IoType ioType, RegisterSize registerSize)
  387. {
  388. _ilBlock.Add(new ILOpCodeLoad(index, ioType, registerSize));
  389. }
  390. private void Stloc(int index, IoType ioType)
  391. {
  392. _ilBlock.Add(new ILOpCodeStore(index, ioType, CurrOp.RegisterSize));
  393. }
  394. public void EmitCallPropGet(Type objType, string propName)
  395. {
  396. if (objType == null)
  397. {
  398. throw new ArgumentNullException(nameof(objType));
  399. }
  400. if (propName == null)
  401. {
  402. throw new ArgumentNullException(nameof(propName));
  403. }
  404. EmitCall(objType.GetMethod($"get_{propName}"));
  405. }
  406. public void EmitCallPropSet(Type objType, string propName)
  407. {
  408. if (objType == null)
  409. {
  410. throw new ArgumentNullException(nameof(objType));
  411. }
  412. if (propName == null)
  413. {
  414. throw new ArgumentNullException(nameof(propName));
  415. }
  416. EmitCall(objType.GetMethod($"set_{propName}"));
  417. }
  418. public void EmitCall(Type objType, string mthdName)
  419. {
  420. if (objType == null)
  421. {
  422. throw new ArgumentNullException(nameof(objType));
  423. }
  424. if (mthdName == null)
  425. {
  426. throw new ArgumentNullException(nameof(mthdName));
  427. }
  428. EmitCall(objType.GetMethod(mthdName));
  429. }
  430. public void EmitPrivateCall(Type objType, string mthdName)
  431. {
  432. if (objType == null)
  433. {
  434. throw new ArgumentNullException(nameof(objType));
  435. }
  436. if (mthdName == null)
  437. {
  438. throw new ArgumentNullException(nameof(mthdName));
  439. }
  440. EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic));
  441. }
  442. public void EmitCall(MethodInfo mthdInfo)
  443. {
  444. if (mthdInfo == null)
  445. {
  446. throw new ArgumentNullException(nameof(mthdInfo));
  447. }
  448. _ilBlock.Add(new ILOpCodeCall(mthdInfo));
  449. }
  450. public void EmitLdc_I(long value)
  451. {
  452. if (CurrOp.RegisterSize == RegisterSize.Int32)
  453. {
  454. EmitLdc_I4((int)value);
  455. }
  456. else
  457. {
  458. EmitLdc_I8(value);
  459. }
  460. }
  461. public void EmitLdc_I4(int value)
  462. {
  463. _ilBlock.Add(new ILOpCodeConst(value));
  464. }
  465. public void EmitLdc_I8(long value)
  466. {
  467. _ilBlock.Add(new ILOpCodeConst(value));
  468. }
  469. public void EmitLdc_R4(float value)
  470. {
  471. _ilBlock.Add(new ILOpCodeConst(value));
  472. }
  473. public void EmitLdc_R8(double value)
  474. {
  475. _ilBlock.Add(new ILOpCodeConst(value));
  476. }
  477. public void EmitZnFlagCheck()
  478. {
  479. EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit);
  480. EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
  481. }
  482. private void EmitZnCheck(OpCode ilCmpOp, int flag)
  483. {
  484. Emit(OpCodes.Dup);
  485. Emit(OpCodes.Ldc_I4_0);
  486. if (CurrOp.RegisterSize != RegisterSize.Int32)
  487. {
  488. Emit(OpCodes.Conv_I8);
  489. }
  490. Emit(ilCmpOp);
  491. EmitStflg(flag);
  492. }
  493. }
  494. }