/// <summary>
        /// Expands method call instruction represented by the context to perform the method call.
        /// </summary>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        public override void MakeCall(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context)
        {
            /*
             * Calling convention is right-to-left, pushed on the stack. Return value in EAX for integral
             * types 4 bytes or less, XMM0 for floating point and EAX:EDX for 64-bit. If this is a method
             * of a type, the this argument is moved to ECX right before the call.
             * If return value is value type, a stack local is allocated as a hidden parameter in caller
             * stack, the callee will then store the return value in the allocated space
             * The return value is the first parameter (even before this)
             * The callee will place the address of the return value into EAX and the caller will then
             * either retrieve the return value using compound move or will let one of the callers higher
             * in the caller chain handle the retrieval of the return value using compound move.
             */

            Operand target = context.Operand1;
            Operand result = context.Result;
            MosaMethod method = context.InvokeMethod;

            Debug.Assert(method != null, context.ToString());

            Operand scratch = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.Pointer, scratchRegister);

            List<Operand> operands = BuildOperands(context);

            int stackSize = CalculateStackSizeForParameters(typeLayout, architecture, operands, method);

            context.Empty();

            int returnSize = 0;
            if (typeLayout.IsCompoundType(method.Signature.ReturnType))
            {
                returnSize = typeLayout.GetTypeSize(method.Signature.ReturnType);
            }

            if (stackSize != 0 || returnSize != 0)
            {
                ReserveStackSizeForCall(typeLayout.TypeSystem, context, returnSize + stackSize, scratch);
                PushOperands(compiler, typeLayout, context, method, operands, returnSize + stackSize, scratch);
            }

            // the mov/call two-instructions combo is to help facilitate the register allocator
            architecture.InsertMoveInstruction(context, scratch, target);
            architecture.InsertCallInstruction(context, scratch);

            CleanupReturnValue(compiler, typeLayout, context, result);
            FreeStackAfterCall(typeLayout.TypeSystem, context, returnSize + stackSize);
        }
Example #2
0
        protected BasicBlocks CopyInstructions()
        {
            var newBasicBlocks = new BasicBlocks();
            var mapBlocks      = new Dictionary <BasicBlock, BasicBlock>(BasicBlocks.Count);
            var map            = new Dictionary <Operand, Operand>();

            foreach (var block in BasicBlocks)
            {
                var newBlock = newBasicBlocks.CreateBlock(block.Label);
                mapBlocks.Add(block, newBlock);
            }

            var newPrologueBlock = newBasicBlocks.GetByLabel(BasicBlock.PrologueLabel);

            foreach (var operand in MethodCompiler.Parameters)
            {
                if (operand.Definitions.Count > 0)
                {
                    var newOp = Map(operand, map);

                    var newOperand = Operand.CreateVirtualRegister(operand.Type, -operand.Index);

                    var moveInstruction = MosaTypeLayout.IsStoredOnStack(newOperand.Type)
                                                ? IRInstruction.MoveCompound
                                                : GetMoveInstruction(newOperand.Type);

                    var moveNode = new InstructionNode(moveInstruction, newOperand, newOp);

                    newPrologueBlock.BeforeLast.Insert(moveNode);

                    // redirect map from parameter to virtual register going forward
                    map.Remove(operand);
                    map.Add(operand, newOperand);
                }
            }

            foreach (var block in BasicBlocks)
            {
                var newBlock = newBasicBlocks.GetByLabel(block.Label);

                for (var node = block.AfterFirst; !node.IsBlockEndInstruction; node = node.Next)
                {
                    if (node.IsEmpty)
                    {
                        continue;
                    }

                    var newNode = new InstructionNode(node.Instruction, node.OperandCount, node.ResultCount)
                    {
                        ConditionCode = node.ConditionCode
                    };
                    if (node.BranchTargets != null)
                    {
                        // copy targets
                        foreach (var target in node.BranchTargets)
                        {
                            newNode.AddBranchTarget(mapBlocks[target]);
                        }
                    }

                    // copy results
                    for (int i = 0; i < node.ResultCount; i++)
                    {
                        var op    = node.GetResult(i);
                        var newOp = Map(op, map);

                        newNode.SetResult(i, newOp);
                    }

                    // copy operands
                    for (int i = 0; i < node.OperandCount; i++)
                    {
                        var op    = node.GetOperand(i);
                        var newOp = Map(op, map);

                        newNode.SetOperand(i, newOp);
                    }

                    // copy other
                    if (node.MosaType != null)
                    {
                        newNode.MosaType = node.MosaType;
                    }

                    if (node.MosaField != null)
                    {
                        newNode.MosaField = node.MosaField;
                    }

                    newBlock.BeforeLast.Insert(newNode);
                }
            }

            var trace = CreateTraceLog("InlineMap");

            if (trace != null)
            {
                foreach (var entry in map)
                {
                    trace.Log($"{entry.Value} from: {entry.Key}");
                }
            }

            return(newBasicBlocks);
        }
