ATranslator.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using ChocolArm64.Decoder;
  2. using ChocolArm64.Events;
  3. using ChocolArm64.Instruction;
  4. using ChocolArm64.Memory;
  5. using ChocolArm64.Translation;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.Reflection.Emit;
  10. namespace ChocolArm64
  11. {
  12. public class ATranslator
  13. {
  14. private HashSet<long> SubBlocks;
  15. private ConcurrentDictionary<long, ATranslatedSub> CachedSubs;
  16. private ConcurrentDictionary<long, string> SymbolTable;
  17. public event EventHandler<ACpuTraceEventArgs> CpuTrace;
  18. public bool EnableCpuTrace { get; set; }
  19. private bool KeepRunning;
  20. public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
  21. {
  22. SubBlocks = new HashSet<long>();
  23. CachedSubs = new ConcurrentDictionary<long, ATranslatedSub>();
  24. if (SymbolTable != null)
  25. {
  26. this.SymbolTable = new ConcurrentDictionary<long, string>(SymbolTable);
  27. }
  28. else
  29. {
  30. this.SymbolTable = new ConcurrentDictionary<long, string>();
  31. }
  32. KeepRunning = true;
  33. }
  34. internal void StopExecution() => KeepRunning = false;
  35. internal void ExecuteSubroutine(AThread Thread, long Position)
  36. {
  37. do
  38. {
  39. if (EnableCpuTrace)
  40. {
  41. if (!SymbolTable.TryGetValue(Position, out string SubName))
  42. {
  43. SubName = string.Empty;
  44. }
  45. CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName));
  46. }
  47. if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub))
  48. {
  49. Sub = TranslateTier0(Thread.Memory, Position);
  50. }
  51. if (Sub.ShouldReJit())
  52. {
  53. TranslateTier1(Thread.Memory, Position);
  54. }
  55. Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
  56. }
  57. while (Position != 0 && KeepRunning);
  58. }
  59. internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
  60. {
  61. if (OpCode.Emitter != AInstEmit.Bl)
  62. {
  63. Sub = null;
  64. return false;
  65. }
  66. return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub);
  67. }
  68. internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
  69. {
  70. return CachedSubs.TryGetValue(Position, out Sub);
  71. }
  72. internal bool HasCachedSub(long Position)
  73. {
  74. return CachedSubs.ContainsKey(Position);
  75. }
  76. private ATranslatedSub TranslateTier0(AMemory Memory, long Position)
  77. {
  78. ABlock Block = ADecoder.DecodeBasicBlock(this, Memory, Position);
  79. ABlock[] Graph = new ABlock[] { Block };
  80. string SubName = GetSubName(Position);
  81. AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName);
  82. do
  83. {
  84. Context.EmitOpCode();
  85. }
  86. while (Context.AdvanceOpCode());
  87. ATranslatedSub Subroutine = Context.GetSubroutine();
  88. if (SubBlocks.Contains(Position))
  89. {
  90. SubBlocks.Remove(Position);
  91. Subroutine.SetType(ATranslatedSubType.SubBlock);
  92. }
  93. else
  94. {
  95. Subroutine.SetType(ATranslatedSubType.SubTier0);
  96. }
  97. CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
  98. AOpCode LastOp = Block.GetLastOp();
  99. if (LastOp.Emitter != AInstEmit.Ret &&
  100. LastOp.Emitter != AInstEmit.Br)
  101. {
  102. SubBlocks.Add(LastOp.Position + 4);
  103. }
  104. return Subroutine;
  105. }
  106. private void TranslateTier1(AMemory Memory, long Position)
  107. {
  108. (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Memory, Position);
  109. string SubName = GetSubName(Position);
  110. PropagateName(Cfg.Graph, SubName);
  111. AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName);
  112. if (Context.CurrBlock.Position != Position)
  113. {
  114. Context.Emit(OpCodes.Br, Context.GetLabel(Position));
  115. }
  116. do
  117. {
  118. Context.EmitOpCode();
  119. }
  120. while (Context.AdvanceOpCode());
  121. //Mark all methods that calls this method for ReJiting,
  122. //since we can now call it directly which is faster.
  123. foreach (ATranslatedSub TS in CachedSubs.Values)
  124. {
  125. if (TS.HasCallee(Position))
  126. {
  127. TS.MarkForReJit();
  128. }
  129. }
  130. ATranslatedSub Subroutine = Context.GetSubroutine();
  131. Subroutine.SetType(ATranslatedSubType.SubTier1);
  132. CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
  133. }
  134. private string GetSubName(long Position)
  135. {
  136. return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}");
  137. }
  138. private void PropagateName(ABlock[] Graph, string Name)
  139. {
  140. foreach (ABlock Block in Graph)
  141. {
  142. AOpCode LastOp = Block.GetLastOp();
  143. if (LastOp != null &&
  144. (LastOp.Emitter == AInstEmit.Bl ||
  145. LastOp.Emitter == AInstEmit.Blr))
  146. {
  147. SymbolTable.TryAdd(LastOp.Position + 4, Name);
  148. }
  149. }
  150. }
  151. }
  152. }