ILEmitterCtx.cs 19 KB

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