Example #3
0
        public bool CanInline(MethodData method)
        {
            if (method.HasDoNotInlineAttribute)
            {
                return(false);
            }

            if (method.IsMethodImplementationReplaced)
            {
                return(false);
            }

            if (method.HasProtectedRegions)
            {
                return(false);
            }

            //if (method.HasLoops)
            //	return false;

            if (method.IsVirtual && !method.IsDevirtualized)
            {
                return(false);
            }

            // current implementation limitation - can't include methods with addressOf instruction
            if (method.HasAddressOfInstruction)
            {
                return(false);
            }

            if (method.NonIRInstructionCount > 0)
            {
                return(false);
            }

            if (method.DoNotInline)
            {
                return(false);
            }

            if (MethodData.CompileCount >= MaximumCompileCount)
            {
                return(false);                  // too many compiles - cyclic loop suspected
            }
            // methods with aggressive inline attribute will double the IR instruction count
            int max = method.HasAggressiveInliningAttribute ? (CompilerOptions.InlinedIRMaximum * 2) : CompilerOptions.InlinedIRMaximum;

            if ((method.IRInstructionCount - method.IRStackParameterInstructionCount) > max)
            {
                return(false);
            }

            var returnType = method.Method.Signature.ReturnType;

            // FIXME: Add rational
            if (MosaTypeLayout.IsStoredOnStack(returnType) && !returnType.IsUI8 && !returnType.IsR8)
            {
                return(false);
            }

            return(true);
        }
Example #4
0
        /// <summary>
        /// Gets the type memory requirements.
        /// </summary>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="type">The type.</param>
        /// <param name="size">Receives the memory size of the type.</param>
        /// <param name="alignment">Receives alignment requirements of the type.</param>
        public override void GetTypeRequirements(MosaTypeLayout typeLayout, MosaType type, out int size, out int alignment)
        {
            alignment = 4;

            size = type.IsValueType ? typeLayout.GetTypeSize(type) : 4;
        }
 /// <summary>
 /// Expands method call instruction represented by the context to perform the method call.
 /// </summary>
 /// <param name="compiler">The compiler.</param>
 /// <param name="typeLayout">The type layouts.</param>
 /// <param name="context">The context.</param>
 public abstract void MakeCall(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context);
