aboutsummaryrefslogblamecommitdiff
path: root/src/ARMeilleure/CodeGen/X86/CodeGenContext.cs
blob: 899487241c24c17872d0a41ac20e7934b16d501b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                             
                                             
                            
                
                      



                                 
                                        
                                                
 



                                                       

                                                         
                                             
 
                                                                                                               
         
                                                             
                                                    
                                      
                                                            
                                                                                                            
         
                                                                                                                          
         
                                                                                                                 

                                                                                                        
                                                                           
 
                                                                                                         



                                        
                                                                                                                      


                                   
                                                                                                                    

                              
                                                                                                
                                                                               

                                                                                                                      








                                                                         
                                                 




                                             
                                            


                                                                     
                                                       
         
                                                  
         
                                                              
 
                                 
             
                                                
             
 
                         

         
using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.IntermediateRepresentation;
using Ryujinx.Common.Memory;
using System.IO;
using System.Numerics;

namespace ARMeilleure.CodeGen.X86
{
    class CodeGenContext
    {
        private readonly Stream _stream;
        private readonly Operand[] _blockLabels;

        public int StreamOffset => (int)_stream.Length;

        public AllocationResult AllocResult { get; }

        public Assembler Assembler { get; }
        public BasicBlock CurrBlock { get; private set; }

        public int CallArgsRegionSize { get; }
        public int XmmSaveRegionSize { get; }

        public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
        {
            _stream = MemoryStreamManager.Shared.GetStream();
            _blockLabels = new Operand[blocksCount];

            AllocResult = allocResult;
            Assembler = new Assembler(_stream, relocatable);

            CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
            XmmSaveRegionSize  = xmmSaveRegionSize;
        }

        private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
        {
            // We need to add 8 bytes to the total size, as the call to this function already pushed 8 bytes (the
            // return address).
            int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
            int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;

            xmmSaveRegionSize = BitOperations.PopCount((uint)vecMask) * 16;

            int calleeSaveRegionSize = BitOperations.PopCount((uint)intMask) * 8 + xmmSaveRegionSize + 8;

            int argsCount = maxCallArgs;

            if (argsCount < 0)
            {
                // When the function has no calls, argsCount is -1. In this case, we don't need to allocate the shadow
                // space.
                argsCount = 0;
            }
            else if (argsCount < 4)
            {
                // The ABI mandates that the space for at least 4 arguments is reserved on the stack (this is called
                // shadow space).
                argsCount = 4;
            }

            // TODO: Align XMM save region to 16 bytes because unwinding on Windows requires it.
            int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;

            // TODO: Instead of always multiplying by 16 (the largest possible size of a variable, since a V128 has 16
            // bytes), we should calculate the exact size consumed by the arguments passed to the called functions on
            // the stack.
            int callArgsAndFrameSize = frameSize + argsCount * 16;

            // Ensure that the Stack Pointer will be aligned to 16 bytes.
            callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;

            return callArgsAndFrameSize - frameSize;
        }

        public void EnterBlock(BasicBlock block)
        {
            Assembler.MarkLabel(GetLabel(block));

            CurrBlock = block;
        }

        public void JumpTo(BasicBlock target)
        {
            Assembler.Jmp(GetLabel(target));
        }

        public void JumpTo(X86Condition condition, BasicBlock target)
        {
            Assembler.Jcc(condition, GetLabel(target));
        }

        private Operand GetLabel(BasicBlock block)
        {
            ref Operand label = ref _blockLabels[block.Index];

            if (label == default)
            {
                label = Operand.Factory.Label();
            }

            return label;
        }
    }
}