CodeGenContext.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. using ARMeilleure.CodeGen.RegisterAllocators;
  2. using ARMeilleure.Common;
  3. using ARMeilleure.IntermediateRepresentation;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. namespace ARMeilleure.CodeGen.X86
  8. {
  9. class CodeGenContext
  10. {
  11. private const int ReservedBytesForJump = 1;
  12. private Stream _stream;
  13. public int StreamOffset => (int)_stream.Length;
  14. public AllocationResult AllocResult { get; }
  15. public Assembler Assembler { get; }
  16. public BasicBlock CurrBlock { get; private set; }
  17. public int CallArgsRegionSize { get; }
  18. public int XmmSaveRegionSize { get; }
  19. private long[] _blockOffsets;
  20. private struct Jump
  21. {
  22. public bool IsConditional { get; }
  23. public X86Condition Condition { get; }
  24. public BasicBlock Target { get; }
  25. public long JumpPosition { get; }
  26. public long RelativeOffset { get; set; }
  27. public int InstSize { get; set; }
  28. public Jump(BasicBlock target, long jumpPosition)
  29. {
  30. IsConditional = false;
  31. Condition = 0;
  32. Target = target;
  33. JumpPosition = jumpPosition;
  34. RelativeOffset = 0;
  35. InstSize = 0;
  36. }
  37. public Jump(X86Condition condition, BasicBlock target, long jumpPosition)
  38. {
  39. IsConditional = true;
  40. Condition = condition;
  41. Target = target;
  42. JumpPosition = jumpPosition;
  43. RelativeOffset = 0;
  44. InstSize = 0;
  45. }
  46. }
  47. private List<Jump> _jumps;
  48. private X86Condition _jNearCondition;
  49. private long _jNearPosition;
  50. private int _jNearLength;
  51. public CodeGenContext(Stream stream, AllocationResult allocResult, int maxCallArgs, int blocksCount)
  52. {
  53. _stream = stream;
  54. AllocResult = allocResult;
  55. Assembler = new Assembler(stream);
  56. CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
  57. XmmSaveRegionSize = xmmSaveRegionSize;
  58. _blockOffsets = new long[blocksCount];
  59. _jumps = new List<Jump>();
  60. }
  61. private int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
  62. {
  63. // We need to add 8 bytes to the total size, as the call to this
  64. // function already pushed 8 bytes (the return address).
  65. int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
  66. int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;
  67. xmmSaveRegionSize = BitUtils.CountBits(vecMask) * 16;
  68. int calleeSaveRegionSize = BitUtils.CountBits(intMask) * 8 + xmmSaveRegionSize + 8;
  69. int argsCount = maxCallArgs;
  70. if (argsCount < 0)
  71. {
  72. // When the function has no calls, argsCount is -1.
  73. // In this case, we don't need to allocate the shadow space.
  74. argsCount = 0;
  75. }
  76. else if (argsCount < 4)
  77. {
  78. // The ABI mandates that the space for at least 4 arguments
  79. // is reserved on the stack (this is called shadow space).
  80. argsCount = 4;
  81. }
  82. int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
  83. // TODO: Instead of always multiplying by 16 (the largest possible size of a variable,
  84. // since a V128 has 16 bytes), we should calculate the exact size consumed by the
  85. // arguments passed to the called functions on the stack.
  86. int callArgsAndFrameSize = frameSize + argsCount * 16;
  87. // Ensure that the Stack Pointer will be aligned to 16 bytes.
  88. callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
  89. return callArgsAndFrameSize - frameSize;
  90. }
  91. public void EnterBlock(BasicBlock block)
  92. {
  93. _blockOffsets[block.Index] = _stream.Position;
  94. CurrBlock = block;
  95. }
  96. public void JumpTo(BasicBlock target)
  97. {
  98. _jumps.Add(new Jump(target, _stream.Position));
  99. WritePadding(ReservedBytesForJump);
  100. }
  101. public void JumpTo(X86Condition condition, BasicBlock target)
  102. {
  103. _jumps.Add(new Jump(condition, target, _stream.Position));
  104. WritePadding(ReservedBytesForJump);
  105. }
  106. public void JumpToNear(X86Condition condition)
  107. {
  108. _jNearCondition = condition;
  109. _jNearPosition = _stream.Position;
  110. _jNearLength = Assembler.GetJccLength(0);
  111. _stream.Seek(_jNearLength, SeekOrigin.Current);
  112. }
  113. public void JumpHere()
  114. {
  115. long currentPosition = _stream.Position;
  116. _stream.Seek(_jNearPosition, SeekOrigin.Begin);
  117. long offset = currentPosition - (_jNearPosition + _jNearLength);
  118. Debug.Assert(_jNearLength == Assembler.GetJccLength(offset), "Relative offset doesn't fit on near jump.");
  119. Assembler.Jcc(_jNearCondition, offset);
  120. _stream.Seek(currentPosition, SeekOrigin.Begin);
  121. }
  122. private void WritePadding(int size)
  123. {
  124. while (size-- > 0)
  125. {
  126. _stream.WriteByte(0);
  127. }
  128. }
  129. public byte[] GetCode()
  130. {
  131. // Write jump relative offsets.
  132. bool modified;
  133. do
  134. {
  135. modified = false;
  136. for (int index = 0; index < _jumps.Count; index++)
  137. {
  138. Jump jump = _jumps[index];
  139. long jumpTarget = _blockOffsets[jump.Target.Index];
  140. long offset = jumpTarget - jump.JumpPosition;
  141. if (offset < 0)
  142. {
  143. for (int index2 = index - 1; index2 >= 0; index2--)
  144. {
  145. Jump jump2 = _jumps[index2];
  146. if (jump2.JumpPosition < jumpTarget)
  147. {
  148. break;
  149. }
  150. offset -= jump2.InstSize - ReservedBytesForJump;
  151. }
  152. }
  153. else
  154. {
  155. for (int index2 = index + 1; index2 < _jumps.Count; index2++)
  156. {
  157. Jump jump2 = _jumps[index2];
  158. if (jump2.JumpPosition >= jumpTarget)
  159. {
  160. break;
  161. }
  162. offset += jump2.InstSize - ReservedBytesForJump;
  163. }
  164. offset -= ReservedBytesForJump;
  165. }
  166. if (jump.IsConditional)
  167. {
  168. jump.InstSize = Assembler.GetJccLength(offset);
  169. }
  170. else
  171. {
  172. jump.InstSize = Assembler.GetJmpLength(offset);
  173. }
  174. // The jump is relative to the next instruction, not the current one.
  175. // Since we didn't know the next instruction address when calculating
  176. // the offset (as the size of the current jump instruction was not know),
  177. // we now need to compensate the offset with the jump instruction size.
  178. // It's also worth to note that:
  179. // - This is only needed for backward jumps.
  180. // - The GetJmpLength and GetJccLength also compensates the offset
  181. // internally when computing the jump instruction size.
  182. if (offset < 0)
  183. {
  184. offset -= jump.InstSize;
  185. }
  186. if (jump.RelativeOffset != offset)
  187. {
  188. modified = true;
  189. }
  190. jump.RelativeOffset = offset;
  191. _jumps[index] = jump;
  192. }
  193. }
  194. while (modified);
  195. // Write the code, ignoring the dummy bytes after jumps, into a new stream.
  196. _stream.Seek(0, SeekOrigin.Begin);
  197. using (MemoryStream codeStream = new MemoryStream())
  198. {
  199. Assembler assembler = new Assembler(codeStream);
  200. byte[] buffer;
  201. for (int index = 0; index < _jumps.Count; index++)
  202. {
  203. Jump jump = _jumps[index];
  204. buffer = new byte[jump.JumpPosition - _stream.Position];
  205. _stream.Read(buffer, 0, buffer.Length);
  206. _stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
  207. codeStream.Write(buffer);
  208. if (jump.IsConditional)
  209. {
  210. assembler.Jcc(jump.Condition, jump.RelativeOffset);
  211. }
  212. else
  213. {
  214. assembler.Jmp(jump.RelativeOffset);
  215. }
  216. }
  217. buffer = new byte[_stream.Length - _stream.Position];
  218. _stream.Read(buffer, 0, buffer.Length);
  219. codeStream.Write(buffer);
  220. return codeStream.ToArray();
  221. }
  222. }
  223. }
  224. }