X86Optimizer.cs 8.4 KB

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