Example #6
0
        private bool StaticCanNotInline(MethodData methodData)
        {
            if (InlineExplicitOnly && !methodData.HasAggressiveInliningAttribute)
            {
                return(true);
            }

            if (methodData.HasDoNotInlineAttribute)
            {
                return(true);
            }

            if (methodData.IsMethodImplementationReplaced)
            {
                return(true);
            }

            if (methodData.HasProtectedRegions)
            {
                return(true);
            }

            if (methodData.HasMethodPointerReferenced)
            {
                return(true);
            }

            var method = methodData.Method;

            if (method.IsVirtual && !methodData.IsDevirtualized)
            {
                return(true);
            }

            if (methodData.DoNotInline)
            {
                return(true);
            }

            if (method.DeclaringType.IsValueType &&
                method.IsVirtual &&
                !method.IsConstructor &&
                !method.IsStatic)
            {
                return(true);
            }

            var returnType = methodData.Method.Signature.ReturnType;

            // FIXME: Add rational
            if (!MosaTypeLayout.CanFitInRegister(returnType) && !returnType.IsUI8 && !returnType.IsR8)
            {
                return(true);
            }

            // FUTURE: Don't hardcode namepsace
            if (((method.MethodAttributes & MosaMethodAttributes.Public) == MosaMethodAttributes.Public) && method.DeclaringType.BaseType != null && method.DeclaringType.BaseType.Namespace == "Mosa.UnitTests")
            {
                return(true);
            }

            return(false);
        }
 /// <summary>
 /// Pushes the specified instructions.
 /// </summary>
 /// <param name="compiler">The compiler.</param>
 /// <param name="typeLayout">The type layout.</param>
 /// <param name="context">The context.</param>
 /// <param name="op">The op.</param>
 /// <param name="stackSize">Size of the stack.</param>
 /// <param name="parameterSize">Size of the parameter.</param>
 /// <param name="scratch">The scratch.</param>
 private void Push(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand op, int stackSize, int parameterSize, Operand scratch)
 {
     if (op.Is64BitInteger)
     {
         architecture.InsertMoveInstruction(context, Operand.CreateMemoryAddress(typeLayout.TypeSystem.BuiltIn.I4, scratch, stackSize), op.Low);
         architecture.InsertMoveInstruction(context, Operand.CreateMemoryAddress(typeLayout.TypeSystem.BuiltIn.I4, scratch, stackSize + 4), op.High);
     }
     else if (op.IsR)
     {
         if (op.IsR8 && parameterSize == 4)
         {
             Operand op2 = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.R4, returnFloatingPointRegister);
             architecture.InsertMoveInstruction(context, op2, op);
             architecture.InsertMoveInstruction(context, Operand.CreateMemoryAddress(scratch.Type, scratch, stackSize), op2);
         }
         else
         {
             architecture.InsertMoveInstruction(context, Operand.CreateMemoryAddress(scratch.Type, scratch, stackSize), op);
         }
     }
     else if (typeLayout.IsCompoundType(op.Type))
     {
         architecture.InsertCompoundMoveInstruction(compiler, context, Operand.CreateMemoryAddress(op.Type, scratch, stackSize), op, typeLayout.GetTypeSize(op.Type));
     }
     else
     {
         architecture.InsertMoveInstruction(context, Operand.CreateMemoryAddress(op.Type, scratch, stackSize), op);
     }
 }
        /// <summary>
        /// Calculates the remaining space.
        /// </summary>
        /// <param name="compiler">The compiler.</param>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        /// <param name="method">The method.</param>
        /// <param name="operands">The operand stack.</param>
        /// <param name="space">The space.</param>
        /// <param name="scratch">The scratch.</param>
        private void PushOperands(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, MosaMethod method, List<Operand> operands, int space, Operand scratch)
        {
            Debug.Assert((method.Signature.Parameters.Count + (method.HasThis ? 1 : 0) == operands.Count) ||
                        (method.DeclaringType.IsDelegate && method.Signature.Parameters.Count == operands.Count));

            int offset = method.Signature.Parameters.Count - operands.Count;

            for (int index = operands.Count - 1; index >= 0; index--)
            {
                Operand operand = operands[index];

                MosaType param = (index + offset >= 0) ? method.Signature.Parameters[index + offset].ParameterType : null;

                int size, alignment;

                if (param != null && operand.IsR8 && param.IsR4)
                {
                    architecture.GetTypeRequirements(typeLayout, param, out size, out alignment);
                }
                else
                {
                    architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment);
                }

                size = Alignment.AlignUp(size, alignment);

                space -= size;

                Push(compiler, typeLayout, context, operand, space, size, scratch);
            }
        }
        /// <summary>
        /// Requests the calling convention to create an appropriate move instruction to populate the return
        /// value of a method.
        /// </summary>
        /// <param name="compiler">The compiler.</param>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        /// <param name="operand">The operand, that's holding the return value.</param>
        public override void SetReturnValue(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand operand)
        {
            int size, alignment;
            architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment);
            size = Alignment.AlignUp(size, alignment);

            if (operand.IsR4)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand);
            }
            else if (operand.IsR8)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand);
            }
            else if (operand.IsLong)
            {
                MosaType highType = (operand.IsI8) ? typeLayout.TypeSystem.BuiltIn.I4 : typeLayout.TypeSystem.BuiltIn.U4;

                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.U4, return32BitRegister), operand.Low);
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(highType, return64BitRegister), operand.High);
            }
            else if (size == 4 || size == 2 || size == 1)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, return32BitRegister), operand);
            }
            else if (typeLayout.IsCompoundType(operand.Type))
            {
                architecture.InsertAddressOfInstruction(context, Operand.CreateCPURegister(operand.Type, return32BitRegister), operand);
            }
        }
        /// <summary>
        /// Cleanups the return value.
        /// </summary>
        /// <param name="compiler">The compiler.</param>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        /// <param name="result">The result.</param>
        private void CleanupReturnValue(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand result)
        {
            if (result == null)
                return;

            if (result.IsR)
            {
                Operand returnFP = Operand.CreateCPURegister(result.Type, returnFloatingPointRegister);
                context.AppendInstruction(IRInstruction.Gen, returnFP);
                architecture.InsertMoveInstruction(context, result, returnFP);
            }
            else if (result.Is64BitInteger)
            {
                Operand returnLow = Operand.CreateCPURegister(result.Type, return32BitRegister);
                Operand returnHigh = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.U4, return64BitRegister);
                context.AppendInstruction(IRInstruction.Gen, returnLow);
                context.AppendInstruction(IRInstruction.Gen, returnHigh);
                architecture.InsertMoveInstruction(context, result.Low, returnLow);
                architecture.InsertMoveInstruction(context, result.High, returnHigh);
            }
            else if (typeLayout.IsCompoundType(result.Type))
            {
                int size = typeLayout.GetTypeSize(result.Type);
                Operand returnLow = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.Pointer, return32BitRegister);
                context.AppendInstruction(IRInstruction.Gen, returnLow);
                architecture.InsertCompoundMoveInstruction(compiler, context, result, Operand.CreateMemoryAddress(result.Type, returnLow, 0), size);
            }
            else
            {
                Operand returnLow = Operand.CreateCPURegister(result.Type, return32BitRegister);
                context.AppendInstruction(IRInstruction.Gen, returnLow);
                architecture.InsertMoveInstruction(context, result, returnLow);
            }
        }
 /// <summary>
 /// Expands method call instruction represented by the context to perform the method call.
 /// </summary>
 /// <param name="compiler">The compiler.</param>
 /// <param name="typeLayout">The type layouts.</param>
 /// <param name="context">The context.</param>
 public abstract void MakeCall(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context);
 /// <summary>
 /// Requests the calling convention to create an appropriate move instruction to populate the return
 /// value of a method.
 /// </summary>
 /// <param name="compiler">The compiler.</param>
 /// <param name="typeLayout">The type layouts.</param>
 /// <param name="context">The context.</param>
 /// <param name="operand">The operand, that's holding the return value.</param>
 public abstract void SetReturnValue(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand operand);
 /// <summary>
 /// Requests the calling convention to create an appropriate move instruction to populate the return
 /// value of a method.
 /// </summary>
 /// <param name="compiler">The compiler.</param>
 /// <param name="typeLayout">The type layouts.</param>
 /// <param name="context">The context.</param>
 /// <param name="operand">The operand, that's holding the return value.</param>
 public abstract void SetReturnValue(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, Operand operand);
        /// <summary>
        /// Requests the calling convention to create an appropriate move instruction to populate the return
        /// value of a method.
        /// </summary>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        /// <param name="operand">The operand, that's holding the return value.</param>
        public override void SetReturnValue(MosaTypeLayout typeLayout, Context context, Operand operand)
        {
            int size, alignment;
            architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment);

            if (size == 4 || size == 2 || size == 1)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, return32BitRegister), operand);
            }
            else if (operand.IsR4)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand);
            }
            else if (operand.IsR8)
            {
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(operand.Type, returnFloatingPointRegister), operand);
            }
            else if (operand.IsLong)
            {
                MosaType highType = (operand.IsI8) ? typeLayout.TypeSystem.BuiltIn.I4 : typeLayout.TypeSystem.BuiltIn.U4;

                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.U4, return32BitRegister), operand.Low);
                architecture.InsertMoveInstruction(context, Operand.CreateCPURegister(highType, return64BitRegister), operand.High);
            }
            else if (typeLayout.IsCompoundType(operand.Type))
            {
                Operand stackBaseReg = Operand.CreateCPURegister(typeLayout.TypeSystem.BuiltIn.Pointer, architecture.StackFrameRegister);
                architecture.InsertCompoundMoveInstruction(context, Operand.CreateMemoryAddress(operand.Type, stackBaseReg, 8), operand, size);
            }
        }
        /// <summary>
        /// Calculates the remaining space.
        /// </summary>
        /// <param name="compiler">The compiler.</param>
        /// <param name="typeLayout">The type layouts.</param>
        /// <param name="context">The context.</param>
        /// <param name="operands">The operand stack.</param>
        /// <param name="space">The space.</param>
        /// <param name="scratch">The scratch.</param>
        private void PushOperands(BaseMethodCompiler compiler, MosaTypeLayout typeLayout, Context context, List<Operand> operands, int space, Operand scratch)
        {
            foreach (var operand in operands)
            {
                int size, alignment;

                architecture.GetTypeRequirements(typeLayout, operand.Type, out size, out alignment);

                size = Alignment.AlignUp(size, alignment);

                space -= size;

                Push(compiler, typeLayout, context, operand, space, size, scratch);
            }
        }
        private static int CalculateReturnSize(MosaTypeLayout typeLayout, Operand result)
        {
            if (result == null)
                return 0;

            if (typeLayout.IsCompoundType(result.Type))
            {
                return typeLayout.GetTypeSize(result.Type);
            }

            return 0;
        }
        private static int CalculateReturnSize(MosaTypeLayout typeLayout, MosaMethod method)
        {
            if (typeLayout.IsCompoundType(method.Signature.ReturnType))
            {
                return typeLayout.GetTypeSize(method.Signature.ReturnType);
            }

            return 0;
        }