/// <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); }
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); }
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); }
/// <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);
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> /// 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; }