Decoder.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. using ARMeilleure.Decoders.Optimizations;
  2. using ARMeilleure.Instructions;
  3. using ARMeilleure.Memory;
  4. using ARMeilleure.State;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.Reflection.Emit;
  9. namespace ARMeilleure.Decoders
  10. {
  11. static class Decoder
  12. {
  13. // We define a limit on the number of instructions that a function may have,
  14. // this prevents functions being potentially too large, which would
  15. // take too long to compile and use too much memory.
  16. private const int MaxInstsPerFunction = 5000;
  17. // For lower code quality translation, we set a lower limit since we're blocking execution.
  18. private const int MaxInstsPerFunctionLowCq = 500;
  19. private delegate object MakeOp(InstDescriptor inst, ulong address, int opCode);
  20. private static ConcurrentDictionary<Type, MakeOp> _opActivators;
  21. static Decoder()
  22. {
  23. _opActivators = new ConcurrentDictionary<Type, MakeOp>();
  24. }
  25. public static Block[] DecodeBasicBlock(MemoryManager memory, ulong address, ExecutionMode mode)
  26. {
  27. Block block = new Block(address);
  28. FillBlock(memory, mode, block, ulong.MaxValue);
  29. return new Block[] { block };
  30. }
  31. public static Block[] DecodeFunction(MemoryManager memory, ulong address, ExecutionMode mode, bool highCq)
  32. {
  33. List<Block> blocks = new List<Block>();
  34. Queue<Block> workQueue = new Queue<Block>();
  35. Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
  36. int opsCount = 0;
  37. int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
  38. Block GetBlock(ulong blkAddress)
  39. {
  40. if (!visited.TryGetValue(blkAddress, out Block block))
  41. {
  42. if (opsCount > instructionLimit || !memory.IsMapped((long)blkAddress))
  43. {
  44. return null;
  45. }
  46. block = new Block(blkAddress);
  47. workQueue.Enqueue(block);
  48. visited.Add(blkAddress, block);
  49. }
  50. return block;
  51. }
  52. GetBlock(address);
  53. while (workQueue.TryDequeue(out Block currBlock))
  54. {
  55. // Check if the current block is inside another block.
  56. if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
  57. {
  58. Block nBlock = blocks[nBlkIndex];
  59. if (nBlock.Address == currBlock.Address)
  60. {
  61. throw new InvalidOperationException("Found duplicate block address on the list.");
  62. }
  63. nBlock.Split(currBlock);
  64. blocks.Insert(nBlkIndex + 1, currBlock);
  65. continue;
  66. }
  67. // If we have a block after the current one, set the limit address.
  68. ulong limitAddress = ulong.MaxValue;
  69. if (nBlkIndex != blocks.Count)
  70. {
  71. Block nBlock = blocks[nBlkIndex];
  72. int nextIndex = nBlkIndex + 1;
  73. if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
  74. {
  75. limitAddress = blocks[nextIndex].Address;
  76. }
  77. else if (nBlock.Address > currBlock.Address)
  78. {
  79. limitAddress = blocks[nBlkIndex].Address;
  80. }
  81. }
  82. FillBlock(memory, mode, currBlock, limitAddress);
  83. opsCount += currBlock.OpCodes.Count;
  84. if (currBlock.OpCodes.Count != 0)
  85. {
  86. // Set child blocks. "Branch" is the block the branch instruction
  87. // points to (when taken), "Next" is the block at the next address,
  88. // executed when the branch is not taken. For Unconditional Branches
  89. // (except BL/BLR that are sub calls) or end of executable, Next is null.
  90. OpCode lastOp = currBlock.GetLastOp();
  91. bool isCall = IsCall(lastOp);
  92. if (lastOp is IOpCodeBImm op && !isCall)
  93. {
  94. currBlock.Branch = GetBlock((ulong)op.Immediate);
  95. }
  96. if (!IsUnconditionalBranch(lastOp) || isCall)
  97. {
  98. currBlock.Next = GetBlock(currBlock.EndAddress);
  99. }
  100. }
  101. // Insert the new block on the list (sorted by address).
  102. if (blocks.Count != 0)
  103. {
  104. Block nBlock = blocks[nBlkIndex];
  105. blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
  106. }
  107. else
  108. {
  109. blocks.Add(currBlock);
  110. }
  111. }
  112. TailCallRemover.RunPass(address, blocks);
  113. return blocks.ToArray();
  114. }
  115. public static bool BinarySearch(List<Block> blocks, ulong address, out int index)
  116. {
  117. index = 0;
  118. int left = 0;
  119. int right = blocks.Count - 1;
  120. while (left <= right)
  121. {
  122. int size = right - left;
  123. int middle = left + (size >> 1);
  124. Block block = blocks[middle];
  125. index = middle;
  126. if (address >= block.Address && address < block.EndAddress)
  127. {
  128. return true;
  129. }
  130. if (address < block.Address)
  131. {
  132. right = middle - 1;
  133. }
  134. else
  135. {
  136. left = middle + 1;
  137. }
  138. }
  139. return false;
  140. }
  141. private static void FillBlock(
  142. MemoryManager memory,
  143. ExecutionMode mode,
  144. Block block,
  145. ulong limitAddress)
  146. {
  147. ulong address = block.Address;
  148. OpCode opCode;
  149. do
  150. {
  151. if (address >= limitAddress)
  152. {
  153. break;
  154. }
  155. opCode = DecodeOpCode(memory, address, mode);
  156. block.OpCodes.Add(opCode);
  157. address += (ulong)opCode.OpCodeSizeInBytes;
  158. }
  159. while (!(IsBranch(opCode) || IsException(opCode)));
  160. block.EndAddress = address;
  161. }
  162. private static bool IsBranch(OpCode opCode)
  163. {
  164. return opCode is OpCodeBImm ||
  165. opCode is OpCodeBReg || IsAarch32Branch(opCode);
  166. }
  167. private static bool IsUnconditionalBranch(OpCode opCode)
  168. {
  169. return opCode is OpCodeBImmAl ||
  170. opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode);
  171. }
  172. private static bool IsAarch32UnconditionalBranch(OpCode opCode)
  173. {
  174. if (!(opCode is OpCode32 op))
  175. {
  176. return false;
  177. }
  178. // Note: On ARM32, most instructions have conditional execution,
  179. // so there's no "Always" (unconditional) branch like on ARM64.
  180. // We need to check if the condition is "Always" instead.
  181. return IsAarch32Branch(op) && op.Cond >= Condition.Al;
  182. }
  183. private static bool IsAarch32Branch(OpCode opCode)
  184. {
  185. // Note: On ARM32, most ALU operations can write to R15 (PC),
  186. // so we must consider such operations as a branch in potential aswell.
  187. if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
  188. {
  189. return true;
  190. }
  191. // Same thing for memory operations. We have the cases where PC is a target
  192. // register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
  193. // a write back to PC (wback == true && Rn == 15), however the later may
  194. // be "undefined" depending on the CPU, so compilers should not produce that.
  195. if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
  196. {
  197. int rt, rn;
  198. bool wBack, isLoad;
  199. if (opCode is IOpCode32Mem opMem)
  200. {
  201. rt = opMem.Rt;
  202. rn = opMem.Rn;
  203. wBack = opMem.WBack;
  204. isLoad = opMem.IsLoad;
  205. // For the dual load, we also need to take into account the
  206. // case were Rt2 == 15 (PC).
  207. if (rt == 14 && opMem.Instruction.Name == InstName.Ldrd)
  208. {
  209. rt = RegisterAlias.Aarch32Pc;
  210. }
  211. }
  212. else if (opCode is IOpCode32MemMult opMemMult)
  213. {
  214. const int pcMask = 1 << RegisterAlias.Aarch32Pc;
  215. rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
  216. rn = opMemMult.Rn;
  217. wBack = opMemMult.PostOffset != 0;
  218. isLoad = opMemMult.IsLoad;
  219. }
  220. else
  221. {
  222. throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
  223. }
  224. if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
  225. (rn == RegisterAlias.Aarch32Pc && wBack))
  226. {
  227. return true;
  228. }
  229. }
  230. // Explicit branch instructions.
  231. return opCode is IOpCode32BImm ||
  232. opCode is IOpCode32BReg;
  233. }
  234. private static bool IsCall(OpCode opCode)
  235. {
  236. return opCode.Instruction.Name == InstName.Bl ||
  237. opCode.Instruction.Name == InstName.Blr ||
  238. opCode.Instruction.Name == InstName.Blx;
  239. }
  240. private static bool IsException(OpCode opCode)
  241. {
  242. return opCode.Instruction.Name == InstName.Brk ||
  243. opCode.Instruction.Name == InstName.Svc ||
  244. opCode.Instruction.Name == InstName.Trap ||
  245. opCode.Instruction.Name == InstName.Und;
  246. }
  247. public static OpCode DecodeOpCode(MemoryManager memory, ulong address, ExecutionMode mode)
  248. {
  249. int opCode = memory.ReadInt32((long)address);
  250. InstDescriptor inst;
  251. Type type;
  252. if (mode == ExecutionMode.Aarch64)
  253. {
  254. (inst, type) = OpCodeTable.GetInstA64(opCode);
  255. }
  256. else
  257. {
  258. if (mode == ExecutionMode.Aarch32Arm)
  259. {
  260. (inst, type) = OpCodeTable.GetInstA32(opCode);
  261. }
  262. else /* if (mode == ExecutionMode.Aarch32Thumb) */
  263. {
  264. (inst, type) = OpCodeTable.GetInstT32(opCode);
  265. }
  266. }
  267. if (type != null)
  268. {
  269. return MakeOpCode(inst, type, address, opCode);
  270. }
  271. else
  272. {
  273. return new OpCode(inst, address, opCode);
  274. }
  275. }
  276. private static OpCode MakeOpCode(InstDescriptor inst, Type type, ulong address, int opCode)
  277. {
  278. MakeOp createInstance = _opActivators.GetOrAdd(type, CacheOpActivator);
  279. return (OpCode)createInstance(inst, address, opCode);
  280. }
  281. private static MakeOp CacheOpActivator(Type type)
  282. {
  283. Type[] argTypes = new Type[] { typeof(InstDescriptor), typeof(ulong), typeof(int) };
  284. DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes);
  285. ILGenerator generator = mthd.GetILGenerator();
  286. generator.Emit(OpCodes.Ldarg_0);
  287. generator.Emit(OpCodes.Ldarg_1);
  288. generator.Emit(OpCodes.Ldarg_2);
  289. generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes));
  290. generator.Emit(OpCodes.Ret);
  291. return (MakeOp)mthd.CreateDelegate(typeof(MakeOp));
  292. }
  293. }
  294. }