Translator.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. TranslatedSub subroutine = GetOrTranslateSubroutine(state, position);
  50. position = subroutine.Execute(state, _memory);
  51. }
  52. while (position != 0 && state.Running);
  53. state.CurrentTranslator = null;
  54. }
  55. internal void TranslateVirtualSubroutine(CpuThreadState state, long position)
  56. {
  57. if (!_cache.TryGetSubroutine(position, out TranslatedSub sub) || sub.Tier == TranslationTier.Tier0)
  58. {
  59. _queue.Enqueue(new TranslatorQueueItem(position, state.GetExecutionMode(), TranslationTier.Tier1));
  60. }
  61. }
  62. internal ArmSubroutine GetOrTranslateVirtualSubroutine(CpuThreadState state, long position)
  63. {
  64. if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
  65. {
  66. sub = TranslateLowCq(position, state.GetExecutionMode());
  67. }
  68. if (sub.Tier == TranslationTier.Tier0)
  69. {
  70. _queue.Enqueue(new TranslatorQueueItem(position, state.GetExecutionMode(), TranslationTier.Tier1));
  71. }
  72. return sub.Delegate;
  73. }
  74. internal TranslatedSub GetOrTranslateSubroutine(CpuThreadState state, long position)
  75. {
  76. if (!_cache.TryGetSubroutine(position, out TranslatedSub subroutine))
  77. {
  78. subroutine = TranslateLowCq(position, state.GetExecutionMode());
  79. }
  80. return subroutine;
  81. }
  82. private void TranslateQueuedSubs()
  83. {
  84. while (_threadCount != 0)
  85. {
  86. if (_queue.TryDequeue(out TranslatorQueueItem item))
  87. {
  88. bool isCached = _cache.TryGetSubroutine(item.Position, out TranslatedSub sub);
  89. if (isCached && item.Tier <= sub.Tier)
  90. {
  91. continue;
  92. }
  93. if (item.Tier == TranslationTier.Tier0)
  94. {
  95. TranslateLowCq(item.Position, item.Mode);
  96. }
  97. else
  98. {
  99. TranslateHighCq(item.Position, item.Mode);
  100. }
  101. }
  102. else
  103. {
  104. _queue.WaitForItems();
  105. }
  106. }
  107. }
  108. private TranslatedSub TranslateLowCq(long position, ExecutionMode mode)
  109. {
  110. Block block = Decoder.DecodeBasicBlock(_memory, position, mode);
  111. ILEmitterCtx context = new ILEmitterCtx(_cache, _queue, TranslationTier.Tier0, block);
  112. string subName = GetSubroutineName(position);
  113. ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(context.GetILBlocks(), subName);
  114. TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier0);
  115. return _cache.GetOrAdd(position, subroutine, block.OpCodes.Count);
  116. }
  117. private void TranslateHighCq(long position, ExecutionMode mode)
  118. {
  119. Block graph = Decoder.DecodeSubroutine(_memory, position, mode);
  120. ILEmitterCtx context = new ILEmitterCtx(_cache, _queue, TranslationTier.Tier1, graph);
  121. ILBlock[] ilBlocks = context.GetILBlocks();
  122. string subName = GetSubroutineName(position);
  123. ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(ilBlocks, subName);
  124. TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier1);
  125. int ilOpCount = 0;
  126. foreach (ILBlock ilBlock in ilBlocks)
  127. {
  128. ilOpCount += ilBlock.Count;
  129. }
  130. _cache.AddOrUpdate(position, subroutine, ilOpCount);
  131. ForceAheadOfTimeCompilation(subroutine);
  132. }
  133. private string GetSubroutineName(long position)
  134. {
  135. return $"Sub{position:x16}";
  136. }
  137. private void ForceAheadOfTimeCompilation(TranslatedSub subroutine)
  138. {
  139. subroutine.Execute(_dummyThreadState, null);
  140. }
  141. }
  142. }