CodeGenContext.cs 3.7 KB

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