ILEmitterCtx.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.Instructions;
  3. using ChocolArm64.Memory;
  4. using ChocolArm64.State;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Reflection;
  8. using System.Reflection.Emit;
  9. namespace ChocolArm64.Translation
  10. {
  11. class ILEmitterCtx
  12. {
  13. public MemoryManager Memory { get; }
  14. private TranslatorCache _cache;
  15. private TranslatorQueue _queue;
  16. private Dictionary<long, ILLabel> _labels;
  17. private long _subPosition;
  18. private int _opcIndex;
  19. private Block _currBlock;
  20. public Block CurrBlock => _currBlock;
  21. public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex];
  22. public TranslationTier Tier { get; }
  23. public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
  24. public bool HasIndirectJump { get; set; }
  25. public bool HasSlowCall { get; set; }
  26. private Dictionary<Block, ILBlock> _visitedBlocks;
  27. private Queue<Block> _branchTargets;
  28. private List<ILBlock> _ilBlocks;
  29. private ILBlock _ilBlock;
  30. private OpCode64 _optOpLastCompare;
  31. private OpCode64 _optOpLastFlagSet;
  32. //This is the index of the temporary register, used to store temporary
  33. //values needed by some functions, since IL doesn't have a swap instruction.
  34. //You can use any value here as long it doesn't conflict with the indices
  35. //for the other registers. Any value >= 64 or < 0 will do.
  36. private const int ReservedLocalsCount = 64;
  37. private const int RorTmpIndex = ReservedLocalsCount + 0;
  38. private const int CmpOptTmp1Index = ReservedLocalsCount + 1;
  39. private const int CmpOptTmp2Index = ReservedLocalsCount + 2;
  40. private const int IntGpTmp1Index = ReservedLocalsCount + 3;
  41. private const int IntGpTmp2Index = ReservedLocalsCount + 4;
  42. private const int UserIntTempStart = ReservedLocalsCount + 5;
  43. //Vectors are part of another "set" of locals.
  44. private const int VecGpTmp1Index = ReservedLocalsCount + 0;
  45. private const int VecGpTmp2Index = ReservedLocalsCount + 1;
  46. private const int VecGpTmp3Index = ReservedLocalsCount + 2;
  47. private const int UserVecTempStart = ReservedLocalsCount + 3;
  48. private static int _userIntTempCount;
  49. private static int _userVecTempCount;
  50. public ILEmitterCtx(
  51. MemoryManager memory,
  52. TranslatorCache cache,
  53. TranslatorQueue queue,
  54. TranslationTier tier,
  55. Block graph)
  56. {
  57. Memory = memory ?? throw new ArgumentNullException(nameof(memory));
  58. _cache = cache ?? throw new ArgumentNullException(nameof(cache));
  59. _queue = queue ?? throw new ArgumentNullException(nameof(queue));
  60. _currBlock = graph ?? throw new ArgumentNullException(nameof(graph));
  61. Tier = tier;
  62. _labels = new Dictionary<long, ILLabel>();
  63. _visitedBlocks = new Dictionary<Block, ILBlock>();
  64. _visitedBlocks.Add(graph, new ILBlock());
  65. _branchTargets = new Queue<Block>();
  66. _ilBlocks = new List<ILBlock>();
  67. _subPosition = graph.Position;
  68. ResetBlockState();
  69. if (AdvanceOpCode())
  70. {
  71. EmitSynchronization();
  72. _ilBlock.Add(new ILOpCodeLoadState(_ilBlock, isSubEntry: true));
  73. }
  74. }
  75. public static int GetIntTempIndex()
  76. {
  77. return UserIntTempStart + _userIntTempCount++;
  78. }
  79. public static int GetVecTempIndex()
  80. {
  81. return UserVecTempStart + _userVecTempCount++;
  82. }
  83. public ILBlock[] GetILBlocks()
  84. {
  85. EmitAllOpCodes();
  86. return _ilBlocks.ToArray();
  87. }
  88. private void EmitAllOpCodes()
  89. {
  90. do
  91. {
  92. EmitOpCode();
  93. }
  94. while (AdvanceOpCode());
  95. }
  96. private void EmitOpCode()
  97. {
  98. if (_currBlock == null)
  99. {
  100. return;
  101. }
  102. int opcIndex = _opcIndex;
  103. if (opcIndex == 0)
  104. {
  105. MarkLabel(GetLabel(_currBlock.Position));
  106. }
  107. bool isLastOp = opcIndex == CurrBlock.OpCodes.Count - 1;
  108. if (isLastOp && CurrBlock.Branch != null &&
  109. (ulong)CurrBlock.Branch.Position <= (ulong)CurrBlock.Position)
  110. {
  111. EmitSynchronization();
  112. }
  113. //On AARCH32 mode, (almost) all instruction can be conditionally
  114. //executed, and the required condition is encoded on the opcode.
  115. //We handle that here, skipping the instruction if the condition
  116. //is not met. We can just ignore it when the condition is "Always",
  117. //because in this case the instruction is always going to be executed.
  118. //Condition "Never" is also ignored because this is a special encoding
  119. //used by some unconditional instructions.
  120. ILLabel lblSkip = null;
  121. if (CurrOp is OpCode32 op && op.Cond < Condition.Al)
  122. {
  123. lblSkip = new ILLabel();
  124. EmitCondBranch(lblSkip, GetInverseCond(op.Cond));
  125. }
  126. CurrOp.Emitter(this);
  127. if (lblSkip != null)
  128. {
  129. MarkLabel(lblSkip);
  130. //If this is the last op on the block, and there's no "next" block
  131. //after this one, then we have to return right now, with the address
  132. //of the next instruction to be executed (in the case that the condition
  133. //is false, and the branch was not taken, as all basic blocks should end with
  134. //some kind of branch).
  135. if (isLastOp && CurrBlock.Next == null)
  136. {
  137. EmitStoreState();
  138. EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes);
  139. Emit(OpCodes.Ret);
  140. }
  141. }
  142. _ilBlock.Add(new ILBarrier());
  143. }
  144. private static Condition GetInverseCond(Condition cond)
  145. {
  146. //Bit 0 of all conditions is basically a negation bit, so
  147. //inverting this bit has the effect of inverting the condition.
  148. return (Condition)((int)cond ^ 1);
  149. }
  150. private void EmitSynchronization()
  151. {
  152. EmitLdarg(TranslatedSub.StateArgIdx);
  153. EmitLdc_I4(_currBlock.OpCodes.Count);
  154. EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize));
  155. EmitLdc_I4(0);
  156. ILLabel lblContinue = new ILLabel();
  157. Emit(OpCodes.Bne_Un_S, lblContinue);
  158. EmitLdc_I8(0);
  159. Emit(OpCodes.Ret);
  160. MarkLabel(lblContinue);
  161. }
  162. private bool AdvanceOpCode()
  163. {
  164. if (_currBlock == null)
  165. {
  166. return false;
  167. }
  168. while (++_opcIndex >= _currBlock.OpCodes.Count)
  169. {
  170. if (!AdvanceBlock())
  171. {
  172. return false;
  173. }
  174. ResetBlockState();
  175. }
  176. return true;
  177. }
  178. private bool AdvanceBlock()
  179. {
  180. if (_currBlock.Branch != null)
  181. {
  182. if (_visitedBlocks.TryAdd(_currBlock.Branch, _ilBlock.Branch))
  183. {
  184. _branchTargets.Enqueue(_currBlock.Branch);
  185. }
  186. }
  187. if (_currBlock.Next != null)
  188. {
  189. if (_visitedBlocks.TryAdd(_currBlock.Next, _ilBlock.Next))
  190. {
  191. _currBlock = _currBlock.Next;
  192. return true;
  193. }
  194. else
  195. {
  196. Emit(OpCodes.Br, GetLabel(_currBlock.Next.Position));
  197. }
  198. }
  199. return _branchTargets.TryDequeue(out _currBlock);
  200. }
  201. private void ResetBlockState()
  202. {
  203. _ilBlock = _visitedBlocks[_currBlock];
  204. _ilBlocks.Add(_ilBlock);
  205. _ilBlock.Next = GetOrCreateILBlock(_currBlock.Next);
  206. _ilBlock.Branch = GetOrCreateILBlock(_currBlock.Branch);
  207. _opcIndex = -1;
  208. _optOpLastFlagSet = null;
  209. _optOpLastCompare = null;
  210. }
  211. private ILBlock GetOrCreateILBlock(Block block)
  212. {
  213. if (block == null)
  214. {
  215. return null;
  216. }
  217. if (_visitedBlocks.TryGetValue(block, out ILBlock ilBlock))
  218. {
  219. return ilBlock;
  220. }
  221. return new ILBlock();
  222. }
  223. public void TranslateAhead(long position, ExecutionMode mode = ExecutionMode.Aarch64)
  224. {
  225. if (_cache.TryGetSubroutine(position, out TranslatedSub sub) && sub.Tier != TranslationTier.Tier0)
  226. {
  227. return;
  228. }
  229. _queue.Enqueue(position, mode, TranslationTier.Tier1, isComplete: true);
  230. }
  231. public bool TryOptEmitSubroutineCall()
  232. {
  233. //Calls should always have a next block, unless
  234. //we're translating a single basic block.
  235. if (_currBlock.Next == null)
  236. {
  237. return false;
  238. }
  239. if (!(CurrOp is IOpCodeBImm op))
  240. {
  241. return false;
  242. }
  243. if (!_cache.TryGetSubroutine(op.Imm, out TranslatedSub sub))
  244. {
  245. return false;
  246. }
  247. //It's not worth to call a Tier0 method, because
  248. //it contains slow code, rather than the entire function.
  249. if (sub.Tier == TranslationTier.Tier0)
  250. {
  251. return false;
  252. }
  253. EmitStoreState(sub);
  254. for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++)
  255. {
  256. EmitLdarg(index);
  257. }
  258. EmitCall(sub.Method);
  259. return true;
  260. }
  261. public void TryOptMarkCondWithoutCmp()
  262. {
  263. _optOpLastCompare = CurrOp;
  264. InstEmitAluHelper.EmitAluLoadOpers(this);
  265. Stloc(CmpOptTmp2Index, VarType.Int);
  266. Stloc(CmpOptTmp1Index, VarType.Int);
  267. }
  268. private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
  269. {
  270. { Condition.Eq, OpCodes.Beq },
  271. { Condition.Ne, OpCodes.Bne_Un },
  272. { Condition.GeUn, OpCodes.Bge_Un },
  273. { Condition.LtUn, OpCodes.Blt_Un },
  274. { Condition.GtUn, OpCodes.Bgt_Un },
  275. { Condition.LeUn, OpCodes.Ble_Un },
  276. { Condition.Ge, OpCodes.Bge },
  277. { Condition.Lt, OpCodes.Blt },
  278. { Condition.Gt, OpCodes.Bgt },
  279. { Condition.Le, OpCodes.Ble }
  280. };
  281. public void EmitCondBranch(ILLabel target, Condition cond)
  282. {
  283. if (_optOpLastCompare != null &&
  284. _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
  285. {
  286. if (_optOpLastCompare.Emitter == InstEmit.Subs)
  287. {
  288. Ldloc(CmpOptTmp1Index, VarType.Int, _optOpLastCompare.RegisterSize);
  289. Ldloc(CmpOptTmp2Index, VarType.Int, _optOpLastCompare.RegisterSize);
  290. Emit(_branchOps[cond], target);
  291. return;
  292. }
  293. else if (_optOpLastCompare.Emitter == InstEmit.Adds && cond != Condition.GeUn
  294. && cond != Condition.LtUn
  295. && cond != Condition.GtUn
  296. && cond != Condition.LeUn)
  297. {
  298. //There are several limitations that needs to be taken into account for CMN comparisons:
  299. //* The unsigned comparisons are not valid, as they depend on the
  300. //carry flag value, and they will have different values for addition and
  301. //subtraction. For addition, it's carry, and for subtraction, it's borrow.
  302. //So, we need to make sure we're not doing a unsigned compare for the CMN case.
  303. //* We can only do the optimization for the immediate variants,
  304. //because when the second operand value is exactly INT_MIN, we can't
  305. //negate the value as theres no positive counterpart.
  306. //Such invalid values can't be encoded on the immediate encodings.
  307. if (_optOpLastCompare is IOpCodeAluImm64 op)
  308. {
  309. Ldloc(CmpOptTmp1Index, VarType.Int, _optOpLastCompare.RegisterSize);
  310. if (_optOpLastCompare.RegisterSize == RegisterSize.Int32)
  311. {
  312. EmitLdc_I4((int)-op.Imm);
  313. }
  314. else
  315. {
  316. EmitLdc_I8(-op.Imm);
  317. }
  318. Emit(_branchOps[cond], target);
  319. return;
  320. }
  321. }
  322. }
  323. OpCode ilOp;
  324. int intCond = (int)cond;
  325. if (intCond < 14)
  326. {
  327. int condTrue = intCond >> 1;
  328. switch (condTrue)
  329. {
  330. case 0: EmitLdflg((int)PState.ZBit); break;
  331. case 1: EmitLdflg((int)PState.CBit); break;
  332. case 2: EmitLdflg((int)PState.NBit); break;
  333. case 3: EmitLdflg((int)PState.VBit); break;
  334. case 4:
  335. EmitLdflg((int)PState.CBit);
  336. EmitLdflg((int)PState.ZBit);
  337. Emit(OpCodes.Not);
  338. Emit(OpCodes.And);
  339. break;
  340. case 5:
  341. case 6:
  342. EmitLdflg((int)PState.NBit);
  343. EmitLdflg((int)PState.VBit);
  344. Emit(OpCodes.Ceq);
  345. if (condTrue == 6)
  346. {
  347. EmitLdflg((int)PState.ZBit);
  348. Emit(OpCodes.Not);
  349. Emit(OpCodes.And);
  350. }
  351. break;
  352. }
  353. ilOp = (intCond & 1) != 0
  354. ? OpCodes.Brfalse
  355. : OpCodes.Brtrue;
  356. }
  357. else
  358. {
  359. ilOp = OpCodes.Br;
  360. }
  361. Emit(ilOp, target);
  362. }
  363. public void EmitCast(IntType intType)
  364. {
  365. switch (intType)
  366. {
  367. case IntType.UInt8: Emit(OpCodes.Conv_U1); break;
  368. case IntType.UInt16: Emit(OpCodes.Conv_U2); break;
  369. case IntType.UInt32: Emit(OpCodes.Conv_U4); break;
  370. case IntType.UInt64: Emit(OpCodes.Conv_U8); break;
  371. case IntType.Int8: Emit(OpCodes.Conv_I1); break;
  372. case IntType.Int16: Emit(OpCodes.Conv_I2); break;
  373. case IntType.Int32: Emit(OpCodes.Conv_I4); break;
  374. case IntType.Int64: Emit(OpCodes.Conv_I8); break;
  375. }
  376. bool sz64 = CurrOp.RegisterSize != RegisterSize.Int32;
  377. if (sz64 == (intType == IntType.UInt64 ||
  378. intType == IntType.Int64))
  379. {
  380. return;
  381. }
  382. if (sz64)
  383. {
  384. Emit(intType >= IntType.Int8
  385. ? OpCodes.Conv_I8
  386. : OpCodes.Conv_U8);
  387. }
  388. else
  389. {
  390. Emit(OpCodes.Conv_U4);
  391. }
  392. }
  393. public void EmitLsl(int amount) => EmitILShift(amount, OpCodes.Shl);
  394. public void EmitLsr(int amount) => EmitILShift(amount, OpCodes.Shr_Un);
  395. public void EmitAsr(int amount) => EmitILShift(amount, OpCodes.Shr);
  396. private void EmitILShift(int amount, OpCode ilOp)
  397. {
  398. if (amount > 0)
  399. {
  400. EmitLdc_I4(amount);
  401. Emit(ilOp);
  402. }
  403. }
  404. public void EmitRor(int amount)
  405. {
  406. if (amount > 0)
  407. {
  408. Stloc(RorTmpIndex, VarType.Int);
  409. Ldloc(RorTmpIndex, VarType.Int);
  410. EmitLdc_I4(amount);
  411. Emit(OpCodes.Shr_Un);
  412. Ldloc(RorTmpIndex, VarType.Int);
  413. EmitLdc_I4(CurrOp.GetBitsCount() - amount);
  414. Emit(OpCodes.Shl);
  415. Emit(OpCodes.Or);
  416. }
  417. }
  418. public ILLabel GetLabel(long position)
  419. {
  420. if (!_labels.TryGetValue(position, out ILLabel output))
  421. {
  422. output = new ILLabel();
  423. _labels.Add(position, output);
  424. }
  425. return output;
  426. }
  427. public void MarkLabel(ILLabel label)
  428. {
  429. _ilBlock.Add(label);
  430. }
  431. public void Emit(OpCode ilOp)
  432. {
  433. _ilBlock.Add(new ILOpCode(ilOp));
  434. }
  435. public void Emit(OpCode ilOp, ILLabel label)
  436. {
  437. _ilBlock.Add(new ILOpCodeBranch(ilOp, label));
  438. }
  439. public void EmitFieldLoad(FieldInfo info)
  440. {
  441. _ilBlock.Add(new ILOpCodeLoadField(info));
  442. }
  443. public void EmitPrint(string text)
  444. {
  445. _ilBlock.Add(new ILOpCodeLog(text));
  446. }
  447. public void EmitLdarg(int index)
  448. {
  449. _ilBlock.Add(new ILOpCodeLoad(index, VarType.Arg));
  450. }
  451. public void EmitLdintzr(int index)
  452. {
  453. if (index != RegisterAlias.Zr)
  454. {
  455. EmitLdint(index);
  456. }
  457. else
  458. {
  459. EmitLdc_I(0);
  460. }
  461. }
  462. public void EmitStintzr(int index)
  463. {
  464. if (index != RegisterAlias.Zr)
  465. {
  466. EmitStint(index);
  467. }
  468. else
  469. {
  470. Emit(OpCodes.Pop);
  471. }
  472. }
  473. public void EmitLoadState()
  474. {
  475. if (_ilBlock.Next == null)
  476. {
  477. throw new InvalidOperationException("Can't load state for next block, because there's no next block.");
  478. }
  479. _ilBlock.Add(new ILOpCodeLoadState(_ilBlock.Next));
  480. }
  481. public void EmitStoreState()
  482. {
  483. _ilBlock.Add(new ILOpCodeStoreState(_ilBlock));
  484. }
  485. private void EmitStoreState(TranslatedSub callSub)
  486. {
  487. _ilBlock.Add(new ILOpCodeStoreState(_ilBlock, callSub));
  488. }
  489. public void EmitLdtmp() => EmitLdint(IntGpTmp1Index);
  490. public void EmitSttmp() => EmitStint(IntGpTmp1Index);
  491. public void EmitLdtmp2() => EmitLdint(IntGpTmp2Index);
  492. public void EmitSttmp2() => EmitStint(IntGpTmp2Index);
  493. public void EmitLdvectmp() => EmitLdvec(VecGpTmp1Index);
  494. public void EmitStvectmp() => EmitStvec(VecGpTmp1Index);
  495. public void EmitLdvectmp2() => EmitLdvec(VecGpTmp2Index);
  496. public void EmitStvectmp2() => EmitStvec(VecGpTmp2Index);
  497. public void EmitLdvectmp3() => EmitLdvec(VecGpTmp3Index);
  498. public void EmitStvectmp3() => EmitStvec(VecGpTmp3Index);
  499. public void EmitLdint(int index) => Ldloc(index, VarType.Int);
  500. public void EmitStint(int index) => Stloc(index, VarType.Int);
  501. public void EmitLdvec(int index) => Ldloc(index, VarType.Vector);
  502. public void EmitStvec(int index) => Stloc(index, VarType.Vector);
  503. public void EmitLdflg(int index) => Ldloc(index, VarType.Flag);
  504. public void EmitStflg(int index)
  505. {
  506. //Set this only if any of the NZCV flag bits were modified.
  507. //This is used to ensure that, when emiting a direct IL branch
  508. //instruction for compare + branch sequences, we're not expecting
  509. //to use comparison values from an old instruction, when in fact
  510. //the flags were already overwritten by another instruction further along.
  511. if (index >= (int)PState.VBit)
  512. {
  513. _optOpLastFlagSet = CurrOp;
  514. }
  515. Stloc(index, VarType.Flag);
  516. }
  517. private void Ldloc(int index, VarType varType)
  518. {
  519. _ilBlock.Add(new ILOpCodeLoad(index, varType, CurrOp.RegisterSize));
  520. }
  521. private void Ldloc(int index, VarType varType, RegisterSize registerSize)
  522. {
  523. _ilBlock.Add(new ILOpCodeLoad(index, varType, registerSize));
  524. }
  525. private void Stloc(int index, VarType varType)
  526. {
  527. _ilBlock.Add(new ILOpCodeStore(index, varType, CurrOp.RegisterSize));
  528. }
  529. public void EmitCallPropGet(Type objType, string propName)
  530. {
  531. EmitCall(objType, $"get_{propName}");
  532. }
  533. public void EmitCallPropSet(Type objType, string propName)
  534. {
  535. EmitCall(objType, $"set_{propName}");
  536. }
  537. public void EmitCall(Type objType, string mthdName)
  538. {
  539. if (objType == null)
  540. {
  541. throw new ArgumentNullException(nameof(objType));
  542. }
  543. if (mthdName == null)
  544. {
  545. throw new ArgumentNullException(nameof(mthdName));
  546. }
  547. EmitCall(objType.GetMethod(mthdName));
  548. }
  549. public void EmitCallPrivatePropGet(Type objType, string propName)
  550. {
  551. EmitPrivateCall(objType, $"get_{propName}");
  552. }
  553. public void EmitCallPrivatePropSet(Type objType, string propName)
  554. {
  555. EmitPrivateCall(objType, $"set_{propName}");
  556. }
  557. public void EmitPrivateCall(Type objType, string mthdName)
  558. {
  559. if (objType == null)
  560. {
  561. throw new ArgumentNullException(nameof(objType));
  562. }
  563. if (mthdName == null)
  564. {
  565. throw new ArgumentNullException(nameof(mthdName));
  566. }
  567. EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic));
  568. }
  569. public void EmitCall(MethodInfo mthdInfo, bool isVirtual = false)
  570. {
  571. _ilBlock.Add(new ILOpCodeCall(mthdInfo ?? throw new ArgumentNullException(nameof(mthdInfo)), isVirtual));
  572. }
  573. public void EmitLdc_I(long value)
  574. {
  575. if (CurrOp.RegisterSize == RegisterSize.Int32)
  576. {
  577. EmitLdc_I4((int)value);
  578. }
  579. else
  580. {
  581. EmitLdc_I8(value);
  582. }
  583. }
  584. public void EmitLdc_I4(int value)
  585. {
  586. _ilBlock.Add(new ILOpCodeConst(value));
  587. }
  588. public void EmitLdc_I8(long value)
  589. {
  590. _ilBlock.Add(new ILOpCodeConst(value));
  591. }
  592. public void EmitLdc_R4(float value)
  593. {
  594. _ilBlock.Add(new ILOpCodeConst(value));
  595. }
  596. public void EmitLdc_R8(double value)
  597. {
  598. _ilBlock.Add(new ILOpCodeConst(value));
  599. }
  600. public void EmitZnFlagCheck()
  601. {
  602. EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit);
  603. EmitZnCheck(OpCodes.Clt, (int)PState.NBit);
  604. }
  605. private void EmitZnCheck(OpCode ilCmpOp, int flag)
  606. {
  607. Emit(OpCodes.Dup);
  608. Emit(OpCodes.Ldc_I4_0);
  609. if (CurrOp.RegisterSize != RegisterSize.Int32)
  610. {
  611. Emit(OpCodes.Conv_I8);
  612. }
  613. Emit(ilCmpOp);
  614. EmitStflg(flag);
  615. }
  616. }
  617. }