ILEmitterCtx.cs 22 KB

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