CodeGenContext.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. using ARMeilleure.CodeGen.RegisterAllocators;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using System.IO;
  4. using System.Numerics;
  5. namespace ARMeilleure.CodeGen.X86
  6. {
  7. class CodeGenContext
  8. {
  9. private readonly Stream _stream;
  10. private readonly Operand[] _blockLabels;
  11. public int StreamOffset => (int)_stream.Length;
  12. public AllocationResult AllocResult { get; }
  13. public Assembler Assembler { get; }
  14. public BasicBlock CurrBlock { get; private set; }
  15. public int CallArgsRegionSize { get; }
  16. public int XmmSaveRegionSize { get; }
  17. public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
  18. {
  19. _stream = new MemoryStream();
  20. _blockLabels = new Operand[blocksCount];
  21. AllocResult = allocResult;
  22. Assembler = new Assembler(_stream, relocatable);
  23. CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
  24. XmmSaveRegionSize = xmmSaveRegionSize;
  25. }
  26. private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
  27. {
  28. // We need to add 8 bytes to the total size, as the call to this function already pushed 8 bytes (the
  29. // return address).
  30. int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
  31. int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;
  32. xmmSaveRegionSize = BitOperations.PopCount((uint)vecMask) * 16;
  33. int calleeSaveRegionSize = BitOperations.PopCount((uint)intMask) * 8 + xmmSaveRegionSize + 8;
  34. int argsCount = maxCallArgs;
  35. if (argsCount < 0)
  36. {
  37. // When the function has no calls, argsCount is -1. In this case, we don't need to allocate the shadow
  38. // space.
  39. argsCount = 0;
  40. }
  41. else if (argsCount < 4)
  42. {
  43. // The ABI mandates that the space for at least 4 arguments is reserved on the stack (this is called
  44. // shadow space).
  45. argsCount = 4;
  46. }
  47. // TODO: Align XMM save region to 16 bytes because unwinding on Windows requires it.
  48. int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
  49. // TODO: Instead of always multiplying by 16 (the largest possible size of a variable, since a V128 has 16
  50. // bytes), we should calculate the exact size consumed by the arguments passed to the called functions on
  51. // the stack.
  52. int callArgsAndFrameSize = frameSize + argsCount * 16;
  53. // Ensure that the Stack Pointer will be aligned to 16 bytes.
  54. callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
  55. return callArgsAndFrameSize - frameSize;
  56. }
  57. public void EnterBlock(BasicBlock block)
  58. {
  59. Assembler.MarkLabel(GetLabel(block));
  60. CurrBlock = block;
  61. }
  62. public void JumpTo(BasicBlock target)
  63. {
  64. Assembler.Jmp(GetLabel(target));
  65. }
  66. public void JumpTo(X86Condition condition, BasicBlock target)
  67. {
  68. Assembler.Jcc(condition, GetLabel(target));
  69. }
  70. private Operand GetLabel(BasicBlock block)
  71. {
  72. ref Operand label = ref _blockLabels[block.Index];
  73. if (label == default)
  74. {
  75. label = Operand.Factory.Label();
  76. }
  77. return label;
  78. }
  79. }
  80. }