Translator.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.Events;
  3. using ChocolArm64.Memory;
  4. using ChocolArm64.State;
  5. using System;
  6. using System.Threading;
  7. namespace ChocolArm64.Translation
  8. {
  9. public class Translator
  10. {
  11. private MemoryManager _memory;
  12. private CpuThreadState _dummyThreadState;
  13. private TranslatorCache _cache;
  14. private TranslatorQueue _queue;
  15. private Thread _backgroundTranslator;
  16. public event EventHandler<CpuTraceEventArgs> CpuTrace;
  17. public bool EnableCpuTrace { get; set; }
  18. private volatile int _threadCount;
  19. public Translator(MemoryManager memory)
  20. {
  21. _memory = memory;
  22. _dummyThreadState = new CpuThreadState();
  23. _dummyThreadState.Running = false;
  24. _cache = new TranslatorCache();
  25. _queue = new TranslatorQueue();
  26. }
  27. internal void ExecuteSubroutine(CpuThread thread, long position)
  28. {
  29. if (Interlocked.Increment(ref _threadCount) == 1)
  30. {
  31. _backgroundTranslator = new Thread(TranslateQueuedSubs);
  32. _backgroundTranslator.Start();
  33. }
  34. ExecuteSubroutine(thread.ThreadState, position);
  35. if (Interlocked.Decrement(ref _threadCount) == 0)
  36. {
  37. _queue.ForceSignal();
  38. }
  39. }
  40. private void ExecuteSubroutine(CpuThreadState state, long position)
  41. {
  42. state.CurrentTranslator = this;
  43. do
  44. {
  45. if (EnableCpuTrace)
  46. {
  47. CpuTrace?.Invoke(this, new CpuTraceEventArgs(position));
  48. }
  49. if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
  50. {
  51. sub = TranslateLowCq(position, state.GetExecutionMode());
  52. }
  53. position = sub.Execute(state, _memory);
  54. }
  55. while (position != 0 && state.Running);
  56. state.CurrentTranslator = null;
  57. }
  58. internal ArmSubroutine GetOrTranslateSubroutine(CpuThreadState state, long position, CallType cs)
  59. {
  60. if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
  61. {
  62. sub = TranslateLowCq(position, state.GetExecutionMode());
  63. }
  64. if (sub.IsWorthOptimizing())
  65. {
  66. bool isComplete = cs == CallType.Call ||
  67. cs == CallType.VirtualCall;
  68. _queue.Enqueue(position, state.GetExecutionMode(), TranslationTier.Tier1, isComplete);
  69. }
  70. return sub.Delegate;
  71. }
  72. private void TranslateQueuedSubs()
  73. {
  74. while (_threadCount != 0)
  75. {
  76. if (_queue.TryDequeue(out TranslatorQueueItem item))
  77. {
  78. bool isCached = _cache.TryGetSubroutine(item.Position, out TranslatedSub sub);
  79. if (isCached && item.Tier <= sub.Tier)
  80. {
  81. continue;
  82. }
  83. if (item.Tier == TranslationTier.Tier0)
  84. {
  85. TranslateLowCq(item.Position, item.Mode);
  86. }
  87. else
  88. {
  89. TranslateHighCq(item.Position, item.Mode, item.IsComplete);
  90. }
  91. }
  92. else
  93. {
  94. _queue.WaitForItems();
  95. }
  96. }
  97. }
  98. private TranslatedSub TranslateLowCq(long position, ExecutionMode mode)
  99. {
  100. Block block = Decoder.DecodeBasicBlock(_memory, position, mode);
  101. ILEmitterCtx context = new ILEmitterCtx(_memory, _cache, _queue, TranslationTier.Tier0, block);
  102. string subName = GetSubroutineName(position);
  103. bool isAarch64 = mode == ExecutionMode.Aarch64;
  104. ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(context.GetILBlocks(), subName, isAarch64);
  105. TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier0, isWorthOptimizing: true);
  106. return _cache.GetOrAdd(position, subroutine, block.OpCodes.Count);
  107. }
  108. private TranslatedSub TranslateHighCq(long position, ExecutionMode mode, bool isComplete)
  109. {
  110. Block graph = Decoder.DecodeSubroutine(_memory, position, mode);
  111. ILEmitterCtx context = new ILEmitterCtx(_memory, _cache, _queue, TranslationTier.Tier1, graph);
  112. ILBlock[] ilBlocks = context.GetILBlocks();
  113. string subName = GetSubroutineName(position);
  114. bool isAarch64 = mode == ExecutionMode.Aarch64;
  115. isComplete &= !context.HasIndirectJump;
  116. ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(ilBlocks, subName, isAarch64, isComplete);
  117. TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier1, context.HasSlowCall);
  118. int ilOpCount = 0;
  119. foreach (ILBlock ilBlock in ilBlocks)
  120. {
  121. ilOpCount += ilBlock.Count;
  122. }
  123. ForceAheadOfTimeCompilation(subroutine);
  124. _cache.AddOrUpdate(position, subroutine, ilOpCount);
  125. return subroutine;
  126. }
  127. private string GetSubroutineName(long position)
  128. {
  129. return $"Sub{position:x16}";
  130. }
  131. private void ForceAheadOfTimeCompilation(TranslatedSub subroutine)
  132. {
  133. subroutine.Execute(_dummyThreadState, null);
  134. }
  135. }
  136. }