/// <summary> /// Creates a new verification exception /// </summary> /// <param name="message">The message</param> /// <param name="function">The function being verified</param> /// <param name="instruction">The instruction being verified</param> /// <param name="index">The index of the instruction</param> public VerificationException(string message, Function function, Instruction instruction, int index) : base($"{index}: {message}") { this.Function = function; this.Instruction = instruction; this.InstructionIndex = index; }
/// <summary> /// Generates native code for the given instruction /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="instruction">The current instruction</param> /// <param name="index">The index of the instruction</param> private void GenerateInstruction(CompilationData compilationData, Instruction instruction, int index) { var generatedCode = compilationData.Function.GeneratedCode; var operandStack = compilationData.OperandStack; var funcDef = compilationData.Function.Definition; int stackOffset = 1; compilationData.InstructionMapping.Add(generatedCode.Count); switch (instruction.OpCode) { case OpCodes.Pop: operandStack.PopRegister(Register.AX); break; case OpCodes.LoadInt: operandStack.PushInt(instruction.IntValue); break; case OpCodes.LoadFloat: int floatPattern = BitConverter.ToInt32(BitConverter.GetBytes(instruction.FloatValue), 0); operandStack.PushInt(floatPattern); break; case OpCodes.AddInt: case OpCodes.SubInt: case OpCodes.MulInt: case OpCodes.DivInt: operandStack.PopRegister(Register.CX); operandStack.PopRegister(Register.AX); switch (instruction.OpCode) { case OpCodes.AddInt: Assembler.Add(generatedCode, Register.AX, Register.CX); break; case OpCodes.SubInt: Assembler.Sub(generatedCode, Register.AX, Register.CX); break; case OpCodes.MulInt: Assembler.Mult(generatedCode, Register.AX, Register.CX); break; case OpCodes.DivInt: //This sign extends the eax register generatedCode.Add(0x99); //cdq Assembler.Div(generatedCode, Register.CX); break; } operandStack.PushRegister(Register.AX); break; case OpCodes.AddFloat: case OpCodes.SubFloat: case OpCodes.MulFloat: case OpCodes.DivFloat: operandStack.PopRegister(FloatRegister.XMM1); operandStack.PopRegister(FloatRegister.XMM0); switch (instruction.OpCode) { case OpCodes.AddFloat: Assembler.Add(generatedCode, FloatRegister.XMM0, FloatRegister.XMM1); break; case OpCodes.SubFloat: Assembler.Sub(generatedCode, FloatRegister.XMM0, FloatRegister.XMM1); break; case OpCodes.MulFloat: Assembler.Mult(generatedCode, FloatRegister.XMM0, FloatRegister.XMM1); break; case OpCodes.DivFloat: Assembler.Div(generatedCode, FloatRegister.XMM0, FloatRegister.XMM1); break; } operandStack.PushRegister(FloatRegister.XMM0); break; case OpCodes.Call: { var signature = this.virtualMachine.Binder.FunctionSignature( instruction.StringValue, instruction.Parameters); var funcToCall = this.virtualMachine.Binder.GetFunction(signature); //Align the stack int stackAlignment = this.callingConvetions.CalculateStackAlignment( compilationData, funcToCall.Parameters); if (stackAlignment > 0) { Assembler.Sub(generatedCode, Register.SP, stackAlignment); } //Set the function arguments this.callingConvetions.CallFunctionArguments(compilationData, funcToCall); //Reserve 32 bytes for called function to spill registers Assembler.Sub(generatedCode, Register.SP, 32); //Generate the call if (funcToCall.IsManaged) { //Mark that the function call needs to be patched with the entry point later compilationData.UnresolvedFunctionCalls.Add(new UnresolvedFunctionCall( FunctionCallAddressModes.Relative, funcToCall, generatedCode.Count)); Assembler.Call(generatedCode, 0); } else { this.GenerateCall(generatedCode, funcToCall.EntryPoint); } //Unalign the stack Assembler.Add(generatedCode, Register.SP, stackAlignment + 32); //Hande the return value this.callingConvetions.HandleReturnValue(compilationData, funcToCall); } break; case OpCodes.Ret: //Handle the return value this.callingConvetions.MakeReturnValue(compilationData); //Restore the base pointer this.CreateEpilog(compilationData); //Make the return Assembler.Return(generatedCode); break; case OpCodes.LoadArgument: { //Load rax with the argument int argOffset = (instruction.IntValue + stackOffset) * -Assembler.RegisterSize; Assembler.Move( generatedCode, Register.AX, new MemoryOperand(Register.BP, argOffset)); //mov rax, [rbp+<arg offset>] //Push the loaded value operandStack.PushRegister(Register.AX); } break; case OpCodes.LoadLocal: case OpCodes.StoreLocal: { //Load rax with the locals offset int localOffset = (stackOffset + instruction.IntValue + funcDef.Parameters.Count) * -Assembler.RegisterSize; if (instruction.OpCode == OpCodes.LoadLocal) { //Load rax with the local Assembler.Move( generatedCode, Register.AX, new MemoryOperand(Register.BP, localOffset)); //mov rax, [rbp+<offset>] //Push the loaded value operandStack.PushRegister(Register.AX); } else { //Pop the top operand operandStack.PopRegister(Register.AX); //Store the operand at the given local Assembler.Move( generatedCode, new MemoryOperand(Register.BP, localOffset), Register.AX); //mov [rbp+<local offset>], rax } } break; case OpCodes.Branch: Assembler.Jump(generatedCode, JumpCondition.Always, 0); compilationData.UnresolvedBranches.Add( generatedCode.Count - 5, new UnresolvedBranchTarget(instruction.IntValue, 5)); break; case OpCodes.BranchEqual: case OpCodes.BranchNotEqual: case OpCodes.BranchGreaterThan: case OpCodes.BranchGreaterOrEqual: case OpCodes.BranchLessThan: case OpCodes.BranchLessOrEqual: { var opType = compilationData.Function.OperandTypes[index].Last(); bool unsignedComparison = false; if (opType.IsPrimitiveType(PrimitiveTypes.Int)) { //Pop 2 operands operandStack.PopRegister(Register.CX); operandStack.PopRegister(Register.AX); //Compare Assembler.Compare(generatedCode, Register.AX, Register.CX); //cmp rax, rcx } else if (opType.IsPrimitiveType(PrimitiveTypes.Float)) { //Pop 2 operands operandStack.PopRegister(FloatRegister.XMM1); operandStack.PopRegister(FloatRegister.XMM0); //Compare generatedCode.AddRange(new byte[] { 0x0f, 0x2e, 0xc1 }); //ucomiss xmm0, xmm1 unsignedComparison = true; } JumpCondition condition = JumpCondition.Always; switch (instruction.OpCode) { case OpCodes.BranchEqual: condition = JumpCondition.Equal; break; case OpCodes.BranchNotEqual: condition = JumpCondition.NotEqual; break; case OpCodes.BranchGreaterThan: condition = JumpCondition.GreaterThan; break; case OpCodes.BranchGreaterOrEqual: condition = JumpCondition.GreaterThanOrEqual; break; case OpCodes.BranchLessThan: condition = JumpCondition.LessThan; break; case OpCodes.BranchLessOrEqual: condition = JumpCondition.LessThanOrEqual; break; } Assembler.Jump(generatedCode, condition, 0, unsignedComparison); compilationData.UnresolvedBranches.Add( generatedCode.Count - 6, new UnresolvedBranchTarget(instruction.IntValue, 6)); } break; } }
public void TestMixed3() { using (var container = new Win64Container()) { var intType = container.VirtualMachine.TypeProvider.GetPrimitiveType(PrimitiveTypes.Int); var floatType = container.VirtualMachine.TypeProvider.GetPrimitiveType(PrimitiveTypes.Float); container.VirtualMachine.Binder.Define(FunctionDefinition.NewExternal<FuncFloatArgInt>( "intToFloat", new List<VMType>() { intType }, floatType, FloatToInt)); var convIntToFloat = new Instruction(OpCodes.Call, "intToFloat", new List<VMType>() { intType }); var paramTypes = new List<VMType>() { intType, floatType, intType, floatType, intType, floatType }; var addFunc = new Function( new FunctionDefinition("add", paramTypes, floatType), new List<Instruction>() { new Instruction(OpCodes.LoadArgument, 0), convIntToFloat, new Instruction(OpCodes.LoadArgument, 1), new Instruction(OpCodes.LoadArgument, 2), convIntToFloat, new Instruction(OpCodes.LoadArgument, 3), new Instruction(OpCodes.LoadArgument, 4), convIntToFloat, new Instruction(OpCodes.LoadArgument, 5), new Instruction(OpCodes.AddFloat), new Instruction(OpCodes.AddFloat), new Instruction(OpCodes.AddFloat), new Instruction(OpCodes.AddFloat), new Instruction(OpCodes.AddFloat), new Instruction(OpCodes.Ret) }, new List<VMType>()); addFunc.Optimize = true; var mainFunc = new Function( new FunctionDefinition("floatMain", new List<VMType>(), floatType), new List<Instruction>() { new Instruction(OpCodes.LoadInt, 1), new Instruction(OpCodes.LoadFloat, 2.0f), new Instruction(OpCodes.LoadInt, 3), new Instruction(OpCodes.LoadFloat, 4.0f), new Instruction(OpCodes.LoadInt, 5), new Instruction(OpCodes.LoadFloat, 6.0f), new Instruction(OpCodes.Call, "add", paramTypes), new Instruction(OpCodes.Ret) }, new List<VMType>()); mainFunc.Optimize = true; var assembly = new Assembly(mainFunc, addFunc); container.VirtualMachine.LoadAssembly(assembly); Assert.AreEqual(1 + 2 + 3 + 4 + 5 + 6, TestProgramGenerator.ExecuteFloatProgram(container)); } }
/// <summary> /// Verifies the given instruction /// </summary> /// <param name="function">The function being verified</param> /// <param name="instruction">The instruction</param> /// <param name="index">The index of the instruction</param> /// <param name="operandStack">The operand stack</param> /// <param name="branches">The branches</param> private void VerifiyInstruction(Function function, Instruction instruction, int index, Stack<VMType> operandStack, IList<BranchCheck> branches) { function.OperandTypes[index].AddRange(operandStack.ToList()); switch (instruction.OpCode) { case OpCodes.Pop: this.AssertOperandCount(function, instruction, index, operandStack, 1); operandStack.Pop(); break; case OpCodes.LoadInt: operandStack.Push(this.intType); break; case OpCodes.LoadFloat: operandStack.Push(this.floatType); break; case OpCodes.AddInt: case OpCodes.SubInt: case OpCodes.MulInt: case OpCodes.DivInt: { this.AssertOperandCount(function, instruction, index, operandStack, 2); var op1 = operandStack.Pop(); var op2 = operandStack.Pop(); this.AssertSameType(function, instruction, index, this.intType, op1); this.AssertSameType(function, instruction, index, this.intType, op2); operandStack.Push(this.intType); } break; case OpCodes.AddFloat: case OpCodes.SubFloat: case OpCodes.MulFloat: case OpCodes.DivFloat: { this.AssertOperandCount(function, instruction, index, operandStack, 2); var op1 = operandStack.Pop(); var op2 = operandStack.Pop(); this.AssertSameType(function, instruction, index, this.floatType, op1); this.AssertSameType(function, instruction, index, this.floatType, op2); operandStack.Push(this.floatType); } break; case OpCodes.Call: { var signature = this.virtualMachine.Binder.FunctionSignature( instruction.StringValue, instruction.Parameters); var funcToCall = this.virtualMachine.Binder.GetFunction(signature); //Check that the function exists if (funcToCall == null) { throw new VerificationException( $"There exists no function with the signature '{signature}'.", function, instruction, index); } //Check argument types int numParams = funcToCall.Parameters.Count; this.AssertOperandCount(function, instruction, index, operandStack, numParams); for (int argIndex = numParams - 1; argIndex >= 0; argIndex--) { var op = operandStack.Pop(); var arg = funcToCall.Parameters[argIndex]; this.AssertSameType(function, instruction, index, arg, op); } if (funcToCall.ReturnType != this.voidType) { operandStack.Push(funcToCall.ReturnType); } } break; case OpCodes.Ret: int returnCount = 0; if (function.Definition.ReturnType != this.voidType) { returnCount = 1; } if (operandStack.Count == returnCount) { if (returnCount > 0) { var returnType = operandStack.Pop(); if (returnType != function.Definition.ReturnType) { throw new VerificationException( $"Expected return type of '{function.Definition.ReturnType}' but got type '{returnType}'.", function, instruction, index); } } } else { throw new VerificationException( $"Expected {returnCount} operand(s) on the stack when returning but got {operandStack.Count} operands.", function, instruction, index); } break; case OpCodes.LoadArgument: if (instruction.IntValue >= 0 && instruction.IntValue < function.Definition.Parameters.Count) { operandStack.Push(function.Definition.Parameters[instruction.IntValue]); } else { throw new VerificationException( $"Argument index {instruction.IntValue} is not valid.", function, instruction, index); } break; case OpCodes.LoadLocal: if (instruction.IntValue >= 0 && instruction.IntValue < function.Locals.Count) { operandStack.Push(function.Locals[instruction.IntValue]); } else { throw new VerificationException( $"Local index {instruction.IntValue} is not valid.", function, instruction, index); } break; case OpCodes.StoreLocal: this.AssertOperandCount(function, instruction, index, operandStack, 1); if (instruction.IntValue >= 0 && instruction.IntValue < function.Locals.Count) { var op = operandStack.Pop(); var local = function.Locals[instruction.IntValue]; this.AssertSameType(function, instruction, index, local, op); } else { throw new VerificationException( $"Local index {instruction.IntValue} is not valid.", function, instruction, index); } break; case OpCodes.Branch: //Check if valid target if (!(instruction.IntValue >= 0 && instruction.IntValue <= function.Instructions.Count)) { throw new VerificationException( $"Invalid jump target ({instruction.IntValue}).", function, instruction, index); } branches.Add(new BranchCheck(index, instruction.IntValue, operandStack.ToList())); break; case OpCodes.BranchEqual: case OpCodes.BranchNotEqual: case OpCodes.BranchGreaterThan: case OpCodes.BranchGreaterOrEqual: case OpCodes.BranchLessThan: case OpCodes.BranchLessOrEqual: { this.AssertOperandCount(function, instruction, index, operandStack, 2); //Check if valid target if (!(instruction.IntValue >= 0 && instruction.IntValue <= function.Instructions.Count)) { throw new VerificationException( $"Invalid jump target ({instruction.IntValue}).", function, instruction, index); } var op1 = operandStack.Pop(); var op2 = operandStack.Pop(); if (op1 == this.intType) { if (op2.IsPrimitiveType(PrimitiveTypes.Int)) { branches.Add(new BranchCheck(index, instruction.IntValue, operandStack.ToList())); } else { throw new VerificationException( "Expected two operands of type 'Int' on the stack.", function, instruction, index); } } else if (op2 == this.floatType) { if (op2.IsPrimitiveType(PrimitiveTypes.Float)) { branches.Add(new BranchCheck(index, instruction.IntValue, operandStack.ToList())); } else { throw new VerificationException( "Expected two operands of type 'Float' on the stack.", function, instruction, index); } } else { throw new VerificationException( "Expected two operands of comparable type on the stack.", function, instruction, index); } } break; } }
/// <summary> /// Asserts that the given types are equal /// </summary> private void AssertSameType(Function function, Instruction instruction, int index, VMType expectedType, VMType actualType) { if (expectedType != actualType) { throw new VerificationException( $"Expected type '{expectedType}' but got type '{actualType}'.", function, instruction, index); } }
/// <summary> /// Asserts that the given amount of operand exists on the stack. /// </summary> private void AssertOperandCount(Function function, Instruction instruction, int index, Stack<VMType> operandStack, int count) { if (operandStack.Count < count) { throw new VerificationException( $"Expected {count} operands on the stack, but got: {operandStack.Count}.", function, instruction, index); } }
/// <summary> /// Indicates if the given instruction is a conditional branch /// </summary> /// <param name="instruction">The instruction</param> public static bool IsConditionalBranch(Instruction instruction) { return instruction.OpCode == OpCodes.BranchEqual || instruction.OpCode == OpCodes.BranchNotEqual || instruction.OpCode == OpCodes.BranchGreaterThan || instruction.OpCode == OpCodes.BranchGreaterOrEqual || instruction.OpCode == OpCodes.BranchLessThan || instruction.OpCode == OpCodes.BranchLessOrEqual; }