/// <summary> /// Moves a float argument to the stack /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="argumentIndex">The argument index</param> private void MoveFloatArgumentToStack(CompilationData compilationData, int argumentIndex) { var assembler = compilationData.Assembler; int argStackOffset = -(1 + argumentIndex) * Assembler.RegisterSize; if (argumentIndex >= numRegisterArguments) { int stackArgumentIndex = this.GetStackArgumentIndex(compilationData, argumentIndex); int stackAlignment = this.CalculateStackAlignment( compilationData, compilationData.Function.Definition.Parameters); assembler.Move( Register.AX, new MemoryOperand( Register.BP, Assembler.RegisterSize * (6 + stackArgumentIndex))); assembler.Move(new MemoryOperand(Register.BP, argStackOffset), Register.AX); } else { assembler.Move( new MemoryOperand(Register.BP, argStackOffset), this.floatArgumentRegisters[argumentIndex]); } }
/// <summary> /// Handles the given function call arguments /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="toCall">The function to call</param> public void CallFunctionArguments(CompilationData compilationData, FunctionDefinition toCall) { for (int arg = toCall.Parameters.Count - 1; arg >= 0; arg--) { this.CallFunctionArgument(compilationData, arg, toCall.Parameters[arg], toCall); } }
/// <summary> /// Adds a stack overflow check /// </summary> /// <param name="compilationData">The function compilation data</param> /// <param name="callStackEnd">The end of the call stack</param> public void AddStackOverflowCheck(CompilationData compilationData, IntPtr callStackEnd) { var assembler = compilationData.Assembler; //Move the end of the call stack to register assembler.Move(Register.CX, callStackEnd.ToInt64()); //Compare the top and the end of the stack assembler.Compare(Register.AX, Register.CX); //Jump to handler if overflow assembler.Jump(JumpCondition.GreaterThanOrEqual, 0); compilationData.UnresolvedNativeLabels.Add(assembler.GeneratedCode.Count - 6, this.stackOverflowCheckHandler); }
/// <summary> /// Adds an array creation check /// </summary> /// <param name="compilationData">The function compilation data</param> /// <param name="sizeRegister">The register where the size is stored</param> /// <param name="compareRegister">The register used for comparison</param> public void AddArrayCreationCheck( CompilationData compilationData, Register sizeRegister = IntCallingConventions.Argument1, Register compareRegister = Register.R11) { var assembler = compilationData.Assembler; assembler.Xor(compareRegister, compareRegister); //Zero the register assembler.Compare(compareRegister, sizeRegister); //Jump to handler if invalid assembler.Jump(JumpCondition.GreaterThan, 0); compilationData.UnresolvedNativeLabels.Add(assembler.GeneratedCode.Count - 6, this.arrayCreationCheckHandler); }
/// <summary> /// Adds a null check /// </summary> /// <param name="compilationData">The function compilation data</param> /// <param name="refRegister">The register where the reference is located</param> /// <param name="compareRegister">The register where the result of the comparison will be stored</param> public void AddNullCheck( CompilationData compilationData, Register refRegister = Register.AX, Register compareRegister = Register.R11) { var assembler = compilationData.Assembler; //Compare the reference with null assembler.Xor(compareRegister, compareRegister); //Zero the register assembler.Compare(refRegister, compareRegister); //Jump to handler if null assembler.Jump(JumpCondition.Equal, 0); compilationData.UnresolvedNativeLabels.Add(assembler.GeneratedCode.Count - 6, this.nullCheckHandler); }
/// <summary> /// Compiles the given function /// </summary> /// <param name="function">The function to compile</param> /// <returns>A pointer to the start of the compiled function</returns> public IntPtr Compile(ManagedFunction function) { //Compile the function var compilationData = new CompilationData(function); this.compiledFunctions.Add(function, compilationData); this.codeGenerator.CompileFunction(compilationData); //Allocate native memory. The instructions will be copied later when all symbols has been resolved. var memory = this.MemoryManager.AllocateCode(function.GeneratedCode.Count); function.Definition.SetEntryPoint(memory); return(memory); }
/// <summary> /// Makes the return value for a function /// </summary> /// <param name="compilationData">The compilation data</param> public void MakeReturnValue(CompilationData compilationData) { var def = compilationData.Function.Definition; if (!def.ReturnType.IsPrimitiveType(PrimitiveTypes.Void)) { if (def.ReturnType.IsPrimitiveType(PrimitiveTypes.Float)) { compilationData.OperandStack.PopRegister(FloatCallingConventions.ReturnValue); } else { compilationData.OperandStack.PopRegister(IntCallingConventions.ReturnValue); } } }
/// <summary> /// Adds an array bounds check /// </summary> /// <param name="compilationData">The function compilation data</param> /// <param name="refRegister">The register where the array is stored</param> /// <param name="indexRegister">The register where the index is stored</param> /// <param name="compareRegister">The register used for comparison</param> public void AddArrayBoundsCheck( CompilationData compilationData, Register refRegister = Register.AX, Register indexRegister = Register.R10, Register compareRegister = Register.CX) { var assembler = compilationData.Assembler; //Get the size of the array (an int) assembler.Move(compareRegister, new MemoryOperand(refRegister), DataSize.Size32); //Compare the index and size assembler.Compare(indexRegister, compareRegister); //Jump to handler if out of bounds. By using an unsigned comparison, we only need one check. assembler.Jump(JumpCondition.GreaterThanOrEqual, 0, true); compilationData.UnresolvedNativeLabels.Add(assembler.GeneratedCode.Count - 6, this.arrayBoundsCheckHandler); }
/// <summary> /// Resolves the branches for the given function /// </summary> /// <param name="compilationData">The compilation data</param> private void ResolveBranches(CompilationData compilationData) { foreach (var branch in compilationData.UnresolvedBranches) { int source = branch.Key; var branchTarget = branch.Value; int nativeTarget = compilationData.InstructionMapping[branchTarget.Target]; //Calculate the native jump location int target = nativeTarget - source - branchTarget.InstructionSize; //Update the source with the native target int sourceOffset = source + branchTarget.InstructionSize - sizeof(int); NativeHelpers.SetInt(compilationData.Function.GeneratedCode, sourceOffset, target); } compilationData.UnresolvedBranches.Clear(); }
/// <summary> /// Resolves the native labels for the given function /// </summary> /// <param name="compilationData">The compilation data</param> private void ResolveNativeLabels(CompilationData compilationData) { var generatedCode = compilationData.Function.GeneratedCode; var entryPoint = compilationData.Function.Definition.EntryPoint.ToInt64(); foreach (var nativeLabel in compilationData.UnresolvedNativeLabels) { var source = nativeLabel.Key; var target = nativeLabel.Value.ToInt64(); //Calculate the native jump location var nativeTarget = (int)(target - (entryPoint + source) - 6); //Update the source with the native target var sourceOffset = source + 6 - sizeof(int); NativeHelpers.SetInt(generatedCode, sourceOffset, nativeTarget); } compilationData.UnresolvedNativeLabels.Clear(); }
/// <summary> /// Moves the argument to the stack /// </summary> /// <param name="compilationData">The compilation data</param> public void MoveArgumentsToStack(CompilationData compilationData) { var function = compilationData.Function; var parameterTypes = function.Definition.Parameters; for (int argumentIndex = parameterTypes.Count - 1; argumentIndex >= 0; argumentIndex--) { if (parameterTypes[argumentIndex].IsPrimitiveType(PrimitiveTypes.Float)) { this.MoveFloatArgumentToStack( compilationData, argumentIndex); } else { this.MoveNoneFloatArgumentToStack( compilationData, argumentIndex); } } }
/// <summary> /// Handles the given function call argument /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="argumentIndex">The index of the argument</param> /// <param name="argumentType">The type of the argument</param> /// <param name="toCall">The function to call</param> public void CallFunctionArgument(CompilationData compilationData, int argumentIndex, BaseType argumentType, FunctionDefinition toCall) { var operandStack = compilationData.OperandStack; //Check if to pass argument by via stack if (argumentIndex >= numRegisterArguments) { //Move from the operand stack to the normal stack operandStack.PopRegister(Register.AX); compilationData.Assembler.Push(Register.AX); } else { if (argumentType.IsPrimitiveType(PrimitiveTypes.Float)) { operandStack.PopRegister(this.floatArgumentRegisters[argumentIndex]); } else { operandStack.PopRegister(this.intArgumentRegisters[argumentIndex]); } } }
/// <summary> /// Resolves the call target for the given function /// </summary> /// <param name="compilationData">The compilation data</param> private void ResolveCallTargets(CompilationData compilationData) { var generatedCode = compilationData.Function.GeneratedCode; var entryPoint = compilationData.Function.Definition.EntryPoint.ToInt64(); foreach (var unresolvedCall in compilationData.UnresolvedFunctionCalls) { var toCallAddress = unresolvedCall.Function.EntryPoint.ToInt64(); //Update the call target if (unresolvedCall.AddressMode == FunctionCallAddressMode.Absolute) { NativeHelpers.SetLong(generatedCode, unresolvedCall.CallSiteOffset + 2, toCallAddress); } else { int target = (int)(toCallAddress - (entryPoint + unresolvedCall.CallSiteOffset + 5)); NativeHelpers.SetInt(generatedCode, unresolvedCall.CallSiteOffset + 1, target); } } compilationData.UnresolvedFunctionCalls.Clear(); }
/// <summary> /// Handles the return value from a function /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="toCall">The function to call</param> public void HandleReturnValue(CompilationData compilationData, FunctionDefinition toCall) { //If we have passed arguments via the stack, adjust the stack pointer. int numStackArgs = this.CalculateStackArguments(toCall.Parameters); if (numStackArgs > 0) { compilationData.Assembler.Add( Register.SP, numStackArgs * Assembler.RegisterSize); } if (!toCall.ReturnType.IsPrimitiveType(PrimitiveTypes.Void)) { if (toCall.ReturnType.IsPrimitiveType(PrimitiveTypes.Float)) { compilationData.OperandStack.PushRegister(FloatCallingConventions.ReturnValue); } else { compilationData.OperandStack.PushRegister(IntCallingConventions.ReturnValue); } } }
/// <summary> /// Returns the stack argument index for the argument /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="argumentIndex">The argument index</param> private int GetStackArgumentIndex(CompilationData compilationData, int argumentIndex) { int stackArgIndex = 0; var parameterTypes = compilationData.Function.Definition.Parameters; int index = 0; foreach (var parameterType in parameterTypes) { if (index == argumentIndex) { break; } if (index >= numRegisterArguments) { stackArgIndex++; } index++; } return(stackArgIndex); }
/// <summary> /// Calculates the stack alignment /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="parameters">The parameters of the function to call</param> public int CalculateStackAlignment(CompilationData compilationData, IReadOnlyList <BaseType> parameterTypes) { int numStackArgs = this.CalculateStackArguments(parameterTypes); return((numStackArgs % 2) * Assembler.RegisterSize); }