ILEmitterCtx.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.Instructions;
  3. using ChocolArm64.IntermediateRepresentation;
  4. using ChocolArm64.Memory;
  5. using ChocolArm64.State;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Reflection;
  9. using System.Reflection.Emit;
  10. namespace ChocolArm64.Translation
  11. {
  12. class ILEmitterCtx
  13. {
  14. public MemoryManager Memory { get; }
  15. private TranslatorCache _cache;
  16. private TranslatorQueue _queue;
  17. private Block _currBlock;
  18. public Block CurrBlock
  19. {
  20. get
  21. {
  22. return _currBlock;
  23. }
  24. set
  25. {
  26. _currBlock = value;
  27. ResetBlockState();
  28. }
  29. }
  30. public OpCode64 CurrOp { get; set; }
  31. public TranslationTier Tier { get; }
  32. public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
  33. public bool HasIndirectJump { get; set; }
  34. public bool HasSlowCall { get; set; }
  35. private Dictionary<long, ILLabel> _labels;
  36. private Dictionary<ILLabel, BasicBlock> _irLabels;
  37. private List<BasicBlock> _irBlocks;
  38. private BasicBlock _irBlock;
  39. private bool _needsNewBlock;
  40. private OpCode64 _optOpLastCompare;
  41. private OpCode64 _optOpLastFlagSet;
  42. // This is the index of the temporary register, used to store temporary
  43. // values needed by some functions, since IL doesn't have a swap instruction.
  44. // You can use any value here as long it doesn't conflict with the indices
  45. // for the other registers. Any value >= 64 or < 0 will do.
  46. private const int ReservedLocalsCount = 64;
  47. private const int RorTmpIndex = ReservedLocalsCount + 0;
  48. private const int CmpOptTmp1Index = ReservedLocalsCount + 1;
  49. private const int CmpOptTmp2Index = ReservedLocalsCount + 2;
  50. private const int IntGpTmp1Index = ReservedLocalsCount + 3;
  51. private const int IntGpTmp2Index = ReservedLocalsCount + 4;
  52. private const int UserIntTempStart = ReservedLocalsCount + 5;
  53. // Vectors are part of another "set" of locals.
  54. private const int VecGpTmp1Index = ReservedLocalsCount + 0;
  55. private const int VecGpTmp2Index = ReservedLocalsCount + 1;
  56. private const int VecGpTmp3Index = ReservedLocalsCount + 2;
  57. private const int UserVecTempStart = ReservedLocalsCount + 3;
  58. private static int _userIntTempCount;
  59. private static int _userVecTempCount;
  60. public ILEmitterCtx(
  61. MemoryManager memory,
  62. TranslatorCache cache,
  63. TranslatorQueue queue,
  64. TranslationTier tier)
  65. {
  66. Memory = memory ?? throw new ArgumentNullException(nameof(memory));
  67. _cache = cache ?? throw new ArgumentNullException(nameof(cache));
  68. _queue = queue ?? throw new ArgumentNullException(nameof(queue));
  69. Tier = tier;
  70. _labels = new Dictionary<long, ILLabel>();
  71. _irLabels = new Dictionary<ILLabel, BasicBlock>();
  72. _irBlocks = new List<BasicBlock>();
  73. NewNextBlock();
  74. EmitSynchronization();
  75. EmitLoadContext();
  76. }
  77. public static int GetIntTempIndex()
  78. {
  79. return UserIntTempStart + _userIntTempCount++;
  80. }
  81. public static int GetVecTempIndex()
  82. {
  83. return UserVecTempStart + _userVecTempCount++;
  84. }
  85. public BasicBlock[] GetBlocks()
  86. {
  87. return _irBlocks.ToArray();
  88. }
  89. public void EmitSynchronization()
  90. {
  91. EmitLdarg(TranslatedSub.StateArgIdx);
  92. EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
  93. EmitLdc_I4(0);
  94. ILLabel lblContinue = new ILLabel();
  95. Emit(OpCodes.Bne_Un_S, lblContinue);
  96. EmitLdc_I8(0);
  97. Emit(OpCodes.Ret);
  98. MarkLabel(lblContinue);
  99. }
  100. public void ResetBlockStateForPredicatedOp()
  101. {
  102. // Check if this is a predicated instruction that modifies flags,
  103. // in this case the value of the flags is unknown as we don't know
  104. // in advance if the instruction is going to be executed or not.
  105. // So, we reset the block state to prevent an invalid optimization.
  106. if (CurrOp == _optOpLastFlagSet)
  107. {
  108. ResetBlockState();
  109. }
  110. }
  111. private void ResetBlockState()
  112. {
  113. _optOpLastFlagSet = null;
  114. _optOpLastCompare = null;
  115. }
  116. public void TranslateAhead(long position, ExecutionMode mode = ExecutionMode.Aarch64)
  117. {
  118. if (_cache.TryGetSubroutine(position, out TranslatedSub sub) && sub.Tier != TranslationTier.Tier0)
  119. {
  120. return;
  121. }
  122. _queue.Enqueue(position, mode, TranslationTier.Tier1, isComplete: true);
  123. }
  124. public bool TryOptEmitSubroutineCall()
  125. {
  126. // Calls should always have a next block, unless
  127. // we're translating a single basic block.
  128. if (_currBlock.Next == null)
  129. {
  130. return false;
  131. }
  132. if (!(CurrOp is IOpCodeBImm op))
  133. {
  134. return false;
  135. }
  136. if (!_cache.TryGetSubroutine(op.Imm, out TranslatedSub sub) || sub.Tier != TranslationTier.Tier0)
  137. {
  138. return false;
  139. }
  140. EmitStoreContext();
  141. for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++)
  142. {
  143. EmitLdarg(index);
  144. }
  145. EmitCall(sub.Method);
  146. return true;
  147. }
  148. public void TryOptMarkCondWithoutCmp()
  149. {
  150. _optOpLastCompare = CurrOp;
  151. InstEmitAluHelper.EmitAluLoadOpers(this);
  152. Stloc(CmpOptTmp2Index, RegisterType.Int);
  153. Stloc(CmpOptTmp1Index, RegisterType.Int);
  154. }
  155. private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
  156. {
  157. { Condition.Eq, OpCodes.Beq },
  158. { Condition.Ne, OpCodes.Bne_Un },
  159. { Condition.GeUn, OpCodes.Bge_Un },
  160. { Condition.LtUn, OpCodes.Blt_Un },
  161. { Condition.GtUn, OpCodes.Bgt_Un },
  162. { Condition.LeUn, OpCodes.Ble_Un },
  163. { Condition.Ge, OpCodes.Bge },
  164. { Condition.Lt, OpCodes.Blt },
  165. { Condition.Gt, OpCodes.Bgt },
  166. { Condition.Le, OpCodes.Ble }
  167. };
  168. public void EmitCondBranch(ILLabel target, Condition cond)
  169. {
  170. if (_optOpLastCompare != null &&
  171. _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
  172. {
  173. if (_optOpLastCompare.Emitter == InstEmit.Subs)
  174. {
  175. Ldloc(CmpOptTmp1Index, RegisterType.Int, _optOpLastCompare.RegisterSize);
  176. Ldloc(CmpOptTmp2Index, RegisterType.Int, _optOpLastCompare.RegisterSize);
  177. Emit(_branchOps[cond], target);
  178. return;
  179. }
  180. else if (_optOpLastCompare.Emitter == InstEmit.Adds && cond != Condition.GeUn
  181. && cond != Condition.LtUn
  182. && cond != Condition.GtUn
  183. && cond != Condition.LeUn)
  184. {
  185. // There are several limitations that needs to be taken into account for CMN comparisons:
  186. // - The unsigned comparisons are not valid, as they depend on the
  187. // carry flag value, and they will have different values for addition and
  188. // subtraction. For addition, it's carry, and for subtraction, it's borrow.
  189. // So, we need to make sure we're not doing a unsigned compare for the CMN case.
  190. // - We can only do the optimization for the immediate variants,
  191. // because when the second operand value is exactly INT_MIN, we can't
  192. // negate the value as theres no positive counterpart.
  193. // Such invalid values can't be encoded on the immediate encodings.
  194. if (_optOpLastCompare is IOpCodeAluImm64 op)
  195. {
  196. Ldloc(CmpOptTmp1Index, RegisterType.Int, _optOpLastCompare.RegisterSize);
  197. if (_optOpLastCompare.RegisterSize == RegisterSize.Int32)
  198. {
  199. EmitLdc_I4((int)-op.Imm);
  200. }
  201. else
  202. {
  203. EmitLdc_I8(-op.Imm);
  204. }
  205. Emit(_branchOps[cond], target);
  206. return;
  207. }
  208. }
  209. }
  210. OpCode ilOp;
  211. int intCond = (int)cond;
  212. if (intCond < 14)
  213. {
  214. int condTrue = intCond >> 1;
  215. switch (condTrue)
  216. {
  217. case 0: EmitLdflg((int)PState.ZBit); break;
  218. case 1: EmitLdflg((int)PState.CBit); break;
  219. case 2: EmitLdflg((int)PState.NBit); break;
  220. case 3: EmitLdflg((int)PState.VBit); break;
  221. case 4:
  222. EmitLdflg((int)PState.CBit);
  223. EmitLdflg((int)PState.ZBit);
  224. Emit(OpCodes.Not);
  225. Emit(OpCodes.And);
  226. break;
  227. case 5:
  228. case 6:
  229. EmitLdflg((int)PState.NBit);
  230. EmitLdflg((int)PState.VBit);
  231. Emit(OpCodes.Ceq);
  232. if (condTrue == 6)
  233. {
  234. EmitLdflg((int)PState.ZBit);
  235. Emit(OpCodes.Not);
  236. Emit(OpCodes.And);
  237. }
  238. break;
  239. }
  240. ilOp = (intCond & 1) != 0 ? OpCodes.Brfalse : 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 || intType == IntType.Int64))
  263. {
  264. return;
  265. }
  266. if (sz64)
  267. {
  268. Emit(intType >= IntType.Int8 ? OpCodes.Conv_I8 : OpCodes.Conv_U8);
  269. }
  270. else
  271. {
  272. Emit(OpCodes.Conv_U4);
  273. }
  274. }
  275. public void EmitLsl(int amount) => EmitILShift(amount, OpCodes.Shl);
  276. public void EmitLsr(int amount) => EmitILShift(amount, OpCodes.Shr_Un);
  277. public void EmitAsr(int amount) => EmitILShift(amount, OpCodes.Shr);
  278. private void EmitILShift(int amount, OpCode ilOp)
  279. {
  280. if (amount > 0)
  281. {
  282. EmitLdc_I4(amount);
  283. Emit(ilOp);
  284. }
  285. }
  286. public void EmitRor(int amount)
  287. {
  288. if (amount > 0)
  289. {
  290. Stloc(RorTmpIndex, RegisterType.Int);
  291. Ldloc(RorTmpIndex, RegisterType.Int);
  292. EmitLdc_I4(amount);
  293. Emit(OpCodes.Shr_Un);
  294. Ldloc(RorTmpIndex, RegisterType.Int);
  295. EmitLdc_I4(CurrOp.GetBitsCount() - amount);
  296. Emit(OpCodes.Shl);
  297. Emit(OpCodes.Or);
  298. }
  299. }
  300. public ILLabel GetLabel(long position)
  301. {
  302. if (!_labels.TryGetValue(position, out ILLabel output))
  303. {
  304. output = new ILLabel();
  305. _labels.Add(position, output);
  306. }
  307. return output;
  308. }
  309. public void MarkLabel(ILLabel label)
  310. {
  311. if (_irLabels.TryGetValue(label, out BasicBlock nextBlock))
  312. {
  313. nextBlock.Index = _irBlocks.Count;
  314. _irBlocks.Add(nextBlock);
  315. NextBlock(nextBlock);
  316. }
  317. else
  318. {
  319. NewNextBlock();
  320. _irLabels.Add(label, _irBlock);
  321. }
  322. AddOperation(Operation.MarkLabel(label));
  323. }
  324. public void Emit(OpCode ilOp)
  325. {
  326. AddOperation(Operation.IL(ilOp));
  327. if (ilOp == OpCodes.Ret)
  328. {
  329. NextBlock(null);
  330. _needsNewBlock = true;
  331. }
  332. }
  333. public void Emit(OpCode ilOp, ILLabel label)
  334. {
  335. AddOperation(Operation.ILBranch(ilOp, label));
  336. _needsNewBlock = true;
  337. if (!_irLabels.TryGetValue(label, out BasicBlock branchBlock))
  338. {
  339. branchBlock = new BasicBlock();
  340. _irLabels.Add(label, branchBlock);
  341. }
  342. _irBlock.Branch = branchBlock;
  343. }
  344. public void EmitLdfld(FieldInfo info)
  345. {
  346. AddOperation(Operation.LoadField(info));
  347. }
  348. public void EmitLdarg(int index)
  349. {
  350. AddOperation(Operation.LoadArgument(index));
  351. }
  352. public void EmitLdintzr(int index)
  353. {
  354. if (index != RegisterAlias.Zr)
  355. {
  356. EmitLdint(index);
  357. }
  358. else
  359. {
  360. EmitLdc_I(0);
  361. }
  362. }
  363. public void EmitStintzr(int index)
  364. {
  365. if (index != RegisterAlias.Zr)
  366. {
  367. EmitStint(index);
  368. }
  369. else
  370. {
  371. Emit(OpCodes.Pop);
  372. }
  373. }
  374. public void EmitLoadContext()
  375. {
  376. _needsNewBlock = true;
  377. AddOperation(Operation.LoadContext());
  378. }
  379. public void EmitStoreContext()
  380. {
  381. AddOperation(Operation.StoreContext());
  382. }
  383. public void EmitLdtmp() => EmitLdint(IntGpTmp1Index);
  384. public void EmitSttmp() => EmitStint(IntGpTmp1Index);
  385. public void EmitLdtmp2() => EmitLdint(IntGpTmp2Index);
  386. public void EmitSttmp2() => EmitStint(IntGpTmp2Index);
  387. public void EmitLdvectmp() => EmitLdvec(VecGpTmp1Index);
  388. public void EmitStvectmp() => EmitStvec(VecGpTmp1Index);
  389. public void EmitLdvectmp2() => EmitLdvec(VecGpTmp2Index);
  390. public void EmitStvectmp2() => EmitStvec(VecGpTmp2Index);
  391. public void EmitLdvectmp3() => EmitLdvec(VecGpTmp3Index);
  392. public void EmitStvectmp3() => EmitStvec(VecGpTmp3Index);
  393. public void EmitLdint(int index) => Ldloc(index, RegisterType.Int);
  394. public void EmitStint(int index) => Stloc(index, RegisterType.Int);
  395. public void EmitLdvec(int index) => Ldloc(index, RegisterType.Vector);
  396. public void EmitStvec(int index) => Stloc(index, RegisterType.Vector);
  397. public void EmitLdflg(int index) => Ldloc(index, RegisterType.Flag);
  398. public void EmitStflg(int index)
  399. {
  400. // Set this only if any of the NZCV flag bits were modified.
  401. // This is used to ensure that when emiting a direct IL branch
  402. // instruction for compare + branch sequences, we're not expecting
  403. // to use comparison values from an old instruction, when in fact
  404. // the flags were already overwritten by another instruction further along.
  405. if (index >= (int)PState.VBit)
  406. {
  407. _optOpLastFlagSet = CurrOp;
  408. }
  409. Stloc(index, RegisterType.Flag);
  410. }
  411. private void Ldloc(int index, RegisterType type)
  412. {
  413. AddOperation(Operation.LoadLocal(index, type, CurrOp.RegisterSize));
  414. }
  415. private void Ldloc(int index, RegisterType type, RegisterSize size)
  416. {
  417. AddOperation(Operation.LoadLocal(index, type, size));
  418. }
  419. private void Stloc(int index, RegisterType type)
  420. {
  421. AddOperation(Operation.StoreLocal(index, type, CurrOp.RegisterSize));
  422. }
  423. public void EmitCallPropGet(Type objType, string propName)
  424. {
  425. EmitCall(objType, $"get_{propName}");
  426. }
  427. public void EmitCallPropSet(Type objType, string propName)
  428. {
  429. EmitCall(objType, $"set_{propName}");
  430. }
  431. public void EmitCall(Type objType, string mthdName)
  432. {
  433. if (objType == null)
  434. {
  435. throw new ArgumentNullException(nameof(objType));
  436. }
  437. if (mthdName == null)
  438. {
  439. throw new ArgumentNullException(nameof(mthdName));
  440. }
  441. EmitCall(objType.GetMethod(mthdName));
  442. }
  443. public void EmitCallPrivatePropGet(Type objType, string propName)
  444. {
  445. EmitPrivateCall(objType, $"get_{propName}");
  446. }
  447. public void EmitCallPrivatePropSet(Type objType, string propName)
  448. {
  449. EmitPrivateCall(objType, $"set_{propName}");
  450. }
  451. public void EmitPrivateCall(Type objType, string mthdName)
  452. {
  453. if (objType == null)
  454. {
  455. throw new ArgumentNullException(nameof(objType));
  456. }
  457. if (mthdName == null)
  458. {
  459. throw new ArgumentNullException(nameof(mthdName));
  460. }
  461. EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic));
  462. }
  463. public void EmitCall(MethodInfo mthdInfo, bool isVirtual = false)
  464. {
  465. if (mthdInfo == null)
  466. {
  467. throw new ArgumentNullException(nameof(mthdInfo));
  468. }
  469. if (isVirtual)
  470. {
  471. AddOperation(Operation.CallVirtual(mthdInfo));
  472. }
  473. else
  474. {
  475. AddOperation(Operation.Call(mthdInfo));
  476. }
  477. }
  478. public void EmitLdc_I(long value)
  479. {
  480. if (CurrOp.RegisterSize == RegisterSize.Int32)
  481. {
  482. EmitLdc_I4((int)value);
  483. }
  484. else
  485. {
  486. EmitLdc_I8(value);
  487. }
  488. }
  489. public void EmitLdc_I4(int value)
  490. {
  491. AddOperation(Operation.LoadConstant(value));
  492. }
  493. public void EmitLdc_I8(long value)
  494. {
  495. AddOperation(Operation.LoadConstant(value));
  496. }
  497. public void EmitLdc_R4(float value)
  498. {
  499. AddOperation(Operation.LoadConstant(value));
  500. }
  501. public void EmitLdc_R8(double value)
  502. {
  503. AddOperation(Operation.LoadConstant(value));
  504. }
  505. public void EmitZnFlagCheck()
  506. {
  507. EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit);
  508. EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
  509. }
  510. private void EmitZnCheck(OpCode ilCmpOp, int flag)
  511. {
  512. Emit(OpCodes.Dup);
  513. Emit(OpCodes.Ldc_I4_0);
  514. if (CurrOp.RegisterSize != RegisterSize.Int32)
  515. {
  516. Emit(OpCodes.Conv_I8);
  517. }
  518. Emit(ilCmpOp);
  519. EmitStflg(flag);
  520. }
  521. private void AddOperation(Operation operation)
  522. {
  523. if (_needsNewBlock)
  524. {
  525. NewNextBlock();
  526. }
  527. _irBlock.Add(operation);
  528. }
  529. private void NewNextBlock()
  530. {
  531. BasicBlock block = new BasicBlock(_irBlocks.Count);
  532. _irBlocks.Add(block);
  533. NextBlock(block);
  534. }
  535. private void NextBlock(BasicBlock nextBlock)
  536. {
  537. if (_irBlock != null && !EndsWithUnconditional(_irBlock))
  538. {
  539. _irBlock.Next = nextBlock;
  540. }
  541. _irBlock = nextBlock;
  542. _needsNewBlock = false;
  543. }
  544. private static bool EndsWithUnconditional(BasicBlock block)
  545. {
  546. Operation lastOp = block.GetLastOp();
  547. if (lastOp == null || lastOp.Type != OperationType.ILBranch)
  548. {
  549. return false;
  550. }
  551. OpCode opCode = lastOp.GetArg<OpCode>(0);
  552. return opCode == OpCodes.Br || opCode == OpCodes.Br_S;
  553. }
  554. }
  555. }