예제 #1
0
 /// <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));
            }
        }
예제 #4
0
        /// <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;
            }
        }
예제 #5
0
 /// <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);
     }
 }
예제 #6
0
 /// <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;
 }