ATranslator.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using ChocolArm64.Decoder;
  2. using ChocolArm64.Events;
  3. using ChocolArm64.Instruction;
  4. using ChocolArm64.Memory;
  5. using ChocolArm64.State;
  6. using ChocolArm64.Translation;
  7. using System;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.Reflection.Emit;
  11. namespace ChocolArm64
  12. {
  13. public class ATranslator
  14. {
  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. public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
  20. {
  21. CachedSubs = new ConcurrentDictionary<long, ATranslatedSub>();
  22. if (SymbolTable != null)
  23. {
  24. this.SymbolTable = new ConcurrentDictionary<long, string>(SymbolTable);
  25. }
  26. else
  27. {
  28. this.SymbolTable = new ConcurrentDictionary<long, string>();
  29. }
  30. }
  31. internal void ExecuteSubroutine(AThread Thread, long Position)
  32. {
  33. //TODO: Both the execute A32/A64 methods should be merged on the future,
  34. //when both ISAs are implemented with the interpreter and JIT.
  35. //As of now, A32 only has a interpreter and A64 a JIT.
  36. AThreadState State = Thread.ThreadState;
  37. AMemory Memory = Thread.Memory;
  38. if (State.ExecutionMode == AExecutionMode.AArch32)
  39. {
  40. ExecuteSubroutineA32(State, Memory);
  41. }
  42. else
  43. {
  44. ExecuteSubroutineA64(State, Memory, Position);
  45. }
  46. }
  47. private void ExecuteSubroutineA32(AThreadState State, AMemory Memory)
  48. {
  49. do
  50. {
  51. AOpCode OpCode = ADecoder.DecodeOpCode(State, Memory, State.R15);
  52. OpCode.Interpreter(State, Memory, OpCode);
  53. }
  54. while (State.R15 != 0 && State.Running);
  55. }
  56. private void ExecuteSubroutineA64(AThreadState State, AMemory Memory, long Position)
  57. {
  58. do
  59. {
  60. if (EnableCpuTrace)
  61. {
  62. if (!SymbolTable.TryGetValue(Position, out string SubName))
  63. {
  64. SubName = string.Empty;
  65. }
  66. CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName));
  67. }
  68. if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub))
  69. {
  70. Sub = TranslateTier0(State, Memory, Position);
  71. }
  72. if (Sub.ShouldReJit())
  73. {
  74. TranslateTier1(State, Memory, Position);
  75. }
  76. Position = Sub.Execute(State, Memory);
  77. }
  78. while (Position != 0 && State.Running);
  79. }
  80. internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
  81. {
  82. if (OpCode.Emitter != AInstEmit.Bl)
  83. {
  84. Sub = null;
  85. return false;
  86. }
  87. return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub);
  88. }
  89. internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
  90. {
  91. return CachedSubs.TryGetValue(Position, out Sub);
  92. }
  93. internal bool HasCachedSub(long Position)
  94. {
  95. return CachedSubs.ContainsKey(Position);
  96. }
  97. private ATranslatedSub TranslateTier0(AThreadState State, AMemory Memory, long Position)
  98. {
  99. ABlock Block = ADecoder.DecodeBasicBlock(State, this, Memory, Position);
  100. ABlock[] Graph = new ABlock[] { Block };
  101. string SubName = GetSubName(Position);
  102. AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName);
  103. do
  104. {
  105. Context.EmitOpCode();
  106. }
  107. while (Context.AdvanceOpCode());
  108. ATranslatedSub Subroutine = Context.GetSubroutine();
  109. Subroutine.SetType(ATranslatedSubType.SubTier0);
  110. CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
  111. AOpCode LastOp = Block.GetLastOp();
  112. return Subroutine;
  113. }
  114. private void TranslateTier1(AThreadState State, AMemory Memory, long Position)
  115. {
  116. (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(State, this, Memory, Position);
  117. string SubName = GetSubName(Position);
  118. PropagateName(Cfg.Graph, SubName);
  119. AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName);
  120. if (Context.CurrBlock.Position != Position)
  121. {
  122. Context.Emit(OpCodes.Br, Context.GetLabel(Position));
  123. }
  124. do
  125. {
  126. Context.EmitOpCode();
  127. }
  128. while (Context.AdvanceOpCode());
  129. //Mark all methods that calls this method for ReJiting,
  130. //since we can now call it directly which is faster.
  131. if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub))
  132. {
  133. foreach (long CallerPos in OldSub.GetCallerPositions())
  134. {
  135. if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub))
  136. {
  137. CallerSub.MarkForReJit();
  138. }
  139. }
  140. }
  141. ATranslatedSub Subroutine = Context.GetSubroutine();
  142. Subroutine.SetType(ATranslatedSubType.SubTier1);
  143. CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
  144. }
  145. private string GetSubName(long Position)
  146. {
  147. return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}");
  148. }
  149. private void PropagateName(ABlock[] Graph, string Name)
  150. {
  151. foreach (ABlock Block in Graph)
  152. {
  153. AOpCode LastOp = Block.GetLastOp();
  154. if (LastOp != null &&
  155. (LastOp.Emitter == AInstEmit.Bl ||
  156. LastOp.Emitter == AInstEmit.Blr))
  157. {
  158. SymbolTable.TryAdd(LastOp.Position + 4, Name);
  159. }
  160. }
  161. }
  162. }
  163. }