Decoder.cs 11 KB

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