X86Optimizer.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. using ARMeilleure.CodeGen.Optimizations;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using ARMeilleure.Translation;
  4. using System.Collections.Generic;
  5. using static ARMeilleure.IntermediateRepresentation.OperandHelper;
  6. using static ARMeilleure.IntermediateRepresentation.OperationHelper;
  7. namespace ARMeilleure.CodeGen.X86
  8. {
  9. static class X86Optimizer
  10. {
  11. public static void RunPass(ControlFlowGraph cfg)
  12. {
  13. var constants = new Dictionary<ulong, Operand>();
  14. Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source)
  15. {
  16. if (!constants.TryGetValue(source.Value, out var constant))
  17. {
  18. constant = Local(source.Type);
  19. Operation copyOp = Operation(Instruction.Copy, constant, source);
  20. block.Operations.AddBefore(operation, copyOp);
  21. constants.Add(source.Value, constant);
  22. }
  23. return constant;
  24. }
  25. for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
  26. {
  27. constants.Clear();
  28. Node nextNode;
  29. for (Node node = block.Operations.First; node != null; node = nextNode)
  30. {
  31. nextNode = node.ListNext;
  32. if (!(node is Operation operation))
  33. {
  34. continue;
  35. }
  36. // Insert copies for constants that can't fit on a 32-bits immediate.
  37. // Doing this early unblocks a few optimizations.
  38. if (operation.Instruction == Instruction.Add)
  39. {
  40. Operand src1 = operation.GetSource(0);
  41. Operand src2 = operation.GetSource(1);
  42. if (src1.Kind == OperandKind.Constant && (src1.Relocatable || CodeGenCommon.IsLongConst(src1)))
  43. {
  44. operation.SetSource(0, GetConstantCopy(block, operation, src1));
  45. }
  46. if (src2.Kind == OperandKind.Constant && (src2.Relocatable || CodeGenCommon.IsLongConst(src2)))
  47. {
  48. operation.SetSource(1, GetConstantCopy(block, operation, src2));
  49. }
  50. }
  51. // Try to fold something like:
  52. // shl rbx, 2
  53. // add rax, rbx
  54. // add rax, 0xcafe
  55. // mov rax, [rax]
  56. // Into:
  57. // mov rax, [rax+rbx*4+0xcafe]
  58. if (IsMemoryLoadOrStore(operation.Instruction))
  59. {
  60. OperandType type;
  61. if (operation.Destination != null)
  62. {
  63. type = operation.Destination.Type;
  64. }
  65. else
  66. {
  67. type = operation.GetSource(1).Type;
  68. }
  69. MemoryOperand memOp = GetMemoryOperandOrNull(operation.GetSource(0), type);
  70. if (memOp != null)
  71. {
  72. operation.SetSource(0, memOp);
  73. }
  74. }
  75. }
  76. }
  77. Optimizer.RemoveUnusedNodes(cfg);
  78. }
  79. private static MemoryOperand GetMemoryOperandOrNull(Operand addr, OperandType type)
  80. {
  81. Operand baseOp = addr;
  82. // First we check if the address is the result of a local X with 32-bits immediate
  83. // addition. If that is the case, then the baseOp is X, and the memory operand immediate
  84. // becomes the addition immediate. Otherwise baseOp keeps being the address.
  85. int imm = GetConstOp(ref baseOp);
  86. // Now we check if the baseOp is the result of a local Y with a local Z addition.
  87. // If that is the case, we now set baseOp to Y and indexOp to Z. We further check
  88. // if Z is the result of a left shift of local W by a value >= 0 and <= 3, if that
  89. // is the case, we set indexOp to W and adjust the scale value of the memory operand
  90. // to match that of the left shift.
  91. // There is one missed case, which is the address being a shift result, but this is
  92. // probably not worth optimizing as it should never happen.
  93. (Operand indexOp, Multiplier scale) = GetIndexOp(ref baseOp);
  94. // If baseOp is still equal to address, then there's nothing that can be optimized.
  95. if (baseOp == addr)
  96. {
  97. return null;
  98. }
  99. if (imm == 0 && scale == Multiplier.x1 && indexOp != null)
  100. {
  101. imm = GetConstOp(ref indexOp);
  102. }
  103. return MemoryOp(type, baseOp, indexOp, scale, imm);
  104. }
  105. private static int GetConstOp(ref Operand baseOp)
  106. {
  107. Operation operation = GetAsgOpWithInst(baseOp, Instruction.Add);
  108. if (operation == null)
  109. {
  110. return 0;
  111. }
  112. Operand src1 = operation.GetSource(0);
  113. Operand src2 = operation.GetSource(1);
  114. Operand constOp;
  115. Operand otherOp;
  116. if (src1.Kind == OperandKind.Constant && src2.Kind == OperandKind.LocalVariable)
  117. {
  118. constOp = src1;
  119. otherOp = src2;
  120. }
  121. else if (src1.Kind == OperandKind.LocalVariable && src2.Kind == OperandKind.Constant)
  122. {
  123. constOp = src2;
  124. otherOp = src1;
  125. }
  126. else
  127. {
  128. return 0;
  129. }
  130. // If we have addition by 64-bits constant, then we can't optimize it further,
  131. // as we can't encode a 64-bits immediate on the memory operand.
  132. if (CodeGenCommon.IsLongConst(constOp))
  133. {
  134. return 0;
  135. }
  136. baseOp = otherOp;
  137. return constOp.AsInt32();
  138. }
  139. private static (Operand, Multiplier) GetIndexOp(ref Operand baseOp)
  140. {
  141. Operand indexOp = null;
  142. Multiplier scale = Multiplier.x1;
  143. Operation addOp = GetAsgOpWithInst(baseOp, Instruction.Add);
  144. if (addOp == null)
  145. {
  146. return (indexOp, scale);
  147. }
  148. Operand src1 = addOp.GetSource(0);
  149. Operand src2 = addOp.GetSource(1);
  150. if (src1.Kind != OperandKind.LocalVariable || src2.Kind != OperandKind.LocalVariable)
  151. {
  152. return (indexOp, scale);
  153. }
  154. baseOp = src1;
  155. indexOp = src2;
  156. Operation shlOp = GetAsgOpWithInst(src1, Instruction.ShiftLeft);
  157. bool indexOnSrc2 = false;
  158. if (shlOp == null)
  159. {
  160. shlOp = GetAsgOpWithInst(src2, Instruction.ShiftLeft);
  161. indexOnSrc2 = true;
  162. }
  163. if (shlOp != null)
  164. {
  165. Operand shSrc = shlOp.GetSource(0);
  166. Operand shift = shlOp.GetSource(1);
  167. if (shSrc.Kind == OperandKind.LocalVariable && shift.Kind == OperandKind.Constant && shift.Value <= 3)
  168. {
  169. scale = shift.Value switch
  170. {
  171. 1 => Multiplier.x2,
  172. 2 => Multiplier.x4,
  173. 3 => Multiplier.x8,
  174. _ => Multiplier.x1
  175. };
  176. baseOp = indexOnSrc2 ? src1 : src2;
  177. indexOp = shSrc;
  178. }
  179. }
  180. return (indexOp, scale);
  181. }
  182. private static Operation GetAsgOpWithInst(Operand op, Instruction inst)
  183. {
  184. // If we have multiple assignments, folding is not safe
  185. // as the value may be different depending on the
  186. // control flow path.
  187. if (op.Assignments.Count != 1)
  188. {
  189. return null;
  190. }
  191. Node asgOp = op.Assignments[0];
  192. if (!(asgOp is Operation operation))
  193. {
  194. return null;
  195. }
  196. if (operation.Instruction != inst)
  197. {
  198. return null;
  199. }
  200. return operation;
  201. }
  202. private static bool IsMemoryLoadOrStore(Instruction inst)
  203. {
  204. return inst == Instruction.Load ||
  205. inst == Instruction.Load16 ||
  206. inst == Instruction.Load8 ||
  207. inst == Instruction.Store ||
  208. inst == Instruction.Store16 ||
  209. inst == Instruction.Store8;
  210. }
  211. }
  212. }