コード例 #1
0
ファイル: Compiler.Emit.cs プロジェクト: frje/SharpLang
        private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor)
        {
            var stack = functionContext.Stack;

            // Make sure .cctor has been called
            EnsureClassInitialized(functionContext, GetClass(type));

            var allocatedObject = AllocateObject(type);

            // Add it to stack, right before arguments
            var ctorNumParams = ctor.ParameterTypes.Length;

            stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject));

            // Invoke ctor
            EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue);

            if (type.StackType != StackValueType.Object)
            {
                allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty);
            }

            // Add created object on the stack
            stack.Add(new StackValue(type.StackType, type, allocatedObject));
        }
コード例 #2
0
        private ValueRef CreateDebugType(FunctionCompilerContext functionContext, Type type)
        {
            var size  = LLVM.ABISizeOfType(targetData, type.DefaultType) * 8;
            var align = LLVM.ABIAlignmentOfType(targetData, type.DefaultType) * 8;

            switch (type.TypeReference.MetadataType)
            {
            case MetadataType.Boolean:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "bool", size, align, (uint)DW_ATE.Boolean));

            case MetadataType.SByte:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "sbyte", size, align, (uint)DW_ATE.Signed));

            case MetadataType.Byte:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "byte", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.Int16:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "short", size, align, (uint)DW_ATE.Signed));

            case MetadataType.UInt16:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "ushort", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.Int32:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "int", size, align, (uint)DW_ATE.Signed));

            case MetadataType.UInt32:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "uint", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.Int64:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "long", size, align, (uint)DW_ATE.Signed));

            case MetadataType.UInt64:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "ulong", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.Single:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "float", size, align, (uint)DW_ATE.Float));

            case MetadataType.Double:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "double", size, align, (uint)DW_ATE.Float));

            case MetadataType.Char:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "char", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.IntPtr:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "IntPtr", size, align, (uint)DW_ATE.Signed));

            case MetadataType.UIntPtr:
                return(LLVM.DIBuilderCreateBasicType(debugBuilder, "UIntPtr", size, align, (uint)DW_ATE.Unsigned));

            case MetadataType.Pointer:
                var elementType = GetType(((PointerType)type.TypeReference).ElementType);
                return(LLVM.DIBuilderCreatePointerType(debugBuilder, CreateDebugType(functionContext, elementType), size, align, type.TypeReference.Name));

            default:
                // For now, let's have a fallback since lot of types are not supported yet.
                return(CreateDebugType(functionContext, intPtr));
            }
        }
コード例 #3
0
        private void PrepareScopes(FunctionCompilerContext functionContext)
        {
            var function        = functionContext.Function;
            var methodReference = function.MethodReference;
            var body            = functionContext.Body;

            // Add root scope variables
            // Note: could be null
            var newScope = new Scope(body.Scope);

            functionContext.Scopes.Add(newScope);

            // Update debug information
            var startSequencePoint = body.Instructions[0].SequencePoint;

            if (startSequencePoint != null)
            {
                var url  = startSequencePoint.Document.Url;
                var line = startSequencePoint.StartLine;
                functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url));
                var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]);
                var functionType           = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes);
                newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, functionContext.DebugFile, methodReference.FullName, LLVM.GetValueName(function.GeneratedValue),
                                                                       functionContext.DebugFile, (uint)line, functionType,
                                                                       false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty);
            }

            SetupDebugLocation(body.Instructions[0], newScope);
            if (body.Scope != null)
            {
                EnterScope(functionContext, newScope);
            }
            else
            {
                // Emit locals (if no scopes)
                for (int index = 0; index < body.Variables.Count; index++)
                {
                    var variable     = functionContext.Locals[index];
                    var variableName = body.Variables[index].Name;

                    EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                }
            }

            // Emit args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var arg = functionContext.Arguments[index];

                var argName = LLVM.GetValueName(arg.Value);

                EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1);
            }
        }
コード例 #4
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private void PrepareScopes(FunctionCompilerContext functionContext)
        {
            var function = functionContext.Function;
            var methodReference = function.MethodReference;
            var body = functionContext.Body;

            // Add root scope variables
            // Note: could be null
            var newScope = new Scope(body.Scope);
            functionContext.Scopes.Add(newScope);
            
            // Update debug information
            var startSequencePoint = body.Instructions[0].SequencePoint;
            if (startSequencePoint != null)
            {
                var url = startSequencePoint.Document.Url;
                var line = startSequencePoint.StartLine;
                functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url));
                var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]);
                var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes);
                newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, functionContext.DebugFile, methodReference.FullName, LLVM.GetValueName(function.GeneratedValue),
                    functionContext.DebugFile, (uint)line, functionType,
                    false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty);
            }

            SetupDebugLocation(body.Instructions[0], newScope);
            if (body.Scope != null)
            {
                EnterScope(functionContext, newScope);
            }
            else
            {
                // Emit locals (if no scopes)
                for (int index = 0; index < body.Variables.Count; index++)
                {
                    var variable = functionContext.Locals[index];
                    var variableName = body.Variables[index].Name;

                    EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                }
            }

            // Emit args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var arg = functionContext.Arguments[index];

                var argName = LLVM.GetValueName(arg.Value);

                EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1);
            }
        }
コード例 #5
0
        private Scope CreateScope(FunctionCompilerContext functionContext, Scope parentScope, Mono.Cecil.Cil.Scope cecilScope)
        {
            var newScope      = new Scope(cecilScope);
            var sequencePoint = newScope.Source.Start.SequencePoint;

            if (sequencePoint != null)
            {
                newScope.GeneratedScope = LLVM.DIBuilderCreateLexicalBlock(debugBuilder, parentScope.GeneratedScope,
                                                                           functionContext.DebugFile,
                                                                           (uint)sequencePoint.StartLine, (uint)sequencePoint.StartColumn, 0);
            }

            return(newScope);
        }
コード例 #6
0
        private void EmitDebugVariable(FunctionCompilerContext functionContext, Instruction start, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0)
        {
            var sequencePoint = start.SequencePoint;
            var debugType     = CreateDebugType(functionContext, variable.Type);

            // TODO: Detect where variable is actually declared (first use of local?)
            var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder,
                                                                       (uint)dwarfType,
                                                                       generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType,
                                                                       true, 0, (uint)argIndex);

            var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable,
                                                                        LLVM.GetInsertBlock(builder));

            LLVM.SetInstDebugLocation(builder, debugVariableDeclare);
        }
コード例 #7
0
ファイル: Compiler.Scope.cs プロジェクト: bpietroiu/SharpLang
        private void ProcessScopes(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var scopes = functionContext.Scopes;

            // Exit finished scopes
            for (int index = scopes.Count - 1; index >= 0; index--)
            {
                var scope = scopes[index];
                if (scope.Source != null && instruction.Offset > scope.Source.End.Offset)
                {
                    scopes.RemoveAt(index);
                }
                else
                {
                    break;
                }
            }

            var  lastScope     = scopes[scopes.Count - 1];
            bool foundNewScope = true;

            while (foundNewScope)
            {
                foundNewScope = false;
                if (lastScope.Source != null && lastScope.Source.HasScopes)
                {
                    foreach (var childScope in lastScope.Source.Scopes)
                    {
                        if (instruction == childScope.Start)
                        {
                            lastScope = CreateScope(functionContext, lastScope, childScope);
                            scopes.Add(lastScope);

                            EnterScope(functionContext, lastScope);

                            foundNewScope = true;
                            break;
                        }
                    }
                }
            }

            if (instruction.SequencePoint != null)
            {
                SetupDebugLocation(instruction.SequencePoint, lastScope);
            }
        }
コード例 #8
0
        private void EnterScope(FunctionCompilerContext functionContext, Scope newScope)
        {
            if (newScope.Source != null)
            {
                SetupDebugLocation(newScope.Source.Start, newScope);
                if (newScope.Source.HasVariables)
                {
                    foreach (var local in newScope.Source.Variables)
                    {
                        var variable     = functionContext.Locals[local.Index];
                        var variableName = local.Name;

                        EmitDebugVariable(functionContext, newScope.Source.Start, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                    }
                }
            }
        }
コード例 #9
0
ファイル: Compiler.Emit.cs プロジェクト: frje/SharpLang
        /// <summary>
        /// Generates invoke if inside a try block, otherwise a call.
        /// </summary>
        /// <param name="functionContext">The function context.</param>
        /// <param name="function">The function.</param>
        /// <param name="args">The arguments.</param>
        /// <returns></returns>
        private ValueRef GenerateInvoke(FunctionCompilerContext functionContext, ValueRef function, ValueRef[] args)
        {
            ValueRef callResult;

            if (functionContext.LandingPadBlock.Value != IntPtr.Zero)
            {
                var nextBlock = LLVM.AppendBasicBlockInContext(context, functionContext.Function.GeneratedValue, string.Empty);
                callResult = LLVM.BuildInvoke(builder, function, args, nextBlock, functionContext.LandingPadBlock, string.Empty);
                LLVM.PositionBuilderAtEnd(builder, nextBlock);
                functionContext.BasicBlock = nextBlock;
            }
            else
            {
                callResult = LLVM.BuildCall(builder, function, args, string.Empty);
            }

            return(callResult);
        }
コード例 #10
0
ファイル: Compiler.Scope.cs プロジェクト: bpietroiu/SharpLang
        private void EmitDebugVariable(FunctionCompilerContext functionContext, SequencePoint sequencePoint, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0)
        {
            var debugType = CreateDebugType(variable.Type);

            // Process fields and other dependent debug types
            ProcessMissingDebugTypes();

            // Read it again in case it was mutated
            debugType = CreateDebugType(variable.Type);

            // TODO: Detect where variable is actually declared (first use of local?)
            var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder,
                                                                       (uint)dwarfType,
                                                                       generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType,
                                                                       true, 0, (uint)argIndex);

            var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable,
                                                                        LLVM.GetInsertBlock(builder));

            LLVM.SetInstDebugLocation(builder, debugVariableDeclare);
        }
コード例 #11
0
ファイル: Compiler.Emit.cs プロジェクト: frje/SharpLang
        private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod)
        {
            var stack = functionContext.Stack;

            // Build argument list
            var targetNumParams = targetMethod.ParameterTypes.Length;
            var args            = new ValueRef[targetNumParams];

            for (int index = 0; index < targetNumParams; index++)
            {
                // TODO: Casting/implicit conversion?
                var stackItem = stack[stack.Count - targetNumParams + index];
                args[index] = ConvertFromStackToLocal(targetMethod.ParameterTypes[index], stackItem);
            }

            // Remove arguments from stack
            stack.RemoveRange(stack.Count - targetNumParams, targetNumParams);

            // Invoke method
            ValueRef callResult;
            var      actualMethod = overrideMethod;

            callResult = GenerateInvoke(functionContext, actualMethod, args);

            // Mark method as needed (if non-virtual call)
            if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero)
            {
                LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage);
            }

            // Push return result on stack
            if (targetMethod.ReturnType.TypeReference.MetadataType != MetadataType.Void)
            {
                // Convert return value from local to stack value
                var returnValue = ConvertFromLocalToStack(targetMethod.ReturnType, callResult);

                // Add value to stack
                stack.Add(new StackValue(targetMethod.ReturnType.StackType, targetMethod.ReturnType, returnValue));
            }
        }
コード例 #12
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private void ProcessScopes(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var scopes = functionContext.Scopes;

            // Exit finished scopes
            for (int index = scopes.Count - 1; index >= 0; index--)
            {
                var scope = scopes[index];
                if (scope.Source != null && instruction.Offset > scope.Source.End.Offset)
                    scopes.RemoveAt(index);
                else
                    break;
            }

            var lastScope = scopes[scopes.Count - 1];
            bool foundNewScope = true;
            while (foundNewScope)
            {
                foundNewScope = false;
                if (lastScope.Source != null && lastScope.Source.HasScopes)
                {
                    foreach (var childScope in lastScope.Source.Scopes)
                    {
                        if (instruction == childScope.Start)
                        {
                            lastScope = CreateScope(functionContext, lastScope, childScope);
                            scopes.Add(lastScope);

                            EnterScope(functionContext, lastScope);

                            foundNewScope = true;
                            break;
                        }
                    }
                }
            }

            SetupDebugLocation(instruction, lastScope);
        }
コード例 #13
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        /// <summary>
        /// Generates invoke if inside a try block, otherwise a call.
        /// </summary>
        /// <param name="functionContext">The function context.</param>
        /// <param name="function">The function.</param>
        /// <param name="args">The arguments.</param>
        /// <returns></returns>
        private ValueRef GenerateInvoke(FunctionCompilerContext functionContext, ValueRef function, ValueRef[] args)
        {
            ValueRef callResult;

            if (functionContext.LandingPadBlock.Value != IntPtr.Zero)
            {
                var nextBlock = LLVM.AppendBasicBlockInContext(context, functionContext.FunctionGlobal, string.Empty);
                LLVM.MoveBasicBlockAfter(nextBlock, LLVM.GetInsertBlock(builder));
                callResult = LLVM.BuildInvoke(builder, function, args, nextBlock, functionContext.LandingPadBlock, string.Empty);
                LLVM.PositionBuilderAtEnd(builder, nextBlock);
                functionContext.BasicBlock = nextBlock;
            }
            else
            {
                callResult = LLVM.BuildCall(builder, function, args, string.Empty);
            }

            return callResult;
        }
コード例 #14
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod)
        {
            var stack = functionContext.Stack;

            bool hasStructValueReturn = targetMethod.ReturnType.ABIParameterInfo.Kind == ABIParameterInfoKind.Indirect;

            // Build argument list
            var targetNumParams = targetMethod.ParameterTypes.Length;
            var args = new ValueRef[targetNumParams + (hasStructValueReturn ? 1 : 0)];
            for (int index = 0; index < targetNumParams; index++)
            {
                var llvmIndex = index + (hasStructValueReturn ? 1 : 0);
                var parameterType = targetMethod.ParameterTypes[index];

                // TODO: Casting/implicit conversion?
                var stackItem = stack[stack.Count - targetNumParams + index];
                args[llvmIndex] = ConvertFromStackToLocal(parameterType.Type, stackItem);

                if (stackItem.StackType == StackValueType.Value)
                {
                    switch (parameterType.ABIParameterInfo.Kind)
                    {
                        case ABIParameterInfoKind.Direct:
                            args[llvmIndex] = LLVM.BuildLoad(builder, args[llvmIndex], string.Empty);
                            break;
                        case ABIParameterInfoKind.Indirect:
                            // Make a copy of indirect aggregate values
                            args[llvmIndex] = LoadValue(stackItem.Type.StackType, args[llvmIndex], InstructionFlags.None);
                            break;
                        case ABIParameterInfoKind.Coerced:
                            // Coerce to integer type
                            args[llvmIndex] = LLVM.BuildPointerCast(builder, args[llvmIndex], LLVM.PointerType(parameterType.ABIParameterInfo.CoerceType, 0), string.Empty);
                            args[llvmIndex] = LLVM.BuildLoad(builder, args[llvmIndex], string.Empty);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }

            // Remove arguments from stack
            stack.RemoveRange(stack.Count - targetNumParams, targetNumParams);

            // Prepare return value storage (in case of struct)
            if (hasStructValueReturn)
            {
                // Allocate storage
                args[0] = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty);
            }

            // Invoke method
            ValueRef callResult;
            var actualMethod = overrideMethod;

            callResult = GenerateInvoke(functionContext, actualMethod, args);
            switch (targetMethod.CallingConvention)
            {
                case MethodCallingConvention.StdCall:
                    LLVM.SetInstructionCallConv(callResult, (uint)CallConv.X86StdcallCallConv);
                    break;
                case MethodCallingConvention.FastCall:
                    LLVM.SetInstructionCallConv(callResult, (uint)CallConv.X86FastcallCallConv);
                    break;
            }

            // Mark method as needed (if non-virtual call)
            if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero)
            {
                LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage);
            }

            // Push return result on stack
            if (targetMethod.ReturnType.Type.TypeReferenceCecil.MetadataType != MetadataType.Void)
            {
                ValueRef returnValue;
                if (targetMethod.ReturnType.Type.StackType == StackValueType.Value)
                {
                    switch (targetMethod.ReturnType.ABIParameterInfo.Kind)
                    {
                        case ABIParameterInfoKind.Direct:
                            returnValue = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty);
                            LLVM.BuildStore(builder, callResult, returnValue);
                            break;
                        case ABIParameterInfoKind.Indirect:
                            returnValue = args[0];
                            break;
                        case ABIParameterInfoKind.Coerced:
                            returnValue = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty);
                            var returnValueCasted = LLVM.BuildPointerCast(builder, returnValue, LLVM.PointerType(targetMethod.ReturnType.ABIParameterInfo.CoerceType, 0), string.Empty);
                            LLVM.BuildStore(builder, callResult, returnValueCasted);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
                else
                {
                    returnValue = callResult;
                }

                // Convert return value from local to stack value
                returnValue = ConvertFromLocalToStack(targetMethod.ReturnType.Type, returnValue);

                // Add value to stack
                stack.Add(new StackValue(targetMethod.ReturnType.Type.StackType, targetMethod.ReturnType.Type, returnValue));
            }

            // Apply attributes to the call instruction
            ApplyCallAttributes(targetMethod, callResult);
        }
コード例 #15
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitRet(FunctionCompilerContext functionContext)
        {
            var method = functionContext.MethodReference;

            if (method.ReturnType.MetadataType == MetadataType.Void)
            {
                // Emit ret void
                LLVM.BuildRetVoid(builder);
            }
            else
            {
                // Get last item from stack
                var stackItem = functionContext.Stack.Pop();

                // Get return type
                var returnType = GetType(ResolveGenericsVisitor.Process(method, method.ReturnType), TypeState.StackComplete);
                var returnValue = ConvertFromStackToLocal(returnType, stackItem);

                if (returnType.StackType == StackValueType.Value)
                {
                    switch (functionContext.Signature.ReturnType.ABIParameterInfo.Kind)
                    {
                        case ABIParameterInfoKind.Coerced:
                            returnValue = LLVM.BuildPointerCast(builder, returnValue, LLVM.PointerType(functionContext.Signature.ReturnType.ABIParameterInfo.CoerceType, 0), string.Empty);
                            returnValue = LLVM.BuildLoad(builder, returnValue, string.Empty);
                            LLVM.BuildRet(builder, returnValue);
                            break;
                        case ABIParameterInfoKind.Direct:
                            returnValue = LLVM.BuildLoad(builder, returnValue, string.Empty);
                            LLVM.BuildRet(builder, returnValue);
                            break;
                        case ABIParameterInfoKind.Indirect:
                            StoreValue(returnType.StackType, returnValue, LLVM.GetParam(functionContext.FunctionGlobal, 0), InstructionFlags.None);
                            LLVM.BuildRetVoid(builder);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
                else
                {
                    LLVM.BuildRet(builder, returnValue);
                }
            }
        }
コード例 #16
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitEndfinally(FunctionCompilerContext functionContext, ExceptionHandlerInfo currentFinallyClause)
        {
            if (currentFinallyClause.Source.HandlerType == ExceptionHandlerType.Finally)
            {
                // Basic block to continue exception handling (if endfinally.jumptarget is -1, but we simply set it on undefined cases)
                var activeTryHandlers = functionContext.ActiveTryHandlers;
                var nextActiveTryHandler = activeTryHandlers.Count > 0 ? activeTryHandlers[activeTryHandlers.Count - 1].CatchDispatch : functionContext.ResumeExceptionBlock;

                // Generate dispatch code (with a switch/case)
                var @switch = LLVM.BuildSwitch(builder, LLVM.BuildLoad(builder, functionContext.EndfinallyJumpTarget, string.Empty), nextActiveTryHandler, (uint)currentFinallyClause.LeaveTargets.Count);
                for (int index = 0; index < currentFinallyClause.LeaveTargets.Count; index++)
                {
                    var leaveTarget = currentFinallyClause.LeaveTargets[index];

                    LLVM.AddCase(@switch, LLVM.ConstInt(int32LLVM, (ulong)index, false), functionContext.BasicBlocks[leaveTarget.Offset]);
                }
            }
            else if (currentFinallyClause.Source.HandlerType == ExceptionHandlerType.Fault)
            {
                var exceptionObject = LLVM.BuildLoad(builder, functionContext.ExceptionSlot, string.Empty);

                // Rethrow exception
                GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, exceptionObject, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)});
                LLVM.BuildUnreachable(builder);
            }
            else
            {
                throw new InvalidOperationException("Exception clause containing a endfinally/endfault is not a finally or fault clause.");
            }

            // Default is not to flow to next instruction, previous Leave instructions already tagged what was necessary
            functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
        }
コード例 #17
0
ファイル: Compiler.cs プロジェクト: RainsSoft/SharpLang
        public ModuleRef GenerateModule()
        {
            LLVM.DIBuilderCreateCompileUnit(debugBuilder,
                0x4, // DW_LANG_C_plus_plus
                "file", "directory", "SharpLang", false, string.Empty, 1, string.Empty);

            LLVM.AddModuleFlag(module, "Dwarf Version", 4);
            LLVM.AddModuleFlag(module, "Debug Info Version", LLVM.DIGetDebugMetadataVersion());

            // Process methods
            while (classesToGenerate.Count > 0)
            {
                var classToGenerate = classesToGenerate.Dequeue();
                if (classToGenerate.IsLocal)
                {
                    PrepareClassMethods(classToGenerate);
                }
            }

            // Generate code
            while (methodsToCompile.Count > 0)
            {
                var methodToCompile = methodsToCompile.Dequeue();
                //Console.WriteLine("Compiling {0}", methodToCompile.Key.FullName);
                CompileFunction(methodToCompile.Key, methodToCompile.Value);
            }

            // Prepare global module constructor
            var globalCtorSignature = new FunctionSignature(abi, @void, new Type[0], MethodCallingConvention.Default, null);
            var globalCtorFunctionType = CreateFunctionTypeLLVM(globalCtorSignature);
            var globalCtor = LLVM.AddFunction(module, "initializeSharpLangModule", globalCtorFunctionType);
            LLVM.SetLinkage(globalCtor, Linkage.PrivateLinkage);
            LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, globalCtor, string.Empty));

            Function entryPoint = null;
            if (assembly.EntryPoint != null)
                functions.TryGetValue(assembly.EntryPoint, out entryPoint);

            if (!TestMode)
            {
                // Emit metadata
                var metadataBytes = ReadMetadata(assembly.MainModule.FullyQualifiedName);
                var metadataData = CreateDataConstant(metadataBytes);

                var metadataGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(metadataData), "metadata");
                LLVM.SetInitializer(metadataGlobal, metadataData);
                LLVM.SetLinkage(metadataGlobal, Linkage.PrivateLinkage);


                // Use metadata to initialize a SharpLangModule, that will be created at module load time using a global ctor
                sharpLangModuleType = GetType(corlib.MainModule.GetType("System.SharpLangModule"), TypeState.VTableEmitted);
                sharpLangTypeType = GetType(corlib.MainModule.GetType("System.SharpLangTypeDefinition"), TypeState.VTableEmitted); // Was only StackComplete until now

                // Get ctor for SharpLangModule and SharpLangType
                var moduleCtor = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Resolve().IsConstructor);

                var registerTypeMethod = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Name == "RegisterType");
                var sortTypesMethod = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Name == "SortTypes");

                // Initialize SharpLangModule instance:
                //   new SharpLangModule(moduleName, metadataStart, metadataLength)
                var sharpLangModuleGlobal = metadataPerModule[assembly.MainModule];
                var functionContext = new FunctionCompilerContext(globalCtor, globalCtorSignature);
                functionContext.Stack = new FunctionStack();
                functionContext.Stack.Add(new StackValue(StackValueType.Object, sharpLangModuleType, sharpLangModuleGlobal));
                functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, metadataGlobal));
                functionContext.Stack.Add(new StackValue(StackValueType.Int32, int32, LLVM.ConstInt(int32LLVM, (ulong)metadataBytes.Length, false)));

                // Setup initial value (note: VTable should be valid)
                LLVM.SetLinkage(sharpLangModuleGlobal, Linkage.ExternalLinkage);
                LLVM.SetInitializer(sharpLangModuleGlobal, SetupVTableConstant(LLVM.ConstNull(sharpLangModuleType.ObjectTypeLLVM), sharpLangModuleType.Class));
                metadataPerModule[assembly.MainModule] = sharpLangModuleGlobal;

                EmitCall(functionContext, moduleCtor.Signature, moduleCtor.GeneratedValue);

                // Register types
                foreach (var type in types)
                {
                    var @class = type.Value.Class;

                    // Skip incomplete types
                    if (@class == null || [email protected])
                        continue;

                    // Skip if no RTTI initializer
                    if (LLVM.GetInitializer(@class.GeneratedEETypeRuntimeLLVM) == ValueRef.Empty)
                        continue;

                    // Skip if interface (fake RTTI pointer)
                    if (type.Value.TypeDefinitionCecil.IsInterface)
                        continue;

                    functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.ConstPointerCast(@class.GeneratedEETypeRuntimeLLVM, intPtrLLVM)));
                    EmitCall(functionContext, registerTypeMethod.Signature, registerTypeMethod.GeneratedValue);
                }

                // Register unmanaged delegate callbacks
                var delegateWrappers = assembly.MainModule.GetType("DelegateWrappers");
                if (delegateWrappers != null)
                {
                    var marshalHelper = GetType(corlib.MainModule.GetType("SharpLang.Marshalling.MarshalHelper"), TypeState.VTableEmitted);
                    var marshalHelperRegisterDelegateWrapper = marshalHelper.Class.Functions.First(x => x.DeclaringType == marshalHelper && x.MethodReference.Name == "RegisterDelegateWrapper");

                    foreach (var delegateWrapper in delegateWrappers.Methods)
                    {
                        var method = GetFunction(delegateWrapper);

                        // Determine delegate type from MarshalFunctionAttribute
                        var marshalFunctionAttribute = method.MethodDefinition.CustomAttributes.First(x => x.AttributeType.FullName == "SharpLang.Marshalling.MarshalFunctionAttribute");
                        var delegateType = GetType((TypeReference)marshalFunctionAttribute.ConstructorArguments[0].Value, TypeState.VTableEmitted);

                        // TODO: Rename those method, and set their linking to linkonce so that it can be merged?
                        // Of course, we would also need to make sure it works when called from external assemblies

                        functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.ConstPointerCast(delegateType.Class.GeneratedEETypeRuntimeLLVM, intPtrLLVM)));
                        EmitLdftn(functionContext.Stack, method);
                        EmitCall(functionContext, marshalHelperRegisterDelegateWrapper.Signature, marshalHelperRegisterDelegateWrapper.GeneratedValue);
                    }
                }

                // Sort and remove duplicates after adding all our types
                // TODO: Somehow sort everything before insertion at compile time?
                EmitCall(functionContext, sortTypesMethod.Signature, sortTypesMethod.GeneratedValue);

                LLVM.BuildRetVoid(builder);
            }
            else
            {
                LLVM.BuildRetVoid(builder);
            }

            // Prepare global ctors
            {
                var globalCtorType = LLVM.StructTypeInContext(context, new[] { int32LLVM, LLVM.PointerType(globalCtorFunctionType, 0) }, true);
                var globalCtorsGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(globalCtorType, 1), "llvm.global_ctors");
                LLVM.SetLinkage(globalCtorsGlobal, Linkage.AppendingLinkage);
                LLVM.SetInitializer(globalCtorsGlobal,
                    LLVM.ConstArray(globalCtorType, new [] {
                        LLVM.ConstNamedStruct(globalCtorType, new[]
                        {
                            LLVM.ConstInt(int32LLVM, (ulong)65536, false),
                            globalCtor,
                        })}));
            }



            // Emit "main" which will call the assembly entry point (if any)
            if (entryPoint != null)
	        {
                var mainFunctionType = LLVM.FunctionType(int32LLVM, new TypeRef[0], false);
	            var mainFunction = LLVM.AddFunction(module, "main", mainFunctionType);
                LLVM.SetLinkage(mainFunction, Linkage.ExternalLinkage);
                LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, mainFunction, string.Empty));

	            var parameters = (entryPoint.ParameterTypes.Length > 0)
	                ? new[] { LLVM.ConstPointerNull(entryPoint.ParameterTypes[0].DefaultTypeLLVM) }
	                : new ValueRef[0];

                LLVM.BuildCall(builder, entryPoint.GeneratedValue, parameters, string.Empty);
                LLVM.BuildRet(builder, LLVM.ConstInt(int32LLVM, 0, false));

                // Emit PInvoke Thunks and Globals needed in entry point module
                PInvokeEmitGlobals();
	        }

            LLVM.DIBuilderFinalize(debugBuilder);
            LLVM.DIBuilderDispose(debugBuilder);
            LLVM.DisposeBuilder(builder);

            return module;
        }
コード例 #18
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private Scope CreateScope(FunctionCompilerContext functionContext, Scope parentScope, Mono.Cecil.Cil.Scope cecilScope)
        {
            var newScope = new Scope(cecilScope);
            var sequencePoint = newScope.Source.Start.SequencePoint;
            if (sequencePoint != null)
            {
                newScope.GeneratedScope = LLVM.DIBuilderCreateLexicalBlock(debugBuilder, parentScope.GeneratedScope,
                    functionContext.DebugFile,
                    (uint) sequencePoint.StartLine, (uint) sequencePoint.StartColumn, 0);
            }

            return newScope;
        }
コード例 #19
0
ファイル: Compiler.Scope.cs プロジェクト: RainsSoft/SharpLang
        private void EmitDebugVariable(FunctionCompilerContext functionContext, SequencePoint sequencePoint, DIDescriptor generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0)
        {
            var debugType = CreateDebugType(variable.Type);

            // Process fields and other dependent debug types
            ProcessMissingDebugTypes();

            // Read it again in case it was mutated
            debugType = CreateDebugType(variable.Type);

            // TODO: Detect where variable is actually declared (first use of local?)
            var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder,
                (uint)dwarfType,
                generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType,
                true, 0, (uint)argIndex);

            var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable, debugEmptyExpression,
                LLVM.GetInsertBlock(builder));
            LLVM.SetInstDebugLocation(builder, debugVariableDeclare);
        }
コード例 #20
0
ファイル: Compiler.Scope.cs プロジェクト: RainsSoft/SharpLang
        private void PrepareScopes(FunctionCompilerContext functionContext, Function function)
        {
            var methodReference = function.MethodReference;
            var body = functionContext.Body;

            // Add root scope variables
            // Note: could be null
            var newScope = new Scope(body.Scope);
            functionContext.Scopes.Add(newScope);

            // Find class scope
            var debugClass = GetOrCreateDebugClass(GetClass(function.DeclaringType));

            // Update debug information
            int line = 0;

            var startSequencePoint = body.Instructions[0].SequencePoint;
            string url;
            if (startSequencePoint != null)
            {
                url = startSequencePoint.Document.Url;
                line = startSequencePoint.StartLine;
            }
            else
            {
                url = assembly.MainModule.FullyQualifiedName;
            }

            functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url));
            var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new DIDescriptor[0]);
            var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes);

            // Replace . with :: so that gdb properly understands it is namespaces.
            var methodDebugName = string.Format("{0}::{1}", methodReference.DeclaringType.FullName.Replace(".", "::"), methodReference.Name);

            newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, debugClass, methodReference.Name, methodDebugName,
                functionContext.DebugFile, (uint)line, functionType,
                false, true, (uint)line, 0, false, function.GeneratedValue, DIDescriptor.Empty, DIDescriptor.Empty);

            SetupDebugLocation(body.Instructions[0].SequencePoint, newScope);
            if (body.Scope != null)
            {
                EnterScope(functionContext, newScope);
            }
            else
            {
                // Emit locals (if no scopes)
                for (int index = 0; index < body.Variables.Count; index++)
                {
                    var variable = functionContext.Locals[index];
                    var variableName = body.Variables[index].Name;
                    if (string.IsNullOrEmpty(variableName))
                        variableName = "var" + index;

                    EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                }
            }

            // Emit args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var arg = functionContext.Arguments[index];

                var argName = LLVM.GetValueName(arg.Value);

                EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1);
            }
        }
コード例 #21
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private ValueRef CreateDebugType(FunctionCompilerContext functionContext, Type type)
        {
            var size = LLVM.ABISizeOfType(targetData, type.DefaultType) * 8;
            var align = LLVM.ABIAlignmentOfType(targetData, type.DefaultType) * 8;

            switch (type.TypeReference.MetadataType)
            {
                case MetadataType.Boolean:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "bool", size, align, (uint)DW_ATE.Boolean);
                case MetadataType.SByte:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "sbyte", size, align, (uint)DW_ATE.Signed);
                case MetadataType.Byte:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "byte", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.Int16:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "short", size, align, (uint)DW_ATE.Signed);
                case MetadataType.UInt16:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "ushort", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.Int32:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "int", size, align, (uint)DW_ATE.Signed);
                case MetadataType.UInt32:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "uint", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.Int64:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "long", size, align, (uint)DW_ATE.Signed);
                case MetadataType.UInt64:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "ulong", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.Single:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "float", size, align, (uint)DW_ATE.Float);
                case MetadataType.Double:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "double", size, align, (uint)DW_ATE.Float);
                case MetadataType.Char:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "char", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.IntPtr:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "IntPtr", size, align, (uint)DW_ATE.Signed);
                case MetadataType.UIntPtr:
                    return LLVM.DIBuilderCreateBasicType(debugBuilder, "UIntPtr", size, align, (uint)DW_ATE.Unsigned);
                case MetadataType.Pointer:
                    var elementType = GetType(((PointerType)type.TypeReference).ElementType);
                    return LLVM.DIBuilderCreatePointerType(debugBuilder, CreateDebugType(functionContext, elementType), size, align, type.TypeReference.Name);
                default:
                    // For now, let's have a fallback since lot of types are not supported yet.
                    return CreateDebugType(functionContext, intPtr);
            }
        }
コード例 #22
0
ファイル: Compiler.Emit.cs プロジェクト: frje/SharpLang
        private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor)
        {
            var stack = functionContext.Stack;

            // Make sure .cctor has been called
            EnsureClassInitialized(functionContext, GetClass(type));

            var allocatedObject = AllocateObject(type);

            // Add it to stack, right before arguments
            var ctorNumParams = ctor.ParameterTypes.Length;
            stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject));

            // Invoke ctor
            EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue);

            if (type.StackType != StackValueType.Object)
            {
                allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty);
            }

            // Add created object on the stack
            stack.Add(new StackValue(type.StackType, type, allocatedObject));
        }
コード例 #23
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private void EmitDebugVariable(FunctionCompilerContext functionContext, Instruction start, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0)
        {
            var sequencePoint = start.SequencePoint;
            var debugType = CreateDebugType(functionContext, variable.Type);

            // TODO: Detect where variable is actually declared (first use of local?)
            var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder,
                (uint)dwarfType,
                generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType,
                true, 0, (uint)argIndex);

            var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable,
                LLVM.GetInsertBlock(builder));
            LLVM.SetInstDebugLocation(builder, debugVariableDeclare);
        }
コード例 #24
0
ファイル: Compiler.Scope.cs プロジェクト: frje/SharpLang
        private void EnterScope(FunctionCompilerContext functionContext, Scope newScope)
        {
            if (newScope.Source != null)
            {
                SetupDebugLocation(newScope.Source.Start, newScope);
                if (newScope.Source.HasVariables)
                {
                    foreach (var local in newScope.Source.Variables)
                    {
                        var variable = functionContext.Locals[local.Index];
                        var variableName = local.Name;

                        EmitDebugVariable(functionContext, newScope.Source.Start, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                    }
                }
            }
        }
コード例 #25
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitIsOrCastclass(FunctionCompilerContext functionContext, FunctionStack stack, Class @class, Code opcode, int instructionOffset)
        {
            var functionGlobal = functionContext.FunctionGlobal;

            var obj = stack.Pop();

            // Force emission of class to be sure we have RTTI type generated
            // Another option would be to cast everything to object before querying RTTI object
            var objClass = GetClass(obj.Type);

            var currentBlock = LLVM.GetInsertBlock(builder);

            // Prepare basic blocks (for PHI instruction)
            var typeIsNotNullBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_not_null", instructionOffset));
            var typeNotMatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_not_match", instructionOffset));
            var typeCheckDoneBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_check_done", instructionOffset));

            // Properly order block for easy LLVM bitcode reading
            LLVM.MoveBasicBlockAfter(typeIsNotNullBlock, currentBlock);
            LLVM.MoveBasicBlockAfter(typeNotMatchBlock, typeIsNotNullBlock);
            LLVM.MoveBasicBlockAfter(typeCheckDoneBlock, typeNotMatchBlock);

            var isObjNonNull = LLVM.BuildICmp(builder, IntPredicate.IntNE, obj.Value, LLVM.ConstPointerNull(LLVM.TypeOf(obj.Value)), string.Empty);
            LLVM.BuildCondBr(builder, isObjNonNull, typeIsNotNullBlock, opcode == Code.Castclass ? typeCheckDoneBlock : typeNotMatchBlock);

            LLVM.PositionBuilderAtEnd(builder, typeIsNotNullBlock);

            // Get RTTI pointer
            var indices = new[]
            {
                LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                LLVM.ConstInt(int32LLVM, (int)ObjectFields.RuntimeTypeInfo, false), // Access RTTI
            };

            var rttiPointer = LLVM.BuildInBoundsGEP(builder, obj.Value, indices, string.Empty);
            rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty);

            // castedPointerObject is valid only from typeCheckBlock
            var castedPointerType = LLVM.PointerType(@class.Type.ObjectTypeLLVM, 0);
            ValueRef castedPointerObject;

            BasicBlockRef typeCheckBlock;

            if (@class.Type.TypeReferenceCecil.Resolve().IsInterface)
            {
                // Cast as appropriate pointer type (for next PHI incoming if success)
                castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty);

                var inlineRuntimeTypeInfoType = LLVM.TypeOf(LLVM.GetParam(isInstInterfaceFunctionLLVM, 0));
                var isInstInterfaceResult = LLVM.BuildCall(builder, isInstInterfaceFunctionLLVM, new[]
                {
                    LLVM.BuildPointerCast(builder, rttiPointer, inlineRuntimeTypeInfoType, string.Empty),
                    LLVM.BuildPointerCast(builder, @class.GeneratedEETypeTokenLLVM, inlineRuntimeTypeInfoType, string.Empty),
                }, string.Empty);

                LLVM.BuildCondBr(builder, isInstInterfaceResult, typeCheckDoneBlock, typeNotMatchBlock);

                typeCheckBlock = LLVM.GetInsertBlock(builder);
            }
            else
            {
                // TODO: Probably better to rewrite this in C, but need to make sure depth will be inlined as constant
                // Get super type count
                // Get method stored in IMT slot
                indices = new[]
                {
                    LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                    LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.SuperTypeCount, false), // Super type count
                };

                typeCheckBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_check", instructionOffset));
                LLVM.MoveBasicBlockBefore(typeCheckBlock, typeNotMatchBlock);

                var superTypeCount = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                superTypeCount = LLVM.BuildLoad(builder, superTypeCount, string.Empty);

                var depthCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntSGE, superTypeCount, LLVM.ConstInt(int32LLVM, (ulong)@class.Depth, false), string.Empty);
                LLVM.BuildCondBr(builder, depthCompareResult, typeCheckBlock, typeNotMatchBlock);

                // Start new typeCheckBlock
                LLVM.PositionBuilderAtEnd(builder, typeCheckBlock);

                // Get super types
                indices = new[]
                {
                    LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                    LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.SuperTypes, false), // Super types
                };

                var superTypes = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                superTypes = LLVM.BuildLoad(builder, superTypes, string.Empty);

                // Get actual super type
                indices = new[]
                {
                    LLVM.ConstInt(int32LLVM, (ulong)@class.Depth, false), // Pointer indirection
                };
                var superType = LLVM.BuildGEP(builder, superTypes, indices, string.Empty);
                superType = LLVM.BuildLoad(builder, superType, string.Empty);

                // Cast as appropriate pointer type (for next PHI incoming if success)
                castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty);

                // Compare super type in array at given depth with expected one
                var typeCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntEQ, superType, LLVM.ConstPointerCast(@class.GeneratedEETypeRuntimeLLVM, intPtrLLVM), string.Empty);
                LLVM.BuildCondBr(builder, typeCompareResult, typeCheckDoneBlock, typeNotMatchBlock);
            }

            // Start new typeNotMatchBlock: set object to null and jump to typeCheckDoneBlock
            LLVM.PositionBuilderAtEnd(builder, typeNotMatchBlock);
            if (opcode == Code.Castclass)
            {
                // Create InvalidCastException object
                var invalidCastExceptionClass = GetClass(corlib.MainModule.GetType(typeof(InvalidCastException).FullName));
                EmitNewobj(functionContext, invalidCastExceptionClass.Type, invalidCastExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0));
                var invalidCastException = stack.Pop();
                GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, invalidCastException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)});
                LLVM.BuildUnreachable(builder);
            }
            else
            {
                LLVM.BuildBr(builder, typeCheckDoneBlock);
            }

            // Start new typeCheckDoneBlock
            LLVM.PositionBuilderAtEnd(builder, typeCheckDoneBlock);
            functionContext.BasicBlock = typeCheckDoneBlock;

            // Put back with appropriate type at end of stack
            ValueRef mergedVariable;
            if (opcode == Code.Castclass)
            {
                mergedVariable = LLVM.BuildPhi(builder, castedPointerType, string.Empty);
                LLVM.AddIncoming(mergedVariable,
                    new[] { castedPointerObject, LLVM.ConstPointerNull(castedPointerType) },
                    new[] { typeCheckBlock, currentBlock });
            }
            else
            {
                mergedVariable = LLVM.BuildPhi(builder, castedPointerType, string.Empty);
                LLVM.AddIncoming(mergedVariable,
                    new[] {castedPointerObject, LLVM.ConstPointerNull(castedPointerType)},
                    new[] {typeCheckBlock, typeNotMatchBlock});
            }
            stack.Add(new StackValue(obj.StackType, @class.Type, mergedVariable));
        }
コード例 #26
0
ファイル: Compiler.Scope.cs プロジェクト: bpietroiu/SharpLang
        private void PrepareScopes(FunctionCompilerContext functionContext, Function function)
        {
            var methodReference = function.MethodReference;
            var body            = functionContext.Body;

            // Add root scope variables
            // Note: could be null
            var newScope = new Scope(body.Scope);

            functionContext.Scopes.Add(newScope);

            // Find class scope
            var debugClass = GetOrCreateDebugClass(GetClass(function.DeclaringType));

            // Update debug information
            int line = 0;

            var    startSequencePoint = body.Instructions[0].SequencePoint;
            string url;

            if (startSequencePoint != null)
            {
                url  = startSequencePoint.Document.Url;
                line = startSequencePoint.StartLine;
            }
            else
            {
                url = assembly.MainModule.FullyQualifiedName;
            }

            functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url));
            var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]);
            var functionType           = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes);

            // Replace . with :: so that gdb properly understands it is namespaces.
            var methodDebugName = string.Format("{0}::{1}", methodReference.DeclaringType.FullName.Replace(".", "::"), methodReference.Name);

            newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, debugClass, methodReference.Name, methodDebugName,
                                                                   functionContext.DebugFile, (uint)line, functionType,
                                                                   false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty);

            SetupDebugLocation(body.Instructions[0].SequencePoint, newScope);
            if (body.Scope != null)
            {
                EnterScope(functionContext, newScope);
            }
            else
            {
                // Emit locals (if no scopes)
                for (int index = 0; index < body.Variables.Count; index++)
                {
                    var variable     = functionContext.Locals[index];
                    var variableName = body.Variables[index].Name;
                    if (string.IsNullOrEmpty(variableName))
                    {
                        variableName = "var" + index;
                    }

                    EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName);
                }
            }

            // Emit args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var arg = functionContext.Arguments[index];

                var argName = LLVM.GetValueName(arg.Value);

                EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1);
            }
        }
コード例 #27
0
ファイル: Compiler.Function.cs プロジェクト: frje/SharpLang
        private void EnsureClassInitialized(FunctionCompilerContext functionContext, Class @class)
        {
            // TODO: Add thread protection (with lock and actual class initialization in a separate method to improve code reuse)
            // If there was a type initializer, let's call it.
            // Even through the type initializer itself will check if type initialization is necessary inside a lock,
            // we do a quick check before here already (early exit).
            //  if (!classInitialized)
            //  {
            //      EnsureClassInitialized();
            //  }
            // with:
            //  void EnsureClassInitialized()
            //  {
            //      lock (initMutex)
            //      {
            //          if (!classInitialized)
            //          {
            //              InitializeClass();
            //              classInitialized = true;
            //          }
            //      }
            //  }
            if (@class.InitializeType != ValueRef.Empty)
            {
                var functionGlobal = functionContext.Function.GeneratedValue;

                // Note: we temporarily ignore extern class
                // TODO: This will be reactivated as soon as we start linking multiple modules
                if ([email protected])
                    return;

                // Check if class is initialized
                var indices = new[]
                {
                    LLVM.ConstInt(int32Type, 0, false),                                                 // Pointer indirection
                    LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.TypeInitialized, false),        // Type initialized flag
                };

                var classInitializedAddress = LLVM.BuildInBoundsGEP(builder, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty);
                var classInitialized = LLVM.BuildLoad(builder, classInitializedAddress, string.Empty);

                var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
                var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);

                LLVM.BuildCondBr(builder, classInitialized, nextBlock, typeNeedInitBlock);

                // Initialize class (first time)
                LLVM.PositionBuilderAtEnd(builder, typeNeedInitBlock);
                LLVM.BuildCall(builder, @class.InitializeType, new ValueRef[0], string.Empty);

                // Set flag so that it won't be initialized again
                // Note: Inner function already does this, so commented out for now.
                // However, enabling it here might help LLVM performs additional optimization if happens multiple time in same function? (vs heavier code? need to test in practice)
                //LLVM.BuildStore(builder, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 1, false), classInitializedAddress);
                LLVM.BuildBr(builder, nextBlock);

                // Normal path
                LLVM.PositionBuilderAtEnd(builder, nextBlock);
                functionContext.BasicBlock = nextBlock;
            }
        }
コード例 #28
0
        /// <summary>
        /// Compiles the given method definition.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="function">The function.</param>
        /// <exception cref="System.NotSupportedException"></exception>
        /// <exception cref="System.NotImplementedException"></exception>
        /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception>
        private void CompileFunction(MethodReference methodReference, Function function)
        {
            var method = methodReference.Resolve();

            var body = method.Body;
            var codeSize = body != null ? body.CodeSize : 0;
            var functionGlobal = function.GeneratedValue;

            var functionContext = new FunctionCompilerContext(function);

            // Create default blocks (alloca one, and entry one)
            var allocaBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
            functionContext.BasicBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);

            {
                LLVM.PositionBuilderAtEnd(builderAlloca, allocaBlock);
                var allocaExitJump = LLVM.BuildBr(builderAlloca, functionContext.BasicBlock);
                LLVM.PositionBuilderBefore(builderAlloca, allocaExitJump);
            }

            LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock);

            if (methodReference.DeclaringType.Name == "EncodingHelper" && methodReference.Name == "LoadGetStringPlatform")
            {
                body = new MethodBody(method);
                body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
                body.Instructions.Add(Instruction.Create(OpCodes.Ret));
            }

            if ((method.ImplAttributes & MethodImplAttributes.Runtime) != 0)
            {
                var declaringClass = GetClass(function.DeclaringType);

                // Generate IL for various methods
                if (declaringClass.BaseType != null &&
                    declaringClass.BaseType.Type.TypeReferenceCecil.FullName == typeof(MulticastDelegate).FullName)
                {
                    body = GenerateDelegateMethod(method, declaringClass);
                    if (body == null)
                        return;

                    codeSize = body.UpdateInstructionOffsets();
                }

                // Reposition builder at end, in case it was used
                LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock);
            }

            if (body == null)
                return;

            functionContext.Body = body;

            var numParams = method.Parameters.Count;

            // Create stack, locals and args
            var stack = new FunctionStack(body.MaxStackSize);
            var locals = new List<StackValue>(body.Variables.Count);
            var args = new List<StackValue>(numParams);
            var exceptionHandlers = new List<ExceptionHandlerInfo>();
            var activeTryHandlers = new List<ExceptionHandlerInfo>();

            functionContext.Stack = stack;
            functionContext.Locals = locals;
            functionContext.Arguments = args;
            functionContext.Scopes = new List<Scope>();
            functionContext.ExceptionHandlers = exceptionHandlers;
            functionContext.ActiveTryHandlers = activeTryHandlers;

            // Process locals
            foreach (var local in body.Variables)
            {
                // TODO: Anything to do on pinned objects?
                //if (local.IsPinned)
                //    throw new NotSupportedException();

                var type = GetType(ResolveGenericsVisitor.Process(methodReference, local.VariableType), TypeState.StackComplete);
                locals.Add(new StackValue(type.StackType, type, LLVM.BuildAlloca(builderAlloca, type.DefaultTypeLLVM, local.Name)));

                // Force value types to be emitted right away
                if (type.TypeDefinitionCecil.IsValueType)
                    GetClass(type);
            }

            bool hasStructValueReturn = function.Signature.ReturnType.ABIParameterInfo.Kind == ABIParameterInfoKind.Indirect;

            // Process args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var argType = function.ParameterTypes[index];
                var llvmIndex = hasStructValueReturn ? index + 1 : index;
                var arg = LLVM.GetParam(functionGlobal, (uint)llvmIndex);

                // Force value types to be emitted right away
                if (argType.TypeDefinitionCecil.IsValueType)
                    GetClass(argType);

                var parameterIndex = index - (functionContext.Method.HasThis ? 1 : 0);
                var parameterName = parameterIndex == -1 ? "this" : method.Parameters[parameterIndex].Name;

                ValueRef storage;
                if (argType.StackType == StackValueType.Value)
                {
                    var parameterType = function.Signature.ParameterTypes[index];
                    switch (parameterType.ABIParameterInfo.Kind)
                    {
                        case ABIParameterInfoKind.Direct:
                            storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName);
                            LLVM.BuildStore(builder, arg, storage);
                            break;
                        case ABIParameterInfoKind.Indirect:
                            // Directly use byval area as storage (caller made an allocation & copy for this parameter)
                            storage = arg;
                            LLVM.SetValueName(arg, parameterName);
                            break;
                        case ABIParameterInfoKind.Coerced:
                            // Coerce to integer type
                            storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName);
                            var castedStorage = LLVM.BuildPointerCast(builderAlloca, storage, LLVM.PointerType(parameterType.ABIParameterInfo.CoerceType, 0), string.Empty);
                            LLVM.BuildStore(builderAlloca, arg, castedStorage);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
                else
                {
                    // Copy argument on stack
                    storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName);
                    LLVM.BuildStore(builderAlloca, arg, storage);
                }

                args.Add(new StackValue(argType.StackType, argType, storage));
            }

            // Some wasted space due to unused offsets, but we only keep one so it should be fine.
            // TODO: Reuse same allocated instance per thread, and grow it only if necessary
            var branchTargets = new bool[codeSize];
            var basicBlocks = new BasicBlockRef[codeSize];
            var forwardStacks = new StackValue[codeSize][];

            functionContext.BasicBlocks = basicBlocks;
            functionContext.ForwardStacks = forwardStacks;

            // Find branch targets (which will require PHI node for stack merging)
            for (int index = 0; index < body.Instructions.Count; index++)
            {
                var instruction = body.Instructions[index];

                var flowControl = instruction.OpCode.FlowControl;

                // Process branch targets
                if (flowControl == FlowControl.Cond_Branch
                    || flowControl == FlowControl.Branch)
                {
                    var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand };

                    foreach (var target in targets)
                    {
                        // Operand Target can be reached
                        branchTargets[target.Offset] = true;
                    }
                }

                // Need to enforce a block to be created for the next instruction after a conditional branch
                // TODO: Break?
                if (flowControl == FlowControl.Cond_Branch)
                {
                    if (instruction.Next != null)
                        branchTargets[instruction.Next.Offset] = true;
                }
            }

            // Setup exception handling
            if (body.HasExceptionHandlers)
            {
                // Add an "ehselector.slot" i32 local, and a "exn.slot" Object reference local
                functionContext.ExceptionHandlerSelectorSlot = LLVM.BuildAlloca(builderAlloca, int32LLVM, "ehselector.slot");
                functionContext.ExceptionSlot = LLVM.BuildAlloca(builderAlloca, @object.DefaultTypeLLVM, "exn.slot");
                functionContext.EndfinallyJumpTarget = LLVM.BuildAlloca(builderAlloca, int32LLVM, "endfinally.jumptarget");

                // Create resume exception block
                functionContext.ResumeExceptionBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "eh.resume");
                LLVM.PositionBuilderAtEnd(builder2, functionContext.ResumeExceptionBlock);
                var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, "exn");
                var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel");

                exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, intPtrLLVM, "exn");
                var landingPadValue = LLVM.BuildInsertValue(builder2, LLVM.GetUndef(caughtResultLLVM), exceptionObject, 0, "lpad.val");
                landingPadValue = LLVM.BuildInsertValue(builder2, landingPadValue, ehselectorValue, 1, "lpad.val");

                LLVM.BuildResume(builder2, landingPadValue);

                // Exception handlers blocks are also branch targets
                foreach (var exceptionHandler in body.ExceptionHandlers)
                {
                    branchTargets[exceptionHandler.HandlerStart.Offset] = true;
                }
            }

            // Create basic block
            // TODO: Could be done during previous pass
            for (int offset = 0; offset < branchTargets.Length; offset++)
            {
                // Create a basic block if this was a branch target or an instruction after a conditional branch
                if (branchTargets[offset])
                {
                    basicBlocks[offset] = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}", offset));
                }
            }

            // Create catch clause stack
            if (body.HasExceptionHandlers)
            {
                // Exception catch handlers blocks are also branch targets
                foreach (var exceptionHandler in body.ExceptionHandlers)
                {
                    exceptionHandlers.Add(new ExceptionHandlerInfo(exceptionHandler));

                    if (exceptionHandler.HandlerType != ExceptionHandlerType.Catch)
                        continue;

                    var handlerStart = exceptionHandler.HandlerStart.Offset;

                    var catchBlock = basicBlocks[handlerStart];
                    var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.CatchType));

                    // Extract exception
                    LLVM.PositionBuilderAtEnd(builder2, catchBlock);
                    var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, string.Empty);
                    exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, catchClass.Type.DefaultTypeLLVM, string.Empty);

                    // Erase exception from exn.slot (it has been handled)
                    LLVM.BuildStore(builder2, LLVM.ConstNull(@object.DefaultTypeLLVM), functionContext.ExceptionSlot);
                    LLVM.BuildStore(builder2, LLVM.ConstInt(int32LLVM, 0, false), functionContext.ExceptionHandlerSelectorSlot);

                    forwardStacks[handlerStart] = new[]
                    {
                        new StackValue(catchClass.Type.StackType, catchClass.Type, exceptionObject)
                    };
                }
            }

            PrepareScopes(functionContext, function);

            foreach (var instruction in body.Instructions)
            {
                try
                {
                    // Check if any exception handlers might have changed
                    if (body.HasExceptionHandlers)
                        UpdateExceptionHandlers(functionContext, instruction);

                    if (branchTargets[instruction.Offset])
                        UpdateBranching(functionContext, instruction);

                    ProcessScopes(functionContext, instruction);

                    // Reset states
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Implicit;

                    EmitInstruction(functionContext, instruction);

                    // If we do a jump, let's merge stack
                    var flowControl = instruction.OpCode.FlowControl;
                    if (flowControl == FlowControl.Cond_Branch
                        || flowControl == FlowControl.Branch)
                        MergeStacks(functionContext, instruction);
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException(string.Format("Error while processing instruction {0} of method {1}", instruction, methodReference), e);
                }
            }

            if (body.HasExceptionHandlers)
            {
                // Place eh.resume block at the very end
                LLVM.MoveBasicBlockAfter(functionContext.ResumeExceptionBlock, LLVM.GetLastBasicBlock(functionGlobal));
            }
        }
コード例 #29
0
        private ValueRef ResolveVirtualMethod(FunctionCompilerContext functionContext, ref Function targetMethod, ref StackValue thisObject)
        {
            // Get constrained class
            var constrainedClass = functionContext.ConstrainedClass;
            if (constrainedClass != null)
                functionContext.ConstrainedClass = null;

            var method = targetMethod.MethodReference.Resolve();

            ValueRef resolvedMethod;
            if ((method.Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual)
            {
                // Build indices for GEP
                var indices = new[]
                {
                    LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                    LLVM.ConstInt(int32LLVM, (int) ObjectFields.RuntimeTypeInfo, false), // Access RTTI
                };

                Class @class;

                // Process constrained class
                if (constrainedClass != null)
                {
                    if (!constrainedClass.Type.TypeDefinitionCecil.IsValueType)
                    {
                        // If thisType is a reference type, dereference
                        thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type,
                            LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty),
                                constrainedClass.Type.DefaultTypeLLVM, string.Empty));
                    }
                    else
                    {
                        var matchingMethod = CecilExtensions.TryMatchMethod(constrainedClass, targetMethod.MethodReference,
                            false);
                        if (matchingMethod != null)
                        {
                            // If thisType is a value type and implements method, then ptr is passed unmodified
                            targetMethod = matchingMethod;

                            // Convert to appropriate type (if necessary)
                            var refType = GetType(constrainedClass.Type.TypeReferenceCecil.MakeByReferenceType(), TypeState.Opaque);
                            if (thisObject.StackType != StackValueType.Reference || thisObject.Type != refType)
                            {
                                thisObject = new StackValue(refType.StackType, refType,
                                    LLVM.BuildPointerCast(builder, thisObject.Value, refType.DefaultTypeLLVM, string.Empty));
                            }
                        }
                        else
                        {
                            // If thisType is a value type and doesn't implement method, dereference (note: already a pointer), box and pass as this
                            if (constrainedClass.Type.StackType == StackValueType.Value)
                                thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type,
                                    LLVM.BuildPointerCast(builder, thisObject.Value,
                                        LLVM.PointerType(constrainedClass.Type.DefaultTypeLLVM, 0), string.Empty));
                            else
                                thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type,
                                    LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty),
                                        constrainedClass.Type.DefaultTypeLLVM, string.Empty));

                            thisObject = new StackValue(StackValueType.Object, constrainedClass.Type,
                                BoxValueType(constrainedClass.Type, thisObject));
                        }
                    }

                    @class = constrainedClass;
                }
                else
                {
                    @class = GetClass(thisObject.Type);
                }

                // TODO: Checking actual type stored in thisObject we might be able to statically resolve method?

                // If it's a byref value type, emit a normal call
                if (thisObject.Type.TypeReferenceCecil.IsByReference
                    && GetType(((ByReferenceType)thisObject.Type.TypeReferenceCecil).ElementType, TypeState.Opaque).TypeDefinitionCecil.IsValueType
                    && MemberEqualityComparer.Default.Equals(targetMethod.DeclaringType.TypeReferenceCecil, ((ByReferenceType)thisObject.Type.TypeReferenceCecil).ElementType))
                {
                    resolvedMethod = targetMethod.GeneratedValue;
                }
                else
                {
                    // Get RTTI pointer
                    var rttiPointer = LLVM.BuildInBoundsGEP(builder, thisObject.Value, indices, string.Empty);
                    rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty);

                    if (targetMethod.MethodReference.DeclaringType.Resolve().IsInterface)
                    {
                        // Interface call

                        // Cast to object type (enough to have IMT)
                        rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(GetClass(@object).GeneratedEETypeRuntimeLLVM), string.Empty);

                        // Get method stored in IMT slot
                        indices = new[]
                        {
                            LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                            LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.InterfaceMethodTable, false), // Access IMT
                            LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific IMT slot
                        };

                        var imtEntry = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);

                        var methodPointer = LLVM.BuildLoad(builder, imtEntry, string.Empty);

                        // Resolve interface call
                        // TODO: Improve resolveInterfaceCall(): if no match is found, it's likely due to covariance/contravariance, so we will need a fallback
                        resolvedMethod = LLVM.BuildCall(builder, resolveInterfaceCallFunctionLLVM, new[]
                        {
                            //LLVM.ConstInt(int32LLVM, methodId, false),
                            LLVM.ConstPointerCast(targetMethod.GeneratedValue, intPtrLLVM),
                            methodPointer,
                        }, string.Empty);
                        resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod,
                            LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty);
                    }
                    else
                    {
                        // Cast to expected RTTI type
                        rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(@class.GeneratedEETypeRuntimeLLVM), string.Empty);

                        // Virtual table call
                        if (targetMethod.VirtualSlot == -1)
                        {
                            throw new InvalidOperationException("Trying to call a virtual method without slot.");
                        }

                        // Get method stored in vtable slot
                        indices = new[]
                        {
                            LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                            LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable
                            LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot
                        };

                        var vtable = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                        resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty);
                        resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty);
                    }
                }
            }
            else if (method.HasPInvokeInfo)
            {
                // PInvoke behaves almost like a virtual call, but directly use given class vtable
                var @class = GetClass(targetMethod.DeclaringType);

                // Get method stored in vtable slot
                var indices = new[]
                {
                    LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection
                    LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable
                    LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot
                };

                var vtable = LLVM.BuildInBoundsGEP(builder, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty);
                resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty);
                resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty);
            }
            else
            {
                // Normal call
                // Callvirt on non-virtual function is only done to force "this" NULL check
                // However, that's probably a part of the .NET spec that we want to skip for performance reasons,
                // so maybe we should keep this as is?
                resolvedMethod = targetMethod.GeneratedValue;
            }
            return resolvedMethod;
        }
コード例 #30
0
        public ModuleRef GenerateModule()
        {
            LLVM.DIBuilderCreateCompileUnit(debugBuilder,
                                            0x4, // DW_LANG_C_plus_plus
                                            "file", "directory", "SharpLang", false, string.Empty, 1, string.Empty);

            LLVM.AddModuleFlag(module, "Dwarf Version", 4);
            LLVM.AddModuleFlag(module, "Debug Info Version", 1);

            // Process methods
            while (classesToGenerate.Count > 0)
            {
                var classToGenerate = classesToGenerate.Dequeue();
                if (classToGenerate.IsLocal)
                {
                    PrepareClassMethods(classToGenerate);
                }
            }

            // Generate code
            while (methodsToCompile.Count > 0)
            {
                var methodToCompile = methodsToCompile.Dequeue();
                //Console.WriteLine("Compiling {0}", methodToCompile.Key.FullName);
                CompileFunction(methodToCompile.Key, methodToCompile.Value);
            }

            // Prepare global module constructor
            var globalCtorFunctionType = LLVM.FunctionType(LLVM.VoidTypeInContext(context), new TypeRef[0], false);
            var globalCtor             = LLVM.AddFunction(module, "initializeSharpLangModule", globalCtorFunctionType);

            LLVM.SetLinkage(globalCtor, Linkage.PrivateLinkage);
            LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, globalCtor, string.Empty));

            if (!TestMode)
            {
                // Emit metadata
                var metadataBytes = ReadMetadata(assembly.MainModule.FullyQualifiedName);
                var metadataData  = CreateDataConstant(metadataBytes);

                var metadataGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(metadataData), "metadata");
                LLVM.SetInitializer(metadataGlobal, metadataData);
                LLVM.SetLinkage(metadataGlobal, Linkage.PrivateLinkage);


                // Use metadata to initialize a SharpLangModule, that will be created at module load time using a global ctor
                sharpLangModuleType = GetType(corlib.MainModule.GetType("System.SharpLangModule"), TypeState.VTableEmitted);
                sharpLangTypeType   = GetType(corlib.MainModule.GetType("System.SharpLangTypeDefinition"), TypeState.VTableEmitted); // Was only StackComplete until now

                // Get ctor for SharpLangModule and SharpLangType
                var moduleCtor = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Resolve().IsConstructor);

                var registerTypeMethod = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Name == "RegisterType");
                var sortTypesMethod    = sharpLangModuleType.Class.Functions.First(x => x.DeclaringType == sharpLangModuleType && x.MethodReference.Name == "SortTypes");

                // Initialize SharpLangModule instance:
                //   new SharpLangModule(moduleName, metadataStart, metadataLength)
                var sharpLangModuleGlobal = metadataPerModule[assembly.MainModule];
                var functionContext       = new FunctionCompilerContext(globalCtor);
                functionContext.Stack = new List <StackValue>();
                functionContext.Stack.Add(new StackValue(StackValueType.Object, sharpLangModuleType, sharpLangModuleGlobal));
                functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, metadataGlobal));
                functionContext.Stack.Add(new StackValue(StackValueType.Int32, int32, LLVM.ConstInt(int32LLVM, (ulong)metadataBytes.Length, false)));

                // Setup initial value (note: VTable should be valid)
                LLVM.SetLinkage(sharpLangModuleGlobal, Linkage.ExternalLinkage);
                LLVM.SetInitializer(sharpLangModuleGlobal, SetupVTableConstant(LLVM.ConstNull(sharpLangModuleType.ObjectTypeLLVM), sharpLangModuleType.Class));
                metadataPerModule[assembly.MainModule] = sharpLangModuleGlobal;

                EmitCall(functionContext, moduleCtor.Signature, moduleCtor.GeneratedValue);

                // Register types
                foreach (var type in types)
                {
                    var @class = type.Value.Class;

                    // Skip incomplete types
                    if (@class == null || [email protected])
                    {
                        continue;
                    }

                    // Skip if no RTTI initializer
                    if (LLVM.GetInitializer(@class.GeneratedEETypeRuntimeLLVM) == ValueRef.Empty)
                    {
                        continue;
                    }

                    // Skip if interface (fake RTTI pointer)
                    if (type.Value.TypeDefinitionCecil.IsInterface)
                    {
                        continue;
                    }

                    functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.ConstPointerCast(@class.GeneratedEETypeRuntimeLLVM, intPtrLLVM)));
                    EmitCall(functionContext, registerTypeMethod.Signature, registerTypeMethod.GeneratedValue);
                }

                // Sort and remove duplicates after adding all our types
                // TODO: Somehow sort everything before insertion at compile time?
                EmitCall(functionContext, sortTypesMethod.Signature, sortTypesMethod.GeneratedValue);

                LLVM.BuildRetVoid(builder);
            }
            else
            {
                LLVM.BuildRetVoid(builder);
            }

            // Prepare global ctors
            {
                var globalCtorType    = LLVM.StructTypeInContext(context, new[] { int32LLVM, LLVM.PointerType(globalCtorFunctionType, 0) }, true);
                var globalCtorsGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(globalCtorType, 1), "llvm.global_ctors");
                LLVM.SetLinkage(globalCtorsGlobal, Linkage.AppendingLinkage);
                LLVM.SetInitializer(globalCtorsGlobal,
                                    LLVM.ConstArray(globalCtorType, new [] {
                    LLVM.ConstNamedStruct(globalCtorType, new[]
                    {
                        LLVM.ConstInt(int32LLVM, (ulong)65536, false),
                        globalCtor,
                    })
                }));
            }

            // Emit "main" which will call the assembly entry point (if any)
            Function entryPoint;

            if (assembly.EntryPoint != null && functions.TryGetValue(assembly.EntryPoint, out entryPoint))
            {
                var mainFunctionType = LLVM.FunctionType(int32LLVM, new TypeRef[0], false);
                var mainFunction     = LLVM.AddFunction(module, "main", mainFunctionType);
                LLVM.SetLinkage(mainFunction, Linkage.ExternalLinkage);
                LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, mainFunction, string.Empty));

                var parameters = (entryPoint.ParameterTypes.Length > 0)
                        ? new[] { LLVM.ConstPointerNull(entryPoint.ParameterTypes[0].DefaultTypeLLVM) }
                        : new ValueRef[0];

                LLVM.BuildCall(builder, entryPoint.GeneratedValue, parameters, string.Empty);
                LLVM.BuildRet(builder, LLVM.ConstInt(int32LLVM, 0, false));
            }

            LLVM.DIBuilderFinalize(debugBuilder);
            LLVM.DIBuilderDispose(debugBuilder);
            LLVM.DisposeBuilder(builder);

            return(module);
        }
コード例 #31
0
        private void EmitInstruction(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var methodReference = functionContext.MethodReference;
            var stack = functionContext.Stack;
            var args = functionContext.Arguments;
            var locals = functionContext.Locals;
            var exceptionHandlers = functionContext.ExceptionHandlers;

            var opcode = instruction.OpCode.Code;

            switch (opcode)
            {
                case Code.Nop:
                {
                    // TODO: Insert nop? Debugger step?

                    // Check if there is a custom action
                    Action<FunctionStack> instructionAction;
                    if (InstructionActions.TryGetValue(instruction, out instructionAction))
                    {
                        instructionAction(stack);
                    }
                    break;
                }
                case Code.Pop:
                {
                    // Pop and discard last stack value
                    stack.Pop();
                    break;
                }
                case Code.Dup:
                {
                    // Readd last stack value
                    var lastStackValue = stack[stack.Count - 1];
                    stack.Add(new StackValue(lastStackValue.StackType, lastStackValue.Type, lastStackValue.Value));
                    break;
                }
                case Code.Ret:
                {
                    EmitRet(functionContext);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Call:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    // If calling a static method, make sure .cctor has been called
                    if (!targetMethodReference.HasThis)
                        EnsureClassInitialized(functionContext, GetClass(targetMethod.DeclaringType));

                    var overrideMethod = targetMethod.GeneratedValue;

                    // PInvoke: go through ResolveVirtualMethod
                    var resolvedMethod = targetMethodReference.Resolve();
                    if (resolvedMethod != null && resolvedMethod.HasPInvokeInfo)
                    {
                        StackValue thisObject = null;
                        overrideMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject);
                    }

                    EmitCall(functionContext, targetMethod.Signature, overrideMethod);

                    break;
                }
                case Code.Calli:
                {
                    var callSite = (CallSite)instruction.Operand;

                    // Generate function type
                    var functionSignature = CreateFunctionSignature(methodReference, callSite);
                    var functionType = CreateFunctionTypeLLVM(functionSignature);

                    var methodPtr = stack.Pop();

                    var castedMethodPtr = LLVM.BuildPointerCast(builder, methodPtr.Value, LLVM.PointerType(functionType, 0), string.Empty);


                    EmitCall(functionContext, functionSignature, castedMethodPtr);

                    break;
                }
                case Code.Callvirt:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    var thisObject = stack[stack.Count - targetMethod.ParameterTypes.Length];

                    var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject);

                    stack[stack.Count - targetMethod.ParameterTypes.Length] = thisObject;

                    // Emit call
                    EmitCall(functionContext, targetMethod.Signature, resolvedMethod);

                    break;
                }
                case Code.Constrained:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    functionContext.ConstrainedClass = GetClass(typeReference);

                    break;
                }
                case Code.Readonly:
                {
                    break;
                }

                #region Obj opcodes (Initobj, Newobj, Stobj, Ldobj, etc...)
                case Code.Initobj:
                {
                    var address = stack.Pop();
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var type = GetType(typeReference, TypeState.StackComplete);
                    EmitInitobj(address, type);
                    break;
                }

                case Code.Newobj:
                {
                    var ctorReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var ctor = GetFunction(ctorReference);
                    var type = GetType(ctorReference.DeclaringType, TypeState.TypeComplete);

                    EmitNewobj(functionContext, type, ctor);

                    break;
                }

                case Code.Stobj:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete);

                    EmitStobj(stack, type, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                case Code.Ldobj:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete);

                    EmitLdobj(stack, type, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                #endregion

                case Code.Cpblk:
                {
                    var size = stack.Pop();
                    var source = stack.Pop();
                    var destination = stack.Pop();

                    var destinationValue = destination.Value;
                    var sourceValue = source.Value;
                    var sizeValue = size.Value;

                    // Adjust types
                    if (LLVM.TypeOf(destinationValue) != intPtrLLVM)
                        destinationValue = LLVM.BuildPointerCast(builder, destinationValue, intPtrLLVM, string.Empty);
                    if (LLVM.TypeOf(sourceValue) != intPtrLLVM)
                        sourceValue = LLVM.BuildPointerCast(builder, sourceValue, intPtrLLVM, string.Empty);
                    sizeValue = ConvertToInt(sizeValue, int32LLVM);

                    var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)Intrinsics.memcpy, new[] { intPtrLLVM, intPtrLLVM, int32LLVM });
                    LLVM.BuildCall(builder, intrinsic,
                        new[]
                        {
                            destinationValue,
                            sourceValue,
                            sizeValue,
                            LLVM.ConstInt(int32LLVM, (functionContext.InstructionFlags & InstructionFlags.Unaligned) != 0 ? 1 : (ulong)intPtrSize, false), // alignment
                            LLVM.ConstInt(LLVM.Int1TypeInContext(context), (functionContext.InstructionFlags & InstructionFlags.Volatile) != 0 ? 1UL : 0, false) // volatile
                        } , string.Empty);

                    functionContext.InstructionFlags = InstructionFlags.None;
                    break;
                }

                case Code.Sizeof:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var type = GetType(typeReference, TypeState.StackComplete);

                    // Use type because @class might be null (i.e. void*)
                    var objectSize = LLVM.SizeOf(type.DefaultTypeLLVM);
                    objectSize = LLVM.BuildIntCast(builder, objectSize, int32LLVM, string.Empty);

                    stack.Add(new StackValue(StackValueType.Int32, int32, objectSize));

                    break;
                }

                case Code.Localloc:
                {
                    EmitLocalloc(stack);
                    break;
                }

                case Code.Castclass:
                case Code.Isinst:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var @class = GetClass(typeReference);

                    EmitIsOrCastclass(functionContext, stack, @class, opcode, instruction.Offset);

                    break;
                }

                case Code.Ldtoken:
                {
                    var token = instruction.Operand;

                    Class runtimeHandleClass;
                    var runtimeHandleValue = ValueRef.Empty;

                    if (token is TypeReference)
                    {
                        var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)token), TypeState.VTableEmitted);

                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeTypeHandle).FullName));

                        if (type != null && type.Class != null)
                        {
                            runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM);
                            runtimeHandleValue = LLVM.BuildInsertValue(builder, runtimeHandleValue, LLVM.ConstPointerCast(type.Class.GeneratedEETypeTokenLLVM, intPtrLLVM), 0, string.Empty);
                        }
                        else
                        {
                            // TODO: Support generic open types and special types such as void
                            // We should issue a warning
                        }
                    }
                    else if (token is FieldReference)
                    {
                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeFieldHandle).FullName));

                        var fieldReference = (FieldReference)token;
                        var fieldDefinition = fieldReference.Resolve();

                        // HACK: Temporary hack so that RuntimeHelpers.InitialiseArray can properly initialize array constants from static struct with initial data.
                        // For now we just pass the field address (instead of a real RuntimeFieldHandle.
                        if (fieldDefinition.IsStatic && (fieldDefinition.Attributes & FieldAttributes.HasFieldRVA) != 0)
                        {
                            // Resolve class and field
                            var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                            var field = @class.StaticFields[fieldDefinition];

                            EmitLdsflda(stack, field);
                            var fieldAddress = stack.Pop().Value;
                            fieldAddress = LLVM.BuildPointerCast(builder, fieldAddress, intPtrLLVM, string.Empty);
                            runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM);
                            runtimeHandleValue = LLVM.BuildInsertValue(builder, runtimeHandleValue, fieldAddress, 0, string.Empty);
                        }
                    }
                    else if (token is MethodReference)
                    {
                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeMethodHandle).FullName));
                    }
                    else
                    {
                        throw new NotSupportedException("Invalid ldtoken operand.");
                    }

                    // Setup default value
                    if (runtimeHandleValue == ValueRef.Empty)
                        runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM);

                    // Add indirection
                    var runtimeHandleValueIndirect = LLVM.BuildAlloca(builderAlloca, runtimeHandleClass.Type.DataTypeLLVM, string.Empty);
                    LLVM.BuildStore(builder, runtimeHandleValue, runtimeHandleValueIndirect);

                    // TODO: Actually transform type to RTTI token.
                    stack.Add(new StackValue(runtimeHandleClass.Type.StackType, runtimeHandleClass.Type, runtimeHandleValueIndirect));

                    break;
                }

                case Code.Ldftn:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    EmitLdftn(stack, targetMethod);

                    break;
                }
                case Code.Ldvirtftn:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    var thisObject = stack.Pop();

                    var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject);

                    stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, resolvedMethod, intPtrLLVM, string.Empty)));

                    break;
                }

                #region Box/Unbox opcodes
                case Code.Box:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var type = GetType(typeReference, TypeState.VTableEmitted);

                    // Only value types need to be boxed
                    if (type.TypeDefinitionCecil.IsValueType)
                    {
                        EmitBoxValueType(stack, type);
                    }

                    break;
                }

                case Code.Unbox_Any:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var type = GetType(typeReference, TypeState.VTableEmitted);

                    if (type.TypeDefinitionCecil.IsValueType)
                    {
                        EmitUnboxAnyValueType(stack, type);
                    }
                    else
                    {
                        // Should act as "castclass" on reference types
                        goto case Code.Castclass;
                    }

                    break;
                }
                #endregion

                #region Array opcodes (Newarr, Ldlen, Stelem_Ref, etc...)
                case Code.Newarr:
                {
                    var elementType = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete);

                    EmitNewarr(stack, elementType);

                    break;
                }
                case Code.Ldlen:
                {
                    EmitLdlen(stack);

                    break;
                }
                case Code.Ldelema:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.Opaque);

                    EmitLdelema(stack, type);

                    break;
                }
                case Code.Ldelem_I1:
                case Code.Ldelem_I2:
                case Code.Ldelem_I4:
                case Code.Ldelem_I8:
                case Code.Ldelem_U1:
                case Code.Ldelem_U2:
                case Code.Ldelem_U4:
                case Code.Ldelem_R4:
                case Code.Ldelem_R8:
                case Code.Ldelem_Any:
                case Code.Ldelem_Ref:
                {
                    // TODO: Properly use opcode for type conversion
                    EmitLdelem(stack);

                    break;
                }
                case Code.Stelem_I1:
                case Code.Stelem_I2:
                case Code.Stelem_I4:
                case Code.Stelem_I8:
                case Code.Stelem_R4:
                case Code.Stelem_R8:
                case Code.Stelem_Any:
                case Code.Stelem_Ref:
                {
                    // TODO: Properly use opcode for type conversion
                    EmitStelem(stack);

                    break;
                }
                #endregion

                #region Argument opcodes (Ldarg, Ldarga, Starg, etc...)
                // Ldarg
                case Code.Ldarg_0:
                case Code.Ldarg_1:
                case Code.Ldarg_2:
                case Code.Ldarg_3:
                {
                    var value = opcode - Code.Ldarg_0;
                    EmitLdarg(stack, args, value);
                    break;
                }
                case Code.Ldarg_S:
                case Code.Ldarg:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitLdarg(stack, args, value);
                    break;
                }
                case Code.Ldarga:
                case Code.Ldarga_S:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitLdarga(stack, args, value);
                    break;
                }
                case Code.Starg:
                case Code.Starg_S:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitStarg(stack, args, value);
                    break;
                }
                case Code.Arglist:
                {
                    // TODO: Implement this opcode
                    //var value = LLVM.BuildVAArg(builder, , , string.Empty);
                    var runtimeHandleType = GetType(corlib.MainModule.GetType(typeof(RuntimeArgumentHandle).FullName), TypeState.StackComplete);
                    stack.Add(new StackValue(runtimeHandleType.StackType, runtimeHandleType, LLVM.ConstNull(runtimeHandleType.DataTypeLLVM)));
                    break;
                }
                #endregion

                #region Load opcodes (Ldc, Ldstr, Ldloc, etc...)
                // Ldc_I4
                case Code.Ldc_I4_M1:
                case Code.Ldc_I4_0:
                case Code.Ldc_I4_1:
                case Code.Ldc_I4_2:
                case Code.Ldc_I4_3:
                case Code.Ldc_I4_4:
                case Code.Ldc_I4_5:
                case Code.Ldc_I4_6:
                case Code.Ldc_I4_7:
                case Code.Ldc_I4_8:
                {
                    var value = opcode - Code.Ldc_I4_0;
                    EmitI4(stack, value);
                    break;
                }
                case Code.Ldc_I4_S:
                {
                    var value = (sbyte)instruction.Operand;
                    EmitI4(stack, value);
                    break;
                }
                case Code.Ldc_I4:
                {
                    var value = (int)instruction.Operand;
                    EmitI4(stack, value);
                    break;
                }
                // Ldc_I8
                case Code.Ldc_I8:
                {
                    var value = (long)instruction.Operand;
                    EmitI8(stack, value);
                    break;
                }
                case Code.Ldc_R4:
                {
                    var value = (float)instruction.Operand;
                    EmitR4(stack, value);
                    break;
                }
                case Code.Ldc_R8:
                {
                    var value = (double)instruction.Operand;
                    EmitR8(stack, value);
                    break;
                }
                case Code.Ldstr:
                {
                    var operand = (string)instruction.Operand;

                    EmitLdstr(stack, operand);

                    break;
                }
                case Code.Ldnull:
                {
                    EmitLdnull(stack);
                    break;
                }

                // Ldloc
                case Code.Ldloc_0:
                case Code.Ldloc_1:
                case Code.Ldloc_2:
                case Code.Ldloc_3:
                {
                    var localIndex = opcode - Code.Ldloc_0;
                    EmitLdloc(stack, locals, localIndex);
                    break;
                }
                case Code.Ldloc:
                case Code.Ldloc_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitLdloc(stack, locals, localIndex);
                    break;
                }
                case Code.Ldloca:
                case Code.Ldloca_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitLdloca(stack, locals, localIndex);
                    break;
                }
                case Code.Ldfld:
                case Code.Ldflda:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve type and field
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType), TypeState.TypeComplete);
                    var field = type.Fields[fieldReference.Resolve()];

                    if (opcode == Code.Ldflda)
                    {
                        EmitLdflda(stack, field);
                    }
                    else
                    {
                        EmitLdfld(stack, field, functionContext.InstructionFlags);
                        functionContext.InstructionFlags = InstructionFlags.None;
                    }

                    break;
                }
                case Code.Ldsfld:
                case Code.Ldsflda:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.StaticFields[fieldReference.Resolve()];

                    EnsureClassInitialized(functionContext, @class);

                    if (opcode == Code.Ldsflda)
                    {
                        EmitLdsflda(stack, field);
                    }
                    else
                    {
                        EmitLdsfld(stack, field, functionContext.InstructionFlags);
                        functionContext.InstructionFlags = InstructionFlags.None;
                    }

                    break;
                }
                #endregion

                #region Indirect opcodes (Stind, Ldind, etc...)
                case Code.Stind_I:
                case Code.Stind_I1:
                case Code.Stind_I2:
                case Code.Stind_I4:
                case Code.Stind_I8:
                case Code.Stind_R4:
                case Code.Stind_R8:
                case Code.Stind_Ref:
                {
                    EmitStind(functionContext, stack, opcode);

                    break;
                }

                case Code.Ldind_I:
                case Code.Ldind_I1:
                case Code.Ldind_I2:
                case Code.Ldind_I4:
                case Code.Ldind_I8:
                case Code.Ldind_U1:
                case Code.Ldind_U2:
                case Code.Ldind_U4:
                case Code.Ldind_R4:
                case Code.Ldind_R8:
                case Code.Ldind_Ref:
                {
                    EmitLdind(functionContext, stack, opcode);

                    break;
                }

                    #endregion

                #region Store opcodes (Stloc, etc...)
                // Stloc
                case Code.Stloc_0:
                case Code.Stloc_1:
                case Code.Stloc_2:
                case Code.Stloc_3:
                {
                    var localIndex = opcode - Code.Stloc_0;
                    EmitStloc(stack, locals, localIndex);
                    break;
                }
                case Code.Stloc:
                case Code.Stloc_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitStloc(stack, locals, localIndex);
                    break;
                }
                case Code.Stfld:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve type and field
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType), TypeState.TypeComplete);
                    var field = type.Fields[fieldReference.Resolve()];

                    EmitStfld(stack, field, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }

                case Code.Stsfld:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.StaticFields[fieldReference.Resolve()];

                    EnsureClassInitialized(functionContext, @class);

                    EmitStsfld(stack, field, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                #endregion

                #region Branching (Brtrue, Brfalse, Switch, etc...)
                case Code.Br:
                case Code.Br_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBr(functionContext.BasicBlocks[targetInstruction.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Brfalse:
                case Code.Brfalse_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBrfalse(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                case Code.Brtrue:
                case Code.Brtrue_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBrtrue(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                case Code.Switch:
                {
                    var targets = (Instruction[])instruction.Operand;
                    var operand = stack.Pop();
                    var @switch = LLVM.BuildSwitch(builder, operand.Value, functionContext.BasicBlocks[instruction.Next.Offset], (uint)targets.Length);
                    for (int i = 0; i < targets.Length; ++i)
                    {
                        var target = targets[i];
                        LLVM.AddCase(@switch, LLVM.ConstInt(int32LLVM, (ulong)i, false), functionContext.BasicBlocks[target.Offset]);
                    }
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                #endregion

                #region Conditional branching (Beq, Bgt, etc...)
                case Code.Beq:
                case Code.Beq_S:
                case Code.Bge:
                case Code.Bge_S:
                case Code.Bgt:
                case Code.Bgt_S:
                case Code.Ble:
                case Code.Ble_S:
                case Code.Blt:
                case Code.Blt_S:
                case Code.Bne_Un:
                case Code.Bne_Un_S:
                case Code.Bge_Un:
                case Code.Bge_Un_S:
                case Code.Bgt_Un:
                case Code.Bgt_Un_S:
                case Code.Ble_Un:
                case Code.Ble_Un_S:
                case Code.Blt_Un:
                case Code.Blt_Un_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;

                    EmitConditionalBranch(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset], opcode);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;

                    break;
                }
                #endregion

                #region Comparison opcodes (Ceq, Cgt, etc...)
                case Code.Ceq:
                case Code.Cgt:
                case Code.Cgt_Un:
                case Code.Clt:
                case Code.Clt_Un:
                {
                    EmitComparison(stack, opcode);

                    break;
                }

                    #endregion

                #region Conversion opcodes (Conv_U, Conv_I, etc...)
                case Code.Conv_U:
                case Code.Conv_I:
                case Code.Conv_U1:
                case Code.Conv_I1:
                case Code.Conv_U2:
                case Code.Conv_I2:
                case Code.Conv_U4:
                case Code.Conv_I4:
                case Code.Conv_U8:
                case Code.Conv_I8:
                case Code.Conv_R4:
                case Code.Conv_R8:
                case Code.Conv_R_Un:
                case Code.Conv_Ovf_U:
                case Code.Conv_Ovf_I:
                case Code.Conv_Ovf_U1:
                case Code.Conv_Ovf_I1:
                case Code.Conv_Ovf_U2:
                case Code.Conv_Ovf_I2:
                case Code.Conv_Ovf_U4:
                case Code.Conv_Ovf_I4:
                case Code.Conv_Ovf_U8:
                case Code.Conv_Ovf_I8:
                case Code.Conv_Ovf_U_Un:
                case Code.Conv_Ovf_I_Un:
                case Code.Conv_Ovf_U1_Un:
                case Code.Conv_Ovf_I1_Un:
                case Code.Conv_Ovf_U2_Un:
                case Code.Conv_Ovf_I2_Un:
                case Code.Conv_Ovf_U4_Un:
                case Code.Conv_Ovf_I4_Un:
                case Code.Conv_Ovf_U8_Un:
                case Code.Conv_Ovf_I8_Un:
                {
                    EmitConv(stack, opcode);

                    break;
                }
                #endregion

                #region Unary operation opcodes (Neg, Not, etc...)
                case Code.Neg:
                case Code.Not:
                {
                    EmitUnaryOperation(stack, opcode);

                    break;
                }
                #endregion

                #region Binary operation opcodes (Add, Sub, etc...)
                case Code.Add:
                case Code.Add_Ovf:
                case Code.Add_Ovf_Un:
                case Code.Sub:
                case Code.Sub_Ovf:
                case Code.Sub_Ovf_Un:
                case Code.Mul:
                case Code.Mul_Ovf:
                case Code.Mul_Ovf_Un:
                case Code.Div:
                case Code.Div_Un:
                case Code.Rem:
                case Code.Rem_Un:
                case Code.Shl:
                case Code.Shr:
                case Code.Shr_Un:
                case Code.Xor:
                case Code.Or:
                case Code.And:
                {
                    EmitBinaryOperation(functionContext, stack, opcode);
                    break;
                }
                #endregion

                #region Exception handling opcodes (Leave, Endfinally, etc...)
                case Code.Throw:
                {
                    var exceptionObject = stack.Pop();

                    // Throw exception
                    // TODO: Update callstack
                    GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty) });
                    LLVM.BuildUnreachable(builder);

                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Rethrow:
                {
                    // Find exception that was on stack at beginning of this catch clause
                    var currentCatchClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset);

                    if (currentCatchClause == null || currentCatchClause.Source.HandlerType != ExceptionHandlerType.Catch)
                        throw new InvalidOperationException("Can't find catch clause matching this rethrow instruction.");

                    var catchClauseStack = functionContext.ForwardStacks[currentCatchClause.Source.HandlerStart.Offset];
                    var exceptionObject = catchClauseStack[0];

                    // Rethrow exception
                    GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty) });
                    LLVM.BuildUnreachable(builder);

                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Leave:
                case Code.Leave_S:
                {
                    // Evaluation stack is cleared
                    stack.Clear();

                    // Default target (if we jump inside the exception clause)
                    var targetInstruction = (Instruction)instruction.Operand;

                    GenerateLeave(functionContext.ActiveTryHandlers, targetInstruction, functionContext.EndfinallyJumpTarget, functionContext.BasicBlocks);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;

                    break;
                }
                case Code.Endfinally:
                {
                    var currentFinallyClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset);

                    if (currentFinallyClause == null)
                        throw new InvalidOperationException("Can't find exception clause matching this endfinally/endfault instruction.");

                    EmitEndfinally(functionContext, currentFinallyClause);

                    break;
                }
                #endregion

                #region Instruction flags (Unaligned, Volatile)
                case Code.Volatile:
                    functionContext.InstructionFlags |= InstructionFlags.Volatile;
                    break;
                case Code.Unaligned:
                    functionContext.InstructionFlags |= InstructionFlags.Unaligned;
                    break;
                #endregion

                default:
                    throw new NotImplementedException(string.Format("Opcode {0} not implemented.", instruction.OpCode));
            }
        }
コード例 #32
0
        private void UpdateExceptionHandlers(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var functionGlobal = functionContext.Function.GeneratedValue;
            var exceptionHandlers = functionContext.ExceptionHandlers;
            var activeTryHandlers = functionContext.ActiveTryHandlers;
            var methodReference = functionContext.MethodReference;
            bool exceptionHandlersChanged = false;

            // Exit finished exception handlers
            for (int index = activeTryHandlers.Count - 1; index >= 0; index--)
            {
                var exceptionHandler = activeTryHandlers[index];
                if (instruction == exceptionHandler.Source.TryEnd)
                {
                    activeTryHandlers.RemoveAt(index);
                    exceptionHandlersChanged = true;
                }
                else
                    break;
            }

            // Add new exception handlers
            for (int index = exceptionHandlers.Count - 1; index >= 0; index--)
            {
                var exceptionHandler = exceptionHandlers[index];
                if (instruction == exceptionHandler.Source.TryStart)
                {
                    var catchDispatchBlock = new BasicBlockRef();

                    if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Catch)
                    {
                        catchDispatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "catch.dispatch");
                        LLVM.PositionBuilderAtEnd(builder2, catchDispatchBlock);

                        var catchBlock = functionContext.BasicBlocks[exceptionHandler.Source.HandlerStart.Offset];
                        var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.Source.CatchType));

                        // Compare exception type
                        var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel");

                        var ehtypeIdFor = LLVM.IntrinsicGetDeclaration(module, (uint)Intrinsics.eh_typeid_for, new TypeRef[0]);
                        var ehtypeid = LLVM.BuildCall(builder2, ehtypeIdFor, new[] { LLVM.ConstBitCast(catchClass.GeneratedRuntimeTypeInfoGlobal, intPtrType) }, string.Empty);

                        // Jump to catch clause if type matches.
                        // Otherwise, go to next exception handler dispatch block (if any), or resume exception block (TODO)
                        var ehtypeComparisonResult = LLVM.BuildICmp(builder2, IntPredicate.IntEQ, ehselectorValue, ehtypeid, string.Empty);
                        LLVM.BuildCondBr(builder2, ehtypeComparisonResult, catchBlock, activeTryHandlers.Count > 0 ? activeTryHandlers.Last().CatchDispatch : functionContext.ResumeExceptionBlock);
                    }
                    else if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Finally)
                    {
                        catchDispatchBlock = functionContext.BasicBlocks[exceptionHandler.Source.HandlerStart.Offset];
                    }

                    exceptionHandler.CatchDispatch = catchDispatchBlock;
                    activeTryHandlers.Add(exceptionHandler);
                    exceptionHandlersChanged = true;
                }
            }

            if (exceptionHandlersChanged)
            {
                // Need to generate a new landing pad
                for (int index = activeTryHandlers.Count - 1; index >= 0; index--)
                {
                    var exceptionHandler = activeTryHandlers[index];
                    switch (exceptionHandler.Source.HandlerType)
                    {
                        case ExceptionHandlerType.Catch:
                            break;
                    }
                }

                if (activeTryHandlers.Count > 0)
                {
                    //var handlerStart = exceptionHandlers.Last().HandlerStart.Offset;
                    //functionContext.LandingPadBlock = basicBlocks[handlerStart];

                    // Prepare landing pad block
                    functionContext.LandingPadBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "landingpad");
                    LLVM.PositionBuilderAtEnd(builder2, functionContext.LandingPadBlock);
                    var landingPad = LLVM.BuildLandingPad(builder2, caughtResultType, sharpPersonalityFunction, 1, string.Empty);

                    // Extract exception, and store it in exn.slot
                    var exceptionObject = LLVM.BuildExtractValue(builder2, landingPad, 0, string.Empty);
                    exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, @object.Class.Type.DefaultType, string.Empty);
                    LLVM.BuildStore(builder2, exceptionObject, functionContext.ExceptionSlot);

                    // Extract selector slot, and store it in ehselector.slot
                    var exceptionType = LLVM.BuildExtractValue(builder2, landingPad, 1, string.Empty);
                    LLVM.BuildStore(builder2, exceptionType, functionContext.ExceptionHandlerSelectorSlot);

                    // Let future finally clause know that we need to propage exception after they are executed
                    // A future Leave instruction should clear that if necessary
                    LLVM.BuildStore(builder2, LLVM.ConstInt(int32Type, unchecked((ulong)-1), true), functionContext.EndfinallyJumpTarget);

                    // Add jump to catch dispatch block or finally block
                    var lastActiveTryHandler = activeTryHandlers.Last();
                    LLVM.BuildBr(builder2, lastActiveTryHandler.CatchDispatch);

                    // Filter exceptions type by type
                    for (int index = activeTryHandlers.Count - 1; index >= 0; index--)
                    {
                        var exceptionHandler = activeTryHandlers[index];

                        // Add landing pad type clause
                        if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Catch)
                        {
                            var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.Source.CatchType));
                            LLVM.AddClause(landingPad, LLVM.ConstBitCast(catchClass.GeneratedRuntimeTypeInfoGlobal, intPtrType));
                        }
                        else if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Finally)
                        {
                            LLVM.SetCleanup(landingPad, true);
                        }
                    }
                }
                else
                {
                    functionContext.LandingPadBlock = new BasicBlockRef();
                }
            }
        }
コード例 #33
0
        /// <summary>
        /// Compiles the given method definition.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="function">The function.</param>
        /// <exception cref="System.NotSupportedException"></exception>
        /// <exception cref="System.NotImplementedException"></exception>
        /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception>
        private void CompileFunction(MethodReference methodReference, Function function)
        {
            var method = methodReference.Resolve();

            var body = method.Body;
            var codeSize = body != null ? body.CodeSize : 0;
            var functionGlobal = function.GeneratedValue;

            var functionContext = new FunctionCompilerContext(function);
            functionContext.BasicBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
            LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock);

            if (body == null && (method.ImplAttributes & MethodImplAttributes.Runtime) != 0)
            {
                var declaringClass = GetClass(function.DeclaringType);

                // Generate IL for various methods
                if (declaringClass.BaseType != null &&
                    declaringClass.BaseType.Type.TypeReference.FullName == typeof(MulticastDelegate).FullName)
                {
                    body = GenerateDelegateMethod(method, declaringClass);
                    if (body == null)
                        return;

                    codeSize = UpdateOffsets(body);
                }
            }

            if (body == null)
                return;

            var numParams = method.Parameters.Count;

            // Create stack, locals and args
            var stack = new List<StackValue>(body.MaxStackSize);
            var locals = new List<StackValue>(body.Variables.Count);
            var args = new List<StackValue>(numParams);
            var exceptionHandlers = new List<ExceptionHandlerInfo>();
            var activeTryHandlers = new List<ExceptionHandlerInfo>();

            functionContext.Stack = stack;
            functionContext.Locals = locals;
            functionContext.Arguments = args;
            functionContext.ExceptionHandlers = exceptionHandlers;
            functionContext.ActiveTryHandlers = activeTryHandlers;

            // Process locals
            foreach (var local in body.Variables)
            {
                // TODO: Anything to do on pinned objects?
                //if (local.IsPinned)
                //    throw new NotSupportedException();

                var type = CreateType(ResolveGenericsVisitor.Process(methodReference, local.VariableType));
                locals.Add(new StackValue(type.StackType, type, LLVM.BuildAlloca(builder, type.DefaultType, local.Name)));
            }

            // Process args
            for (int index = 0; index < function.ParameterTypes.Length; index++)
            {
                var argType = function.ParameterTypes[index];
                var arg = LLVM.GetParam(functionGlobal, (uint)index);

                // Copy argument on stack
                var storage = LLVM.BuildAlloca(builder, argType.DefaultType, "arg" + index.ToString());
                LLVM.BuildStore(builder, arg, storage);

                args.Add(new StackValue(argType.StackType, argType, storage));
            }

            // Some wasted space due to unused offsets, but we only keep one so it should be fine.
            // TODO: Reuse same allocated instance per thread, and grow it only if necessary
            var branchTargets = new bool[codeSize];
            var basicBlocks = new BasicBlockRef[codeSize];
            var forwardStacks = new StackValue[codeSize][];

            functionContext.BasicBlocks = basicBlocks;
            functionContext.ForwardStacks = forwardStacks;

            // Find branch targets (which will require PHI node for stack merging)
            for (int index = 0; index < body.Instructions.Count; index++)
            {
                var instruction = body.Instructions[index];

                var flowControl = instruction.OpCode.FlowControl;

                // Process branch targets
                if (flowControl == FlowControl.Cond_Branch
                    || flowControl == FlowControl.Branch)
                {
                    var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand };

                    foreach (var target in targets)
                    {
                        // Operand Target can be reached
                        branchTargets[target.Offset] = true;
                    }
                }

                // Need to enforce a block to be created for the next instruction after a conditional branch
                // TODO: Break?
                if (flowControl == FlowControl.Cond_Branch)
                {
                    if (instruction.Next != null)
                        branchTargets[instruction.Next.Offset] = true;
                }
            }

            // Setup exception handling
            if (body.HasExceptionHandlers)
            {
                // Add an "ehselector.slot" i32 local, and a "exn.slot" Object reference local
                functionContext.ExceptionHandlerSelectorSlot = LLVM.BuildAlloca(builder, int32Type, "ehselector.slot");
                functionContext.ExceptionSlot = LLVM.BuildAlloca(builder, @object.DefaultType, "exn.slot");
                functionContext.EndfinallyJumpTarget = LLVM.BuildAlloca(builder, int32Type, "endfinally.jumptarget");

                // Create resume exception block
                functionContext.ResumeExceptionBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "eh.resume");
                LLVM.PositionBuilderAtEnd(builder2, functionContext.ResumeExceptionBlock);
                var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, "exn");
                var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel");

                exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, intPtrType, "exn");
                var landingPadValue = LLVM.BuildInsertValue(builder2, LLVM.GetUndef(caughtResultType), exceptionObject, 0, "lpad.val");
                landingPadValue = LLVM.BuildInsertValue(builder2, landingPadValue, ehselectorValue, 1, "lpad.val");

                LLVM.BuildResume(builder2, landingPadValue);

                // Exception handlers blocks are also branch targets
                foreach (var exceptionHandler in body.ExceptionHandlers)
                {
                    branchTargets[exceptionHandler.HandlerStart.Offset] = true;
                }
            }

            // Create basic block
            // TODO: Could be done during previous pass
            for (int offset = 0; offset < branchTargets.Length; offset++)
            {
                // Create a basic block if this was a branch target or an instruction after a conditional branch
                if (branchTargets[offset])
                {
                    basicBlocks[offset] = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}", offset));
                }
            }

            // Create catch clause stack
            if (body.HasExceptionHandlers)
            {
                // Exception handlers blocks are also branch targets
                foreach (var exceptionHandler in body.ExceptionHandlers)
                {
                    exceptionHandlers.Add(new ExceptionHandlerInfo(exceptionHandler));

                    if (exceptionHandler.HandlerType != ExceptionHandlerType.Catch)
                        continue;

                    var handlerStart = exceptionHandler.HandlerStart.Offset;

                    var catchBlock = basicBlocks[handlerStart];
                    var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.CatchType));

                    // Extract exception
                    LLVM.PositionBuilderAtEnd(builder2, catchBlock);
                    var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, string.Empty);
                    exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, catchClass.Type.DefaultType, string.Empty);

                    // Erase exception from exn.slot (it has been handled)
                    LLVM.BuildStore(builder2, LLVM.ConstNull(@object.DefaultType), functionContext.ExceptionSlot);
                    LLVM.BuildStore(builder2, LLVM.ConstInt(int32Type, 0, false), functionContext.ExceptionHandlerSelectorSlot);
                     
                    forwardStacks[handlerStart] = new[]
                    {
                        new StackValue(catchClass.Type.StackType, catchClass.Type, exceptionObject)
                    };
                }
            }

            foreach (var instruction in body.Instructions)
            {
                try
                {
                    // Check if any exception handlers might have changed
                    if (body.HasExceptionHandlers)
                        UpdateExceptionHandlers(functionContext, instruction);

                    if (branchTargets[instruction.Offset])
                        UpdateBranching(functionContext, instruction);

                    // Reset states
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Implicit;

                    EmitInstruction(functionContext, instruction);

                    // If we do a jump, let's merge stack
                    var flowControl = instruction.OpCode.FlowControl;
                    if (flowControl == FlowControl.Cond_Branch
                        || flowControl == FlowControl.Branch)
                        MergeStacks(functionContext, instruction);
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException(string.Format("Error while processing instruction {0} of method {1}", instruction, methodReference), e);
                }
            }
        }
コード例 #34
0
ファイル: Compiler.Emit.cs プロジェクト: frje/SharpLang
        private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod)
        {
            var stack = functionContext.Stack;

            // Build argument list
            var targetNumParams = targetMethod.ParameterTypes.Length;
            var args = new ValueRef[targetNumParams];
            for (int index = 0; index < targetNumParams; index++)
            {
                // TODO: Casting/implicit conversion?
                var stackItem = stack[stack.Count - targetNumParams + index];
                args[index] = ConvertFromStackToLocal(targetMethod.ParameterTypes[index], stackItem);
            }

            // Remove arguments from stack
            stack.RemoveRange(stack.Count - targetNumParams, targetNumParams);

            // Invoke method
            ValueRef callResult;
            var actualMethod = overrideMethod;

            callResult = GenerateInvoke(functionContext, actualMethod, args);

            // Mark method as needed (if non-virtual call)
            if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero)
            {
                LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage);
            }

            // Push return result on stack
            if (targetMethod.ReturnType.TypeReference.MetadataType != MetadataType.Void)
            {
                // Convert return value from local to stack value
                var returnValue = ConvertFromLocalToStack(targetMethod.ReturnType, callResult);

                // Add value to stack
                stack.Add(new StackValue(targetMethod.ReturnType.StackType, targetMethod.ReturnType, returnValue));
            }
        }
コード例 #35
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitBinaryOperation(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode)
        {
            var functionGlobal = functionContext.FunctionGlobal;

            var operand2 = stack.Pop();
            var operand1 = stack.Pop();

            var value1 = operand1.Value;
            var value2 = operand2.Value;

            StackValue outputOperandType;

            bool isShiftOperation = false;
            bool isIntegerOperation = false;

            // Detect shift and integer operations
            switch (opcode)
            {
                case Code.Shl:
                case Code.Shr:
                case Code.Shr_Un:
                    isShiftOperation = true;
                    break;
                case Code.Xor:
                case Code.Or:
                case Code.And:
                case Code.Div_Un:
                case Code.Not:
                    isIntegerOperation = true;
                    break;
            }

            if (isShiftOperation) // Shift operations are specials
            {
                switch (operand2.StackType)
                {
                    case StackValueType.Int32:
                        break;
                    case StackValueType.NativeInt:
                        value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty);
                        break;
                    default:
                        goto InvalidBinaryOperation;
                }

                // Check first operand, and convert second operand to match first one
                switch (operand1.StackType)
                {
                    case StackValueType.Int32:
                        value2 = LLVM.BuildIntCast(builder, value2, int32LLVM, string.Empty);
                        break;
                    case StackValueType.Int64:
                        value2 = LLVM.BuildIntCast(builder, value2, int64LLVM, string.Empty);
                        break;
                    case StackValueType.NativeInt:
                        value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty);
                        value2 = LLVM.BuildIntCast(builder, value2, nativeIntLLVM, string.Empty);
                        break;
                    default:
                        goto InvalidBinaryOperation;
                }

                // Output type is determined by first operand
                outputOperandType = operand1;
            }
            else if (operand1.StackType == operand2.StackType) // Diagonal
            {
                // Check type
                switch (operand1.StackType)
                {
                    case StackValueType.Int32:
                    case StackValueType.Int64:
                    case StackValueType.Float:
                        outputOperandType = operand1;
                        break;
                    case StackValueType.NativeInt:
                        value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty);
                        value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty);
                        outputOperandType = operand1;
                        break;
                    case StackValueType.Reference:
                        if (opcode != Code.Sub && opcode != Code.Sub_Ovf_Un)
                            goto InvalidBinaryOperation;
                        value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty);
                        value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty);
                        outputOperandType = new StackValue(StackValueType.NativeInt, intPtr, ValueRef.Empty);
                        break;
                    default:
                        throw new InvalidOperationException(string.Format("Binary operations are not allowed on {0}.", operand1.StackType));
                }
            }
            else if (operand1.StackType == StackValueType.NativeInt && operand2.StackType == StackValueType.Int32)
            {
                value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty);
                value2 = LLVM.BuildIntCast(builder, value2, nativeIntLLVM, string.Empty);
                outputOperandType = operand1;
            }
            else if (operand1.StackType == StackValueType.Int32 && operand2.StackType == StackValueType.NativeInt)
            {
                value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty);
                value1 = LLVM.BuildIntCast(builder, value1, nativeIntLLVM, string.Empty);
                outputOperandType = operand2;
            }
            else if (!isIntegerOperation
                     && (operand1.StackType == StackValueType.Reference || operand2.StackType == StackValueType.Reference)) // ref + [i32, nativeint] or [i32, nativeint] + ref
            {
                StackValue operandRef, operandInt;
                ValueRef valueRef, valueInt;

                if (operand2.StackType == StackValueType.Reference)
                {
                    operandRef = operand2;
                    operandInt = operand1;
                    valueRef = value2;
                    valueInt = value1;
                }
                else
                {
                    operandRef = operand1;
                    operandInt = operand2;
                    valueRef = value1;
                    valueInt = value2;
                }

                switch (operandInt.StackType)
                {
                    case StackValueType.Int32:
                        break;
                    case StackValueType.NativeInt:
                        valueInt = LLVM.BuildPtrToInt(builder, valueInt, nativeIntLLVM, string.Empty);
                        break;
                    default:
                        goto InvalidBinaryOperation;
                }

                switch (opcode)
                {
                    case Code.Add:
                    case Code.Add_Ovf_Un:
                        break;
                    case Code.Sub:
                    case Code.Sub_Ovf:
                        if (operand2.StackType == StackValueType.Reference)
                            goto InvalidBinaryOperation;

                        valueInt = LLVM.BuildNeg(builder, valueInt, string.Empty);
                        break;
                    default:
                        goto InvalidBinaryOperation;
                }

                // If necessary, cast to i8*
                var valueRefType = LLVM.TypeOf(valueRef);
                if (valueRefType != intPtrLLVM)
                    valueRef = LLVM.BuildPointerCast(builder, valueRef, intPtrLLVM, string.Empty);

                valueRef = LLVM.BuildGEP(builder, valueRef, new[] {valueInt}, string.Empty);

                // Cast back to original type
                if (valueRefType != intPtrLLVM)
                    valueRef = LLVM.BuildPointerCast(builder, valueRef, valueRefType, string.Empty);

                stack.Add(new StackValue(StackValueType.Reference, operandRef.Type, valueRef));

                // Early exit
                return;
            }
            else
            {
                goto InvalidBinaryOperation;
            }

            ValueRef result;

            // Perform binary operation
            if (operand1.StackType == StackValueType.Float)
            {
                switch (opcode)
                {
                    case Code.Add: result = LLVM.BuildFAdd(builder, value1, value2, string.Empty); break;
                    case Code.Sub: result = LLVM.BuildFSub(builder, value1, value2, string.Empty); break;
                    case Code.Mul: result = LLVM.BuildFMul(builder, value1, value2, string.Empty); break;
                    case Code.Div: result = LLVM.BuildFDiv(builder, value1, value2, string.Empty); break;
                    case Code.Rem: result = LLVM.BuildFRem(builder, value1, value2, string.Empty); break;
                    default:
                        goto InvalidBinaryOperation;
                }
            }
            else
            {
                // Special case: char is size 1, not 2!
                if (CharUsesUTF8)
                {
                    if (opcode == Code.Add && operand1.Type.TypeReferenceCecil.FullName == typeof(char*).FullName)
                    {
                        value2 = LLVM.BuildLShr(builder, value2, LLVM.ConstInt(int32LLVM, 1, false), string.Empty);
                    }
                    else if (opcode == Code.Add && operand2.Type.TypeReferenceCecil.FullName == typeof(char*).FullName)
                    {
                        value1 = LLVM.BuildLShr(builder, value1, LLVM.ConstInt(int32LLVM, 1, false), string.Empty);
                    }
                }

                switch (opcode)
                {
                    case Code.Add:          result = LLVM.BuildAdd(builder, value1, value2, string.Empty); break;
                    case Code.Sub:          result = LLVM.BuildSub(builder, value1, value2, string.Empty); break;
                    case Code.Mul:          result = LLVM.BuildMul(builder, value1, value2, string.Empty); break;
                    case Code.Div:          result = LLVM.BuildSDiv(builder, value1, value2, string.Empty); break;
                    case Code.Div_Un:       result = LLVM.BuildUDiv(builder, value1, value2, string.Empty); break;
                    case Code.Rem:          result = LLVM.BuildSRem(builder, value1, value2, string.Empty); break;
                    case Code.Rem_Un:       result = LLVM.BuildURem(builder, value1, value2, string.Empty); break;
                    case Code.Shl:          result = LLVM.BuildShl(builder, value1, value2, string.Empty); break;
                    case Code.Shr:          result = LLVM.BuildAShr(builder, value1, value2, string.Empty); break;
                    case Code.Shr_Un:       result = LLVM.BuildLShr(builder, value1, value2, string.Empty); break;
                    case Code.And:          result = LLVM.BuildAnd(builder, value1, value2, string.Empty); break;
                    case Code.Or:           result = LLVM.BuildOr(builder, value1, value2, string.Empty); break;
                    case Code.Xor:          result = LLVM.BuildXor(builder, value1, value2, string.Empty); break;
                    case Code.Add_Ovf:
                    case Code.Add_Ovf_Un:
                    case Code.Sub_Ovf:
                    case Code.Sub_Ovf_Un:
                    case Code.Mul_Ovf:
                    case Code.Mul_Ovf_Un:
                    {
                        Intrinsics intrinsicId;
                        switch (opcode)
                        {
                            case Code.Add_Ovf:
                                intrinsicId = Intrinsics.sadd_with_overflow;
                                break;
                            case Code.Add_Ovf_Un:
                                intrinsicId = Intrinsics.uadd_with_overflow;
                                break;
                            case Code.Sub_Ovf:
                                intrinsicId = Intrinsics.ssub_with_overflow;
                                break;
                            case Code.Sub_Ovf_Un:
                                intrinsicId = Intrinsics.usub_with_overflow;
                                break;
                            case Code.Mul_Ovf:
                                intrinsicId = Intrinsics.smul_with_overflow;
                                break;
                            case Code.Mul_Ovf_Un:
                                intrinsicId = Intrinsics.umul_with_overflow;
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                        var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)intrinsicId, new[] {LLVM.TypeOf(value1)});
                        var overflowResult = LLVM.BuildCall(builder, intrinsic, new[] {value1, value2}, string.Empty);
                        var hasOverflow = LLVM.BuildExtractValue(builder, overflowResult, 1, string.Empty);

                        var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
                        var overflowBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "overflow");
                        LLVM.MoveBasicBlockAfter(overflowBlock, LLVM.GetInsertBlock(builder));
                        LLVM.MoveBasicBlockAfter(nextBlock, overflowBlock);

                        LLVM.BuildCondBr(builder, hasOverflow, overflowBlock, nextBlock);

                        LLVM.PositionBuilderAtEnd(builder, overflowBlock);

                        // Create OverflowException object
                        var overflowExceptionClass = GetClass(corlib.MainModule.GetType(typeof(OverflowException).FullName));
                        EmitNewobj(functionContext, overflowExceptionClass.Type, overflowExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0));
                        var overflowException = stack.Pop();
                        GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, overflowException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)});
                        LLVM.BuildUnreachable(builder);

                        functionContext.BasicBlock = nextBlock;
                        LLVM.PositionBuilderAtEnd(builder, nextBlock);
                        result = LLVM.BuildExtractValue(builder, overflowResult, 0, string.Empty);

                        break;
                    }
                    default:
                        goto InvalidBinaryOperation;
                }

                if (CharUsesUTF8)
                {
                    if (opcode == Code.Sub
                        && operand1.Type.TypeReferenceCecil.FullName == typeof(char*).FullName
                        && operand2.Type.TypeReferenceCecil.FullName == typeof(char*).FullName)
                    {
                        result = LLVM.BuildLShr(builder, result, LLVM.ConstInt(int32LLVM, 1, false), string.Empty);
                    }
                }
            }

            Type outputType;

            switch (outputOperandType.StackType)
            {
                case StackValueType.Int32:
                case StackValueType.Int64:
                case StackValueType.Float:
                    // No output conversion required, as it could only have been from same input types (non-shift) or operand 1 (shift)
                    outputType = operand1.Type;
                    break;
                case StackValueType.NativeInt:
                    outputType = intPtr;
                    result = LLVM.BuildIntToPtr(builder, result, intPtrLLVM, string.Empty);
                    break;
                case StackValueType.Reference:
                    result = LLVM.BuildIntToPtr(builder, result, intPtrLLVM, string.Empty);

                    // Get type from one of its operand (if output is reference type, one of the two operand must be too)
                    if (operand1.StackType == StackValueType.Reference)
                        outputType = operand1.Type;
                    else if (operand2.StackType == StackValueType.Reference)
                        outputType = operand2.Type;
                    else
                        goto InvalidBinaryOperation;
                    break;
                default:
                    goto InvalidBinaryOperation;
            }

            stack.Add(new StackValue(outputOperandType.StackType, outputOperandType.Type, result));

            return;

            InvalidBinaryOperation:
            throw new InvalidOperationException(string.Format("Binary operation {0} between {1} and {2} is not supported.", opcode, operand1.StackType, operand2.StackType));
        }
コード例 #36
0
        /// <summary>
        /// Merges all the stacks of this instruction targets.
        /// </summary>
        /// <param name="functionContext">The function context.</param>
        /// <param name="instruction">The instruction.</param>
        /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception>
        private void MergeStacks(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var forwardStacks = functionContext.ForwardStacks;
            var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand };

            foreach (var target in targets)
            {
                // Backward jump? Make sure stack was properly created by a previous forward jump, or empty
                if (target.Offset < instruction.Offset)
                {
                    var forwardStack = forwardStacks[target.Offset];
                    if (forwardStack != null && forwardStack.Length > 0)
                        throw new InvalidOperationException("Backward jump with a non-empty stack unknown target.");
                }

                // Merge stack (add PHI incoming)
                MergeStack(functionContext.Stack, functionContext.BasicBlock, ref forwardStacks[target.Offset], functionContext.BasicBlocks[target.Offset]);
            }
        }
コード例 #37
0
        private ValueRef ResolveVirtualMethod(FunctionCompilerContext functionContext, ref Function targetMethod, ref StackValue thisObject)
        {
            ValueRef resolvedMethod;
            if ((targetMethod.MethodReference.Resolve().Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual)
            {
                // Build indices for GEP
                var indices = new[]
                {
                    LLVM.ConstInt(int32Type, 0, false), // Pointer indirection
                    LLVM.ConstInt(int32Type, (int) ObjectFields.RuntimeTypeInfo, false), // Access RTTI
                };

                Class @class;

                var constrainedClass = functionContext.ConstrainedClass;
                if (constrainedClass != null)
                {
                    // Reset state
                    functionContext.ConstrainedClass = null;

                    if (!constrainedClass.Type.TypeReference.IsValueType)
                    {
                        // If thisType is a reference type, dereference
                        thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type,
                            LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty),
                                constrainedClass.Type.DefaultType, string.Empty));
                    }
                    else
                    {
                        var matchingMethod = CecilExtensions.TryMatchMethod(constrainedClass, targetMethod.MethodReference,
                            false);
                        if (matchingMethod != null)
                        {
                            // If thisType is a value type and implements method, then ptr is passed unmodified
                            targetMethod = matchingMethod;

                            // Convert to appropriate type (if necessary)
                            var refType = GetType(constrainedClass.Type.TypeReference.MakeByReferenceType());
                            if (thisObject.StackType != StackValueType.Reference || thisObject.Type != refType)
                            {
                                thisObject = new StackValue(refType.StackType, refType,
                                    LLVM.BuildPointerCast(builder, thisObject.Value, refType.DefaultType, string.Empty));
                            }
                        }
                        else
                        {
                            // If thisType is a value type and doesn't implement method, dereference, box and pass as this
                            thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type,
                                LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty),
                                    constrainedClass.Type.DefaultType, string.Empty));

                            thisObject = new StackValue(StackValueType.Object, constrainedClass.Type,
                                BoxValueType(constrainedClass, thisObject));
                        }
                    }

                    @class = constrainedClass;
                }
                else
                {
                    @class = GetClass(thisObject.Type);
                }

                // TODO: Checking actual type stored in thisObject we might be able to statically resolve method?

                // If it's a byref value type, emit a normal call
                if (thisObject.Type.TypeReference.IsByReference
                    && thisObject.Type.TypeReference.GetElementType().IsValueType
                    && MemberEqualityComparer.Default.Equals(targetMethod.DeclaringType.TypeReference, thisObject.Type.TypeReference.GetElementType()))
                {
                    resolvedMethod = targetMethod.GeneratedValue;
                }
                else
                {
                    // Get RTTI pointer
                    var rttiPointer = LLVM.BuildInBoundsGEP(builder, thisObject.Value, indices, string.Empty);
                    rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty);

                    // Cast to expected RTTI type
                    rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(@class.GeneratedRuntimeTypeInfoGlobal), string.Empty);

                    if (targetMethod.MethodReference.DeclaringType.Resolve().IsInterface)
                    {
                        // Interface call

                        // Get method stored in IMT slot
                        indices = new[]
                        {
                            LLVM.ConstInt(int32Type, 0, false), // Pointer indirection
                            LLVM.ConstInt(int32Type, (int) RuntimeTypeInfoFields.InterfaceMethodTable, false), // Access IMT
                            LLVM.ConstInt(int32Type, (ulong) targetMethod.VirtualSlot, false), // Access specific IMT slot
                        };

                        var imtEntry = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);

                        var methodPointer = LLVM.BuildLoad(builder, imtEntry, string.Empty);

                        // TODO: Compare method ID and iterate in the linked list until the correct match is found
                        // If no match is found, it's likely due to covariance/contravariance, so we will need a fallback
                        var methodId = GetMethodId(targetMethod.MethodReference);

                        // Resolve interface call
                        resolvedMethod = LLVM.BuildCall(builder, resolveInterfaceCallFunction, new[]
                        {
                            LLVM.ConstInt(int32Type, methodId, false),
                            methodPointer,
                        }, string.Empty);
                        resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod,
                            LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty);
                    }
                    else
                    {
                        // Virtual table call

                        // Get method stored in vtable slot
                        indices = new[]
                        {
                            LLVM.ConstInt(int32Type, 0, false), // Pointer indirection
                            LLVM.ConstInt(int32Type, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable
                            LLVM.ConstInt(int32Type, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot
                        };

                        var vtable = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                        resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty);
                        resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty);
                    }
                }
            }
            else
            {
                // Normal call
                // Callvirt on non-virtual function is only done to force "this" NULL check
                // However, that's probably a part of the .NET spec that we want to skip for performance reasons,
                // so maybe we should keep this as is?
                resolvedMethod = targetMethod.GeneratedValue;
            }
            return resolvedMethod;
        }
コード例 #38
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitStind(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode)
        {
            var value = stack.Pop();
            var address = stack.Pop();

            // Determine type
            Type type;
            switch (opcode)
            {
                case Code.Stind_I: type = intPtr; break;
                case Code.Stind_I1: type = int8; break;
                case Code.Stind_I2: type = int16; break;
                case Code.Stind_I4: type = int32; break;
                case Code.Stind_I8: type = int64; break;
                case Code.Stind_R4: type = @float; break;
                case Code.Stind_R8: type = @double; break;
                case Code.Stind_Ref:
                    type = value.Type;
                    break;
                default:
                    throw new ArgumentException("opcode");
            }

            if (CharUsesUTF8)
            {
                if (opcode == Code.Stind_I2 && address.Type.TypeReferenceCecil.FullName == typeof(char*).FullName)
                {
                    type = int8;
                }
            }

            // Convert to local type
            var sourceValue = ConvertFromStackToLocal(type, value);

            // Store value at address
            var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(LLVM.TypeOf(sourceValue), 0), string.Empty);
            StoreValue(type.StackType, sourceValue, pointerCast, functionContext.InstructionFlags);
            functionContext.InstructionFlags = InstructionFlags.None;
        }
コード例 #39
0
        /// <summary>
        /// Update branching before emitting instruction.
        /// </summary>
        /// <param name="functionContext"></param>
        /// <param name="instruction"></param>
        private void UpdateBranching(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var previousBasicBlock = functionContext.BasicBlock;
            var stack = functionContext.Stack;
            var basicBlocks = functionContext.BasicBlocks;
            var forwardStacks = functionContext.ForwardStacks;

            functionContext.BasicBlock = basicBlocks[instruction.Offset];

            var forwardStack = forwardStacks[instruction.Offset];

            if (functionContext.FlowingNextInstructionMode == FlowingNextInstructionMode.Implicit)
            {
                // Add a jump from previous block to new block
                LLVM.BuildBr(builder, functionContext.BasicBlock);
            }

            if (functionContext.FlowingNextInstructionMode != FlowingNextInstructionMode.None)
            {
                // If flowing either automatically or explicitely,
                // flow stack and build PHI nodes
                MergeStack(stack, previousBasicBlock, ref forwardStack, functionContext.BasicBlock);
                forwardStacks[instruction.Offset] = forwardStack;
            }

            // Clear stack
            stack.Clear();

            // Try to restore stack from previously reached forward jump
            if (forwardStack != null)
            {
                // Restoring stack as it was during one of previous forward jump
                stack.AddRange(forwardStack);
            }
            else
            {
                // TODO: Actually, need to restore stack from one of previous forward jump instruction, if any
                // (if only backward jumps, spec says it should be empty to avoid multi-pass IL scanning,
                // but that's something we could also support later -- Mono doesn't but MS.NET does)
            }

            // Position builder to write at beginning of new block
            LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock);
        }
コード例 #40
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor)
        {
            var stack = functionContext.Stack;

            // Make sure .cctor has been called
            EnsureClassInitialized(functionContext, GetClass(type));

            // String is a special case: we redirect to matching CreateString function,
            // and InternalAllocateStr will do the actual allocation
            if (type.TypeReferenceCecil.FullName == typeof(string).FullName)
            {
                var targetMethod = type.Class.Functions.First(x => x.MethodReference.Name == "CreateString" && CompareParameterTypes(x.ParameterTypes, ctor.ParameterTypes));

                // Insert a null "this" pointer (note: CreateString would probably be better as static to avoid that)
                functionContext.Stack.Insert(functionContext.Stack.Count - (targetMethod.ParameterTypes.Length - 1), new StackValue(type.StackType, type, LLVM.ConstNull(type.DefaultTypeLLVM)));

                EmitCall(functionContext, targetMethod.Signature, targetMethod.GeneratedValue);
                return;
            }

            var allocatedObject = AllocateObject(type);

            // Add it to stack, right before arguments
            var ctorNumParams = ctor.ParameterTypes.Length;
            stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject));

            // Invoke ctor
            EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue);

            if (type.StackType != StackValueType.Object && type.StackType != StackValueType.Value)
            {
                allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty);
            }

            // Add created object on the stack
            stack.Add(new StackValue(type.StackType, type, allocatedObject));
        }
コード例 #41
0
        private void EmitInstruction(FunctionCompilerContext functionContext, Instruction instruction)
        {
            var methodReference = functionContext.Function.MethodReference;
            var stack = functionContext.Stack;
            var args = functionContext.Arguments;
            var locals = functionContext.Locals;
            var functionGlobal = functionContext.Function.GeneratedValue;
            var exceptionHandlers = functionContext.ExceptionHandlers;

            var opcode = instruction.OpCode.Code;

            switch (opcode)
            {
                case Code.Nop:
                {
                    // TODO: Insert nop? Debugger step?

                    // Check if there is a custom action
                    Action<List<StackValue>> instructionAction;
                    if (InstructionActions.TryGetValue(instruction, out instructionAction))
                    {
                        instructionAction(stack);
                    }
                    break;
                }
                case Code.Pop:
                {
                    // Pop and discard last stack value
                    stack.Pop();
                    break;
                }
                case Code.Dup:
                {
                    // Readd last stack value
                    var lastStackValue = stack[stack.Count - 1];
                    stack.Add(new StackValue(lastStackValue.StackType, lastStackValue.Type, lastStackValue.Value));
                    break;
                }
                case Code.Ret:
                {
                    EmitRet(stack, methodReference);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Call:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    EmitCall(functionContext, new FunctionSignature(targetMethod.ReturnType, targetMethod.ParameterTypes), targetMethod.GeneratedValue);

                    break;
                }
                case Code.Calli:
                {
                    var callSite = (CallSite)instruction.Operand;

                    // TODO: Unify with CreateFunction code
                    var returnType = GetType(ResolveGenericsVisitor.Process(methodReference, callSite.ReturnType)).DefaultType;
                    var parameterTypesLLVM = callSite.Parameters.Select(x => GetType(ResolveGenericsVisitor.Process(methodReference, x.ParameterType)).DefaultType).ToArray();

                    // Generate function type
                    var functionType = LLVM.FunctionType(returnType, parameterTypesLLVM, false);

                    var methodPtr = stack[stack.Count - parameterTypesLLVM.Length - 1];

                    var castedMethodPtr = LLVM.BuildPointerCast(builder, methodPtr.Value, LLVM.PointerType(functionType, 0), string.Empty);

                    var signature = CreateFunctionSignature(methodReference, callSite);

                    EmitCall(functionContext, signature, castedMethodPtr);

                    break;
                }
                case Code.Callvirt:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    var thisObject = stack[stack.Count - targetMethod.ParameterTypes.Length];

                    var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject);

                    stack[stack.Count - targetMethod.ParameterTypes.Length] = thisObject;

                    // Emit call
                    EmitCall(functionContext, targetMethod.Signature, resolvedMethod);

                    break;
                }
                case Code.Constrained:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    functionContext.ConstrainedClass = GetClass(typeReference);

                    break;
                }

                #region Obj opcodes (Initobj, Newobj, Stobj, Ldobj, etc...)
                case Code.Initobj:
                {
                    var address = stack.Pop();
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var type = GetType(typeReference);
                    EmitInitobj(address, type);
                    break;
                }

                case Code.Newobj:
                {
                    var ctorReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var ctor = GetFunction(ctorReference);
                    var type = GetType(ctorReference.DeclaringType);

                    EmitNewobj(functionContext, type, ctor);

                    break;
                }

                case Code.Stobj:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand));

                    EmitStobj(stack, type, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                case Code.Ldobj:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand));

                    EmitLdobj(stack, type, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                #endregion

                case Code.Localloc:
                {
                    var numElements = stack.Pop();

                    ValueRef numElementsCasted;
                    if (numElements.StackType == StackValueType.NativeInt)
                    {
                        numElementsCasted = LLVM.BuildPtrToInt(builder, numElements.Value, int32Type, string.Empty);
                    }
                    else
                    {
                        numElementsCasted = LLVM.BuildIntCast(builder, numElements.Value, int32Type, string.Empty);
                    }


                    var alloca = LLVM.BuildArrayAlloca(builder, LLVM.Int8TypeInContext(context), numElementsCasted, string.Empty);
                    alloca = LLVM.BuildPointerCast(builder, alloca, intPtr.DataType, string.Empty);

                    stack.Add(new StackValue(StackValueType.NativeInt, intPtr, alloca));
                    break;
                }

                case Code.Castclass:
                case Code.Isinst:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var @class = GetClass(typeReference);

                    var obj = stack.Pop();

                    // Get RTTI pointer
                    var indices = new[]
                    {
                        LLVM.ConstInt(int32Type, 0, false),                                 // Pointer indirection
                        LLVM.ConstInt(int32Type, (int)ObjectFields.RuntimeTypeInfo, false), // Access RTTI
                    };

                    var rttiPointer = LLVM.BuildInBoundsGEP(builder, obj.Value, indices, string.Empty);
                    rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty);

                    // castedPointerObject is valid only from typeCheckBlock
                    var castedPointerType = LLVM.PointerType(@class.Type.ObjectType, 0);
                    ValueRef castedPointerObject;

                    // Prepare basic blocks (for PHI instruction)
                    var typeNotMatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
                    var typeCheckDoneBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
                    BasicBlockRef typeCheckBlock;

                    if (@class.Type.TypeReference.Resolve().IsInterface)
                    {
                        // Cast as appropriate pointer type (for next PHI incoming if success)
                        castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty);

                        var inlineRuntimeTypeInfoType = LLVM.TypeOf(LLVM.GetParam(isInstInterfaceFunction, 0));
                        var isInstInterfaceResult = LLVM.BuildCall(builder, isInstInterfaceFunction, new[]
                        {
                            LLVM.BuildPointerCast(builder, rttiPointer, inlineRuntimeTypeInfoType, string.Empty),
                            LLVM.BuildPointerCast(builder, @class.GeneratedRuntimeTypeInfoGlobal, inlineRuntimeTypeInfoType, string.Empty),
                        }, string.Empty);

                        LLVM.BuildCondBr(builder, isInstInterfaceResult, typeCheckDoneBlock, typeNotMatchBlock);

                        typeCheckBlock = LLVM.GetInsertBlock(builder);
                    }
                    else
                    {
                        // TODO: Probably better to rewrite this in C, but need to make sure depth will be inlined as constant
                        // Get super type count
                        // Get method stored in IMT slot
                        indices = new[]
                        {
                            LLVM.ConstInt(int32Type, 0, false),                                                 // Pointer indirection
                            LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.SuperTypeCount, false),         // Super type count
                        };

                        typeCheckBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);

                        var superTypeCount = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                        superTypeCount = LLVM.BuildLoad(builder, superTypeCount, string.Empty);
                        
                        var depthCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntSGE, superTypeCount, LLVM.ConstInt(int32Type, (ulong)@class.Depth, false), string.Empty);
                        LLVM.BuildCondBr(builder, depthCompareResult, typeCheckBlock, typeNotMatchBlock);

                        // Start new typeCheckBlock
                        LLVM.PositionBuilderAtEnd(builder, typeCheckBlock);

                        // Get super types
                        indices = new[]
                        {
                            LLVM.ConstInt(int32Type, 0, false),                                                 // Pointer indirection
                            LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.SuperTypes, false),             // Super types
                        };

                        var superTypes = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty);
                        superTypes = LLVM.BuildLoad(builder, superTypes, string.Empty);

                        // Get actual super type
                        indices = new[]
                        {
                            LLVM.ConstInt(int32Type, (ulong)@class.Depth, false),                                 // Pointer indirection
                        };
                        var superType = LLVM.BuildGEP(builder, superTypes, indices, string.Empty);
                        superType = LLVM.BuildLoad(builder, superType, string.Empty);

                        // Cast as appropriate pointer type (for next PHI incoming if success)
                        castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty);

                        // Compare super type in array at given depth with expected one
                        var typeCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntEQ, superType, LLVM.ConstPointerCast(@class.GeneratedRuntimeTypeInfoGlobal, intPtrType), string.Empty);
                        LLVM.BuildCondBr(builder, typeCompareResult, typeCheckDoneBlock, typeNotMatchBlock);
                    }

                    // Start new typeNotMatchBlock: set object to null and jump to typeCheckDoneBlock
                    LLVM.PositionBuilderAtEnd(builder, typeNotMatchBlock);
                    if (opcode == Code.Castclass)
                    {
                        // Create InvalidCastException object
                        var invalidCastExceptionClass = GetClass(corlib.MainModule.GetType(typeof(InvalidCastException).FullName));
                        EmitNewobj(functionContext, invalidCastExceptionClass.Type, invalidCastExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0));
                        var invalidCastException = stack.Pop();
                        GenerateInvoke(functionContext, throwExceptionFunction, new[] { LLVM.BuildPointerCast(builder, invalidCastException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) });
                        LLVM.BuildUnreachable(builder);
                    }
                    else
                    {
                        LLVM.BuildBr(builder, typeCheckDoneBlock);
                    }

                    // Start new typeCheckDoneBlock
                    LLVM.PositionBuilderAtEnd(builder, typeCheckDoneBlock);

                    // Put back with appropriate type at end of stack
                    ValueRef mergedVariable;
                    if (opcode == Code.Castclass)
                    {
                        mergedVariable = castedPointerObject;
                    }
                    else
                    {
                        mergedVariable = LLVM.BuildPhi(builder, castedPointerType, string.Empty);
                        LLVM.AddIncoming(mergedVariable,
                            new[] {castedPointerObject, LLVM.ConstPointerNull(castedPointerType)},
                            new[] {typeCheckBlock, typeNotMatchBlock});
                    }
                    stack.Add(new StackValue(obj.StackType, @class.Type, mergedVariable));

                    break;
                }

                case Code.Ldtoken:
                {
                    var token = instruction.Operand;

                    Class runtimeHandleClass;
                    if (token is TypeReference)
                    {
                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeTypeHandle).FullName));
                    }
                    else if (token is FieldReference)
                    {
                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeFieldHandle).FullName));
                    }
                    else if (token is MethodReference)
                    {
                        runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeMethodHandle).FullName));
                    }
                    else
                    {
                        throw new NotSupportedException("Invalid ldtoken operand.");
                    }

                    // TODO: Actually transform type to RTTI token.
                    stack.Add(new StackValue(StackValueType.Value, runtimeHandleClass.Type, LLVM.ConstNull(runtimeHandleClass.Type.DataType)));

                    break;
                }

                case Code.Ldftn:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, targetMethod.GeneratedValue, intPtrType, string.Empty)));

                    break;
                }
                case Code.Ldvirtftn:
                {
                    var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand);
                    var targetMethod = GetFunction(targetMethodReference);

                    var thisObject = stack.Pop();

                    var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject);

                    stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, resolvedMethod, intPtrType, string.Empty)));

                    break;
                }

                #region Box/Unbox opcodes
                case Code.Box:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var @class = GetClass(typeReference);

                    // Only value types need to be boxed
                    if (@class.Type.TypeReference.IsValueType)
                    {
                        var valueType = stack.Pop();

                        var allocatedObject = BoxValueType(@class, valueType);

                        // Add created object on the stack
                        stack.Add(new StackValue(StackValueType.Object, @class.Type, allocatedObject));
                    }

                    break;
                }

                case Code.Unbox_Any:
                {
                    var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand);
                    var @class = GetClass(typeReference);

                    if (typeReference.IsValueType)
                    {
                        var obj = stack.Pop();
                        
                        // TODO: check type?
                        var objCast = LLVM.BuildPointerCast(builder, obj.Value, LLVM.PointerType(@class.Type.ObjectType, 0), string.Empty);

                        var dataPointer = GetDataPointer(objCast);

                        var data = LLVM.BuildLoad(builder, dataPointer, string.Empty);

                        stack.Add(new StackValue(@class.Type.StackType, @class.Type, data));
                    }
                    else
                    {
                        // Should act as "castclass" on reference types
                        goto case Code.Castclass;
                    }

                    break;
                }
                #endregion

                #region Array opcodes (Newarr, Ldlen, Stelem_Ref, etc...)
                case Code.Newarr:
                {
                    var elementType = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand));

                    EmitNewarr(stack, elementType);

                    break;
                }
                case Code.Ldlen:
                {
                    EmitLdlen(stack);

                    break;
                }
                case Code.Ldelema:
                {
                    var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand));

                    EmitLdelema(stack, type);

                    break;
                }
                case Code.Ldelem_I1:
                case Code.Ldelem_I2:
                case Code.Ldelem_I4:
                case Code.Ldelem_I8:
                case Code.Ldelem_U1:
                case Code.Ldelem_U2:
                case Code.Ldelem_U4:
                case Code.Ldelem_R4:
                case Code.Ldelem_R8:
                case Code.Ldelem_Any:
                case Code.Ldelem_Ref:
                {
                    // TODO: Properly use opcode for type conversion
                    EmitLdelem(stack);

                    break;
                }
                case Code.Stelem_I1:
                case Code.Stelem_I2:
                case Code.Stelem_I4:
                case Code.Stelem_I8:
                case Code.Stelem_R4:
                case Code.Stelem_R8:
                case Code.Stelem_Any:
                case Code.Stelem_Ref:
                {
                    // TODO: Properly use opcode for type conversion
                    EmitStelem(stack);

                    break;
                }
                #endregion

                #region Argument opcodes (Ldarg, Ldarga, Starg, etc...)
                // Ldarg
                case Code.Ldarg_0:
                case Code.Ldarg_1:
                case Code.Ldarg_2:
                case Code.Ldarg_3:
                {
                    var value = opcode - Code.Ldarg_0;
                    EmitLdarg(stack, args, value);
                    break;
                }
                case Code.Ldarg_S:
                case Code.Ldarg:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitLdarg(stack, args, value);
                    break;
                }
                case Code.Ldarga:
                case Code.Ldarga_S:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitLdarga(stack, args, value);
                    break;
                }
                case Code.Starg:
                case Code.Starg_S:
                {
                    var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0);
                    EmitStarg(stack, args, value);
                    break;
                }
                #endregion

                #region Load opcodes (Ldc, Ldstr, Ldloc, etc...)
                // Ldc_I4
                case Code.Ldc_I4_M1:
                case Code.Ldc_I4_0:
                case Code.Ldc_I4_1:
                case Code.Ldc_I4_2:
                case Code.Ldc_I4_3:
                case Code.Ldc_I4_4:
                case Code.Ldc_I4_5:
                case Code.Ldc_I4_6:
                case Code.Ldc_I4_7:
                case Code.Ldc_I4_8:
                {
                    var value = opcode - Code.Ldc_I4_0;
                    EmitI4(stack, value);
                    break;
                }
                case Code.Ldc_I4_S:
                {
                    var value = (sbyte)instruction.Operand;
                    EmitI4(stack, value);
                    break;
                }
                case Code.Ldc_I4:
                {
                    var value = (int)instruction.Operand;
                    EmitI4(stack, value);
                    break;
                }
                // Ldc_I8
                case Code.Ldc_I8:
                {
                    var value = (long)instruction.Operand;
                    EmitI8(stack, value);
                    break;
                }
                case Code.Ldc_R4:
                {
                    var value = (float)instruction.Operand;
                    EmitR4(stack, value);
                    break;
                }
                case Code.Ldc_R8:
                {
                    var value = (double)instruction.Operand;
                    EmitR8(stack, value);
                    break;
                }
                case Code.Ldstr:
                {
                    var operand = (string)instruction.Operand;

                    EmitLdstr(stack, operand);

                    break;
                }
                case Code.Ldnull:
                {
                    EmitLdnull(stack);
                    break;
                }

                // Ldloc
                case Code.Ldloc_0:
                case Code.Ldloc_1:
                case Code.Ldloc_2:
                case Code.Ldloc_3:
                {
                    var localIndex = opcode - Code.Ldloc_0;
                    EmitLdloc(stack, locals, localIndex);
                    break;
                }
                case Code.Ldloc:
                case Code.Ldloc_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitLdloc(stack, locals, localIndex);
                    break;
                }
                case Code.Ldloca:
                case Code.Ldloca_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitLdloca(stack, locals, localIndex);
                    break;
                }
                case Code.Ldfld:
                case Code.Ldflda:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.Fields[fieldReference.Resolve()];

                    if (opcode == Code.Ldflda)
                    {
                        EmitLdflda(stack, field);
                    }
                    else
                    {
                        EmitLdfld(stack, field, functionContext.InstructionFlags);
                        functionContext.InstructionFlags = InstructionFlags.None;
                    }

                    break;
                }
                case Code.Ldsfld:
                case Code.Ldsflda:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.Fields[fieldReference.Resolve()];

                    if (opcode == Code.Ldsflda)
                    {
                        EmitLdsflda(stack, field);
                    }
                    else
                    {
                        EmitLdsfld(stack, field, functionContext.InstructionFlags);
                        functionContext.InstructionFlags = InstructionFlags.None;
                    }

                    break;
                }
                #endregion

                #region Indirect opcodes (Stind, Ldind, etc...)
                case Code.Stind_I:
                case Code.Stind_I1:
                case Code.Stind_I2:
                case Code.Stind_I4:
                case Code.Stind_I8:
                case Code.Stind_R4:
                case Code.Stind_R8:
                case Code.Stind_Ref:
                {
                    var value = stack.Pop();
                    var address = stack.Pop();

                    // Determine type
                    Type type;
                    switch (opcode)
                    {
                        case Code.Stind_I: type = intPtr; break;
                        case Code.Stind_I1: type = int8; break;
                        case Code.Stind_I2: type = int16; break;
                        case Code.Stind_I4: type = int32; break;
                        case Code.Stind_I8: type = int64; break;
                        case Code.Stind_R4: type = @float; break;
                        case Code.Stind_R8: type = @double; break;
                        case Code.Stind_Ref:
                            type = value.Type;
                            break;
                        default:
                            throw new ArgumentException("opcode");
                    }

                    // Convert to local type
                    var sourceValue = ConvertFromStackToLocal(type, value);

                    // Store value at address
                    var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultType, 0), string.Empty);
                    var storeInst = LLVM.BuildStore(builder, sourceValue, pointerCast);
                    SetInstructionFlags(storeInst, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;
                    
                    break;
                }

                case Code.Ldind_I:
                case Code.Ldind_I1:
                case Code.Ldind_I2:
                case Code.Ldind_I4:
                case Code.Ldind_I8:
                case Code.Ldind_U1:
                case Code.Ldind_U2:
                case Code.Ldind_U4:
                case Code.Ldind_R4:
                case Code.Ldind_R8:
                case Code.Ldind_Ref:
                {
                    var address = stack.Pop();

                    // Determine type
                    Type type;
                    switch (opcode)
                    {
                        case Code.Ldind_I:      type = intPtr; break;
                        case Code.Ldind_I1:     type = int8; break;
                        case Code.Ldind_I2:     type = int16; break;
                        case Code.Ldind_I4:     type = int32; break;
                        case Code.Ldind_I8:     type = int64; break;
                        case Code.Ldind_U1:     type = int8; break;
                        case Code.Ldind_U2:     type = int16; break;
                        case Code.Ldind_U4:     type = int32; break;
                        case Code.Ldind_R4:     type = @float; break;
                        case Code.Ldind_R8:     type = @double; break;
                        case Code.Ldind_Ref:
                            type = GetType(((ByReferenceType)address.Type.TypeReference).ElementType);
                            break;
                        default:
                            throw new ArgumentException("opcode");
                    }

                    // Load value at address
                    var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultType, 0), string.Empty);
                    var loadInst = LLVM.BuildLoad(builder, pointerCast, string.Empty);
                    SetInstructionFlags(loadInst, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    // Convert to stack type
                    var value = ConvertFromLocalToStack(type, loadInst);

                    // Add to stack
                    stack.Add(new StackValue(type.StackType, type, value));
                    
                    break;
                }
                #endregion

                #region Store opcodes (Stloc, etc...)
                // Stloc
                case Code.Stloc_0:
                case Code.Stloc_1:
                case Code.Stloc_2:
                case Code.Stloc_3:
                {
                    var localIndex = opcode - Code.Stloc_0;
                    EmitStloc(stack, locals, localIndex);
                    break;
                }
                case Code.Stloc:
                case Code.Stloc_S:
                {
                    var localIndex = ((VariableDefinition)instruction.Operand).Index;
                    EmitStloc(stack, locals, localIndex);
                    break;
                }
                case Code.Stfld:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.Fields[fieldReference.Resolve()];

                    EmitStfld(stack, field, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }

                case Code.Stsfld:
                {
                    var fieldReference = (FieldReference)instruction.Operand;

                    // Resolve class and field
                    var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType));
                    var field = @class.Fields[fieldReference.Resolve()];

                    EmitStsfld(stack, field, functionContext.InstructionFlags);
                    functionContext.InstructionFlags = InstructionFlags.None;

                    break;
                }
                #endregion

                #region Branching (Brtrue, Brfalse, Switch, etc...)
                case Code.Br:
                case Code.Br_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBr(functionContext.BasicBlocks[targetInstruction.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Brfalse:
                case Code.Brfalse_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBrfalse(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                case Code.Brtrue:
                case Code.Brtrue_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;
                    EmitBrtrue(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                case Code.Switch:
                {
                    var targets = (Instruction[])instruction.Operand;
                    var operand = stack.Pop();
                    var @switch = LLVM.BuildSwitch(builder, operand.Value, functionContext.BasicBlocks[instruction.Next.Offset], (uint)targets.Length);
                    for (int i = 0; i < targets.Length; ++i)
                    {
                        var target = targets[i];
                        LLVM.AddCase(@switch, LLVM.ConstInt(int32Type, (ulong)i, false), functionContext.BasicBlocks[target.Offset]);
                    }
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;
                    break;
                }
                #endregion

                #region Conditional branching (Beq, Bgt, etc...)
                case Code.Beq:
                case Code.Beq_S:
                case Code.Bge:
                case Code.Bge_S:
                case Code.Bgt:
                case Code.Bgt_S:
                case Code.Ble:
                case Code.Ble_S:
                case Code.Blt:
                case Code.Blt_S:
                case Code.Bne_Un:
                case Code.Bne_Un_S:
                case Code.Bge_Un:
                case Code.Bge_Un_S:
                case Code.Bgt_Un:
                case Code.Bgt_Un_S:
                case Code.Ble_Un:
                case Code.Ble_Un_S:
                case Code.Blt_Un:
                case Code.Blt_Un_S:
                {
                    var targetInstruction = (Instruction)instruction.Operand;

                    var operand2 = stack.Pop();
                    var operand1 = stack.Pop();

                    var value1 = operand1.Value;
                    var value2 = operand2.Value;

                    if ((operand1.StackType == StackValueType.NativeInt && operand2.StackType != StackValueType.NativeInt)
                        || (operand1.StackType != StackValueType.NativeInt && operand2.StackType == StackValueType.NativeInt))
                        throw new NotImplementedException("Comparison between native int and int types.");

                    if (operand1.StackType == StackValueType.NativeInt
                        && LLVM.TypeOf(value1) != LLVM.TypeOf(value2))
                    {
                        // NativeInt types should have same types to be comparable
                        value2 = LLVM.BuildPointerCast(builder, value2, LLVM.TypeOf(value1), string.Empty);
                    }

                    // Different object types: cast everything to object
                    if (operand1.StackType == StackValueType.Object
                        && operand2.StackType == StackValueType.Object
                        && operand1.Type != operand2.Type)
                    {
                        value1 = LLVM.BuildPointerCast(builder, value1, @object.DefaultType, string.Empty);
                        value2 = LLVM.BuildPointerCast(builder, value2, @object.DefaultType, string.Empty);
                    }

                    if (operand1.StackType != operand2.StackType
                        || LLVM.TypeOf(value1) != LLVM.TypeOf(value2))
                        throw new InvalidOperationException(string.Format("Comparison between operands of different types, {0} and {1}.", operand1.Type, operand2.Type));

                    ValueRef compareResult;
                    if (operand1.StackType == StackValueType.Float)
                    {
                        RealPredicate predicate;
                        switch (opcode)
                        {
                            case Code.Beq:
                            case Code.Beq_S:    predicate = RealPredicate.RealOEQ; break;
                            case Code.Bge:
                            case Code.Bge_S:    predicate = RealPredicate.RealOGE; break;
                            case Code.Bgt:
                            case Code.Bgt_S:    predicate = RealPredicate.RealOGT; break;
                            case Code.Ble:
                            case Code.Ble_S:    predicate = RealPredicate.RealOLE; break;
                            case Code.Blt:
                            case Code.Blt_S:    predicate = RealPredicate.RealOLT; break;
                            case Code.Bne_Un:
                            case Code.Bne_Un_S: predicate = RealPredicate.RealUNE; break;
                            case Code.Bge_Un:
                            case Code.Bge_Un_S: predicate = RealPredicate.RealUGE; break;
                            case Code.Bgt_Un:
                            case Code.Bgt_Un_S: predicate = RealPredicate.RealUGT; break;
                            case Code.Ble_Un:
                            case Code.Ble_Un_S: predicate = RealPredicate.RealULE; break;
                            case Code.Blt_Un:
                            case Code.Blt_Un_S: predicate = RealPredicate.RealULT; break;
                            default:
                                throw new NotSupportedException();
                        }
                        compareResult = LLVM.BuildFCmp(builder, predicate, value1, value2, string.Empty);
                    }
                    else
                    {
                        IntPredicate predicate;
                        switch (opcode)
                        {
                            case Code.Beq:
                            case Code.Beq_S:    predicate = IntPredicate.IntEQ; break;
                            case Code.Bge:
                            case Code.Bge_S:    predicate = IntPredicate.IntSGE; break;
                            case Code.Bgt:
                            case Code.Bgt_S:    predicate = IntPredicate.IntSGT; break;
                            case Code.Ble:
                            case Code.Ble_S:    predicate = IntPredicate.IntSLE; break;
                            case Code.Blt:
                            case Code.Blt_S:    predicate = IntPredicate.IntSLT; break;
                            case Code.Bne_Un:
                            case Code.Bne_Un_S: predicate = IntPredicate.IntNE; break;
                            case Code.Bge_Un:
                            case Code.Bge_Un_S: predicate = IntPredicate.IntUGE; break;
                            case Code.Bgt_Un:
                            case Code.Bgt_Un_S: predicate = IntPredicate.IntUGT; break;
                            case Code.Ble_Un:
                            case Code.Ble_Un_S: predicate = IntPredicate.IntULE; break;
                            case Code.Blt_Un:
                            case Code.Blt_Un_S: predicate = IntPredicate.IntULT; break;
                            default:
                                throw new NotSupportedException();
                        }
                        compareResult = LLVM.BuildICmp(builder, predicate, value1, value2, string.Empty);
                    }

                    // Branch depending on previous test
                    LLVM.BuildCondBr(builder, compareResult, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]);

                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit;

                    break;
                }
                #endregion

                #region Comparison opcodes (Ceq, Cgt, etc...)
                case Code.Ceq:
                case Code.Cgt:
                case Code.Cgt_Un:
                case Code.Clt:
                case Code.Clt_Un:
                {
                    var operand2 = stack.Pop();
                    var operand1 = stack.Pop();

                    var value1 = operand1.Value;
                    var value2 = operand2.Value;

                    // Downcast objects to typeof(object) so that they are comparables
                    if (operand1.StackType == StackValueType.Object)
                        value1 = ConvertFromStackToLocal(@object, operand1);
                    if (operand2.StackType == StackValueType.Object)
                        value2 = ConvertFromStackToLocal(@object, operand2);

                    if ((operand1.StackType == StackValueType.NativeInt && operand2.StackType != StackValueType.NativeInt)
                        || (operand1.StackType != StackValueType.NativeInt && operand2.StackType == StackValueType.NativeInt))
                        throw new NotImplementedException("Comparison between native int and int types.");

                    if (operand1.StackType == StackValueType.NativeInt
                        && LLVM.TypeOf(value1) != LLVM.TypeOf(value2))
                    {
                        // NativeInt types should have same types to be comparable
                        value2 = LLVM.BuildPointerCast(builder, value2, LLVM.TypeOf(value1), string.Empty);
                    }

                    // Different object types: cast everything to object
                    if (operand1.StackType == StackValueType.Object
                        && operand2.StackType == StackValueType.Object
                        && operand1.Type != operand2.Type)
                    {
                        value1 = LLVM.BuildPointerCast(builder, value1, @object.DefaultType, string.Empty);
                        value2 = LLVM.BuildPointerCast(builder, value2, @object.DefaultType, string.Empty);
                    }

                    if (operand1.StackType != operand2.StackType
                        || LLVM.TypeOf(value1) != LLVM.TypeOf(value2))
                        throw new InvalidOperationException("Comparison between operands of different types.");

                    ValueRef compareResult;
                    if (operand1.StackType == StackValueType.Float)
                    {
                        RealPredicate predicate;
                        switch (opcode)
                        {
                            case Code.Ceq:      predicate = RealPredicate.RealOEQ; break;
                            case Code.Cgt:      predicate = RealPredicate.RealOGT; break;
                            case Code.Cgt_Un:   predicate = RealPredicate.RealUGT; break;
                            case Code.Clt:      predicate = RealPredicate.RealOLT; break;
                            case Code.Clt_Un:   predicate = RealPredicate.RealULT; break;
                            default:
                                throw new NotSupportedException();
                        }
                        compareResult = LLVM.BuildFCmp(builder, predicate, value1, value2, string.Empty);
                    }
                    else
                    {
                        IntPredicate predicate;
                        switch (opcode)
                        {
                            case Code.Ceq:      predicate = IntPredicate.IntEQ; break;
                            case Code.Cgt:      predicate = IntPredicate.IntSGT; break;
                            case Code.Cgt_Un:   predicate = IntPredicate.IntUGT; break;
                            case Code.Clt:      predicate = IntPredicate.IntSLT; break;
                            case Code.Clt_Un:   predicate = IntPredicate.IntULT; break;
                            default:
                                throw new NotSupportedException();
                        }
                        compareResult = LLVM.BuildICmp(builder, predicate, value1, value2, string.Empty);
                    }

                    // Extends to int32
                    compareResult = LLVM.BuildZExt(builder, compareResult, int32Type, string.Empty);

                    // Push result back on the stack
                    stack.Add(new StackValue(StackValueType.Int32, int32, compareResult));

                    break;
                }
                #endregion

                #region Conversion opcodes (Conv_U, Conv_I, etc...)
                case Code.Conv_U:
                case Code.Conv_I:
                case Code.Conv_U1:
                case Code.Conv_I1:
                case Code.Conv_U2:
                case Code.Conv_I2:
                case Code.Conv_U4:
                case Code.Conv_I4:
                case Code.Conv_U8:
                case Code.Conv_I8:
                case Code.Conv_R4:
                case Code.Conv_R8:
                case Code.Conv_R_Un:
                case Code.Conv_Ovf_U:
                case Code.Conv_Ovf_I:
                case Code.Conv_Ovf_U1:
                case Code.Conv_Ovf_I1:
                case Code.Conv_Ovf_U2:
                case Code.Conv_Ovf_I2:
                case Code.Conv_Ovf_U4:
                case Code.Conv_Ovf_I4:
                case Code.Conv_Ovf_U8:
                case Code.Conv_Ovf_I8:
                {
                    var value = stack.Pop();

                    uint intermediateWidth;
                    System.Type intermediateRealType;
                    bool isSigned;
                    bool isOverflow = false;

                    switch (opcode)
                    {
                        case Code.Conv_U: isSigned = false; intermediateWidth = (uint)intPtrSize; break;
                        case Code.Conv_I: isSigned = true; intermediateWidth = (uint)intPtrSize; break;
                        case Code.Conv_U1: isSigned = false; intermediateWidth = 8; break;
                        case Code.Conv_I1: isSigned = true; intermediateWidth = 8; break;
                        case Code.Conv_U2: isSigned = false; intermediateWidth = 16; break;
                        case Code.Conv_I2: isSigned = true; intermediateWidth = 16; break;
                        case Code.Conv_U4: isSigned = false; intermediateWidth = 32; break;
                        case Code.Conv_I4: isSigned = true; intermediateWidth = 32; break;
                        case Code.Conv_U8: isSigned = false; intermediateWidth = 64; break;
                        case Code.Conv_I8: isSigned = true; intermediateWidth = 64; break;
                        case Code.Conv_Ovf_U:  isOverflow = true; goto case Code.Conv_U;
                        case Code.Conv_Ovf_I:  isOverflow = true; goto case Code.Conv_I;
                        case Code.Conv_Ovf_U1: isOverflow = true; goto case Code.Conv_U1;
                        case Code.Conv_Ovf_I1: isOverflow = true; goto case Code.Conv_I1;
                        case Code.Conv_Ovf_U2: isOverflow = true; goto case Code.Conv_U2;
                        case Code.Conv_Ovf_I2: isOverflow = true; goto case Code.Conv_I2;
                        case Code.Conv_Ovf_U4: isOverflow = true; goto case Code.Conv_U4;
                        case Code.Conv_Ovf_I4: isOverflow = true; goto case Code.Conv_I4;
                        case Code.Conv_Ovf_U8: isOverflow = true; goto case Code.Conv_U8;
                        case Code.Conv_Ovf_I8: isOverflow = true; goto case Code.Conv_I8;
                        case Code.Conv_R4:
                        case Code.Conv_R8:
                            var inputTypeFullName = value.Type.TypeReference.FullName;
                            isSigned = inputTypeFullName == typeof(int).FullName
                                || inputTypeFullName == typeof(short).FullName
                                || inputTypeFullName == typeof(sbyte).FullName
                                || inputTypeFullName == typeof(IntPtr).FullName;
                            intermediateWidth = 0; // unknown yet, depends on input
                            break;
                        case Code.Conv_R_Un:
                            // TODO: Not sure if this is exactly what Conv_R_Un should do...
                            isSigned = false;
                            intermediateWidth = 0;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }


                    var currentValue = value.Value;

                    if (value.StackType == StackValueType.NativeInt)
                    {
                        // Convert to integer
                        currentValue = LLVM.BuildPtrToInt(builder, currentValue, nativeIntType, string.Empty);
                    }
                    else if (value.StackType == StackValueType.Reference
                        || value.StackType == StackValueType.Object)
                    {
                        if (opcode != Code.Conv_U8 && opcode != Code.Conv_U
                            && opcode != Code.Conv_I8 && opcode != Code.Conv_I)
                            throw new InvalidOperationException();

                        // Convert to integer
                        currentValue = LLVM.BuildPtrToInt(builder, currentValue, nativeIntType, string.Empty);
                    }
                    else if (value.StackType == StackValueType.Float)
                    {
                        if (opcode == Code.Conv_R4 || opcode == Code.Conv_R8)
                        {
                            // Special case: float to float, avoid usual case that goes through an intermediary integer.
                            var outputType = opcode == Code.Conv_R8 ? @double : @float;
                            currentValue = LLVM.BuildFPCast(builder, currentValue, outputType.DataType, string.Empty);
                            stack.Add(new StackValue(StackValueType.Float, outputType, currentValue));
                            break;
                        }

                        // TODO: Float conversions
                        currentValue = isSigned
                            ? LLVM.BuildFPToSI(builder, currentValue, LLVM.IntTypeInContext(context, intermediateWidth), string.Empty)
                            : LLVM.BuildFPToUI(builder, currentValue, LLVM.IntTypeInContext(context, intermediateWidth), string.Empty);
                    }

                    var inputType = LLVM.TypeOf(currentValue);
                    var inputWidth = LLVM.GetIntTypeWidth(inputType);

                    // Auto-adapt intermediate width for floats
                    if (opcode == Code.Conv_R4 || opcode == Code.Conv_R8 || opcode == Code.Conv_R_Un)
                    {
                        intermediateWidth = inputWidth;
                    }

                    var smallestWidth = Math.Min(intermediateWidth, inputWidth);
                    var smallestType = LLVM.IntTypeInContext(context, smallestWidth);
                    var outputWidth = Math.Max(intermediateWidth, 32);

                    // Truncate (if necessary)
                    if (smallestWidth < inputWidth)
                        currentValue = LLVM.BuildTrunc(builder, currentValue, smallestType, string.Empty);

                    if (isOverflow)
                    {
                        // TODO: Compare currentValue with pre-trunc value?
                    }

                    // Reextend to appropriate type (if necessary)
                    if (outputWidth > smallestWidth)
                    {
                        var outputIntType = LLVM.IntTypeInContext(context, outputWidth);
                        if (isSigned)
                            currentValue = LLVM.BuildSExt(builder, currentValue, outputIntType, string.Empty);
                        else
                            currentValue = LLVM.BuildZExt(builder, currentValue, outputIntType, string.Empty);
                    }

                    // Add constant integer value to stack
                    switch (opcode)
                    {
                        case Code.Conv_U:
                        case Code.Conv_I:
                        case Code.Conv_Ovf_U:
                        case Code.Conv_Ovf_I:
                            // Convert to native int (if necessary)
                            currentValue = LLVM.BuildIntToPtr(builder, currentValue, intPtrType, string.Empty);
                            stack.Add(new StackValue(StackValueType.NativeInt, intPtr, currentValue));
                            break;
                        case Code.Conv_U1:
                        case Code.Conv_I1:
                        case Code.Conv_U2:
                        case Code.Conv_I2:
                        case Code.Conv_U4:
                        case Code.Conv_I4:
                        case Code.Conv_Ovf_U1:
                        case Code.Conv_Ovf_I1:
                        case Code.Conv_Ovf_U2:
                        case Code.Conv_Ovf_I2:
                        case Code.Conv_Ovf_U4:
                        case Code.Conv_Ovf_I4:
                            stack.Add(new StackValue(StackValueType.Int32, int32, currentValue));
                            break;
                        case Code.Conv_U8:
                        case Code.Conv_I8:
                        case Code.Conv_Ovf_U8:
                        case Code.Conv_Ovf_I8:
                            stack.Add(new StackValue(StackValueType.Int64, int64, currentValue));
                            break;
                        case Code.Conv_R4:
                        case Code.Conv_R8:
                        case Code.Conv_R_Un:
                            var outputType = opcode == Code.Conv_R8 || opcode == Code.Conv_R_Un ? @double : @float;
                            if (isSigned)
                                currentValue = LLVM.BuildSIToFP(builder, currentValue, outputType.DataType, string.Empty);
                            else
                                currentValue = LLVM.BuildUIToFP(builder, currentValue, outputType.DataType, string.Empty);
                            stack.Add(new StackValue(StackValueType.Float, outputType, currentValue));
                            break;
                        default:
                            throw new InvalidOperationException();
                    }

                    break;
                }
                #endregion

                #region Unary operation opcodes (Neg, Not, etc...)
                case Code.Neg:
                case Code.Not:
                {
                    var operand1 = stack.Pop();

                    var value1 = operand1.Value;

                    // Check stack type (and convert if necessary)
                    switch (operand1.StackType)
                    {
                        case StackValueType.Float:
                            if (opcode == Code.Not)
                                throw new InvalidOperationException("Not opcode doesn't work with float");
                            break;
                        case StackValueType.NativeInt:
                            value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty);
                            break;
                        case StackValueType.Int32:
                        case StackValueType.Int64:
                            break;
                        default:
                            throw new InvalidOperationException(string.Format("Opcode {0} not supported with stack type {1}", opcode, operand1.StackType));
                    }

                    // Perform neg or not operation
                    switch (opcode)
                    {
                        case Code.Neg:
                            if (operand1.StackType == StackValueType.Float)
                                value1 = LLVM.BuildFNeg(builder, value1, string.Empty);
                            else
                                value1 = LLVM.BuildNeg(builder, value1, string.Empty);
                            break;
                        case Code.Not:
                            value1 = LLVM.BuildNot(builder, value1, string.Empty);
                            break;
                    }

                    if (operand1.StackType == StackValueType.NativeInt)
                        value1 = LLVM.BuildIntToPtr(builder, value1, intPtrType, string.Empty);

                    // Add back to stack (with same type as before)
                    stack.Add(new StackValue(operand1.StackType, operand1.Type, value1));

                    break;
                }
                #endregion

                #region Binary operation opcodes (Add, Sub, etc...)
                case Code.Add:
                case Code.Add_Ovf:
                case Code.Add_Ovf_Un:
                case Code.Sub:
                case Code.Sub_Ovf:
                case Code.Sub_Ovf_Un:
                case Code.Mul:
                case Code.Mul_Ovf:
                case Code.Mul_Ovf_Un:
                case Code.Div:
                case Code.Div_Un:
                case Code.Rem:
                case Code.Rem_Un:
                case Code.Shl:
                case Code.Shr:
                case Code.Shr_Un:
                case Code.Xor:
                case Code.Or:
                case Code.And:
                {
                    var operand2 = stack.Pop();
                    var operand1 = stack.Pop();

                    var value1 = operand1.Value;
                    var value2 = operand2.Value;

                    StackValueType outputStackType;

                    bool isShiftOperation = false;
                    bool isIntegerOperation = false;

                    // Detect shift and integer operations
                    switch (opcode)
                    {
                        case Code.Shl:
                        case Code.Shr:
                        case Code.Shr_Un:
                            isShiftOperation = true;
                            break;
                        case Code.Xor:
                        case Code.Or:
                        case Code.And:
                        case Code.Div_Un:
                        case Code.Not:
                            isIntegerOperation = true;
                            break;
                    }

                    if (isShiftOperation) // Shift operations are specials
                    {
                        switch (operand2.StackType)
                        {
                            case StackValueType.Int32:
                            case StackValueType.NativeInt:
                                value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty);
                                break;
                            default:
                                goto InvalidBinaryOperation;
                        }

                        // Check first operand, and convert second operand to match first one
                        switch (operand1.StackType)
                        {
                            case StackValueType.Int32:
                                value2 = LLVM.BuildIntCast(builder, value2, int32Type, string.Empty);
                                break;
                            case StackValueType.Int64:
                                value2 = LLVM.BuildIntCast(builder, value2, int64Type, string.Empty);
                                break;
                            case StackValueType.NativeInt:
                                value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty);
                                value2 = LLVM.BuildIntCast(builder, value2, nativeIntType, string.Empty);
                                break;
                            default:
                                goto InvalidBinaryOperation;
                        }

                        // Output type is determined by first operand
                        outputStackType = operand1.StackType;
                    }
                    else if (operand1.StackType == operand2.StackType) // Diagonal
                    {
                        // Check type
                        switch (operand1.StackType)
                        {
                            case StackValueType.Int32:
                            case StackValueType.Int64:
                            case StackValueType.Float:
                                outputStackType = operand1.StackType;
                                break;
                            case StackValueType.NativeInt:
                                value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty);
                                value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty);
                                outputStackType = operand1.StackType;
                                break;
                            case StackValueType.Reference:
                                if (opcode != Code.Sub && opcode != Code.Sub_Ovf_Un)
                                    goto InvalidBinaryOperation;
                                value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty);
                                value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty);
                                outputStackType = StackValueType.NativeInt;
                                break;
                            default:
                                throw new InvalidOperationException(string.Format("Binary operations are not allowed on {0}.", operand1.StackType));
                        }
                    }
                    else if (operand1.StackType == StackValueType.NativeInt && operand2.StackType == StackValueType.Int32)
                    {
                        value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty);
                        outputStackType = StackValueType.NativeInt;
                    }
                    else if (operand1.StackType == StackValueType.Int32 && operand2.StackType == StackValueType.NativeInt)
                    {
                        value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty);
                        outputStackType = StackValueType.NativeInt;
                    }
                    else if (!isIntegerOperation
                        && (operand1.StackType == StackValueType.Reference || operand2.StackType == StackValueType.Reference)) // ref + [i32, nativeint] or [i32, nativeint] + ref
                    {
                        StackValue operandRef, operandInt;
                        ValueRef valueRef, valueInt;

                        if (operand2.StackType == StackValueType.Reference)
                        {
                            operandRef = operand2;
                            operandInt = operand1;
                            valueRef = value2;
                            valueInt = value1;

                        }
                        else
                        {
                            operandRef = operand1;
                            operandInt = operand2;
                            valueRef = value1;
                            valueInt = value2;
                        }

                        switch (operandInt.StackType)
                        {
                            case StackValueType.Int32:
                                break;
                            case StackValueType.NativeInt:
                                valueInt = LLVM.BuildPtrToInt(builder, valueInt, nativeIntType, string.Empty);
                                break;
                            default:
                                goto InvalidBinaryOperation;
                        }

                        switch (opcode)
                        {
                            case Code.Add:
                            case Code.Add_Ovf_Un:
                                break;
                            case Code.Sub:
                            case Code.Sub_Ovf:
                                if (operand2.StackType == StackValueType.Reference)
                                    goto InvalidBinaryOperation;

                                valueInt = LLVM.BuildNeg(builder, valueInt, string.Empty);
                                break;
                            default:
                                goto InvalidBinaryOperation;
                        }

                        // If necessary, cast to i8*
                        var valueRefType = LLVM.TypeOf(valueRef);
                        if (valueRefType != intPtrType)
                            valueRef = LLVM.BuildPointerCast(builder, valueRef, intPtrType, string.Empty);

                        valueRef = LLVM.BuildGEP(builder, valueRef, new[] { valueInt }, string.Empty);

                        // Cast back to original type
                        if (valueRefType != intPtrType)
                            valueRef = LLVM.BuildPointerCast(builder, valueRef, valueRefType, string.Empty);

                        stack.Add(new StackValue(StackValueType.Reference, operandRef.Type, valueRef));

                        // Early exit
                        break;
                    }
                    else
                    {
                        goto InvalidBinaryOperation;
                    }

                    ValueRef result;

                    // Perform binary operation
                    if (operand1.StackType == StackValueType.Float)
                    {
                        switch (opcode)
                        {
                            case Code.Add:          result = LLVM.BuildFAdd(builder, value1, value2, string.Empty); break;
                            case Code.Sub:          result = LLVM.BuildFSub(builder, value1, value2, string.Empty); break;
                            case Code.Mul:          result = LLVM.BuildFMul(builder, value1, value2, string.Empty); break;
                            case Code.Div:          result = LLVM.BuildFDiv(builder, value1, value2, string.Empty); break;
                            case Code.Rem:          result = LLVM.BuildFRem(builder, value1, value2, string.Empty); break;
                            default:
                                goto InvalidBinaryOperation;
                        }
                    }
                    else
                    {
                        switch (opcode)
                        {
                            case Code.Add:          result = LLVM.BuildAdd(builder, value1, value2, string.Empty); break;
                            case Code.Sub:          result = LLVM.BuildSub(builder, value1, value2, string.Empty); break;
                            case Code.Mul:          result = LLVM.BuildMul(builder, value1, value2, string.Empty); break;
                            case Code.Div:          result = LLVM.BuildSDiv(builder, value1, value2, string.Empty); break;
                            case Code.Div_Un:       result = LLVM.BuildUDiv(builder, value1, value2, string.Empty); break;
                            case Code.Rem:          result = LLVM.BuildSRem(builder, value1, value2, string.Empty); break;
                            case Code.Rem_Un:       result = LLVM.BuildURem(builder, value1, value2, string.Empty); break;
                            case Code.Shl:          result = LLVM.BuildShl(builder, value1, value2, string.Empty); break;
                            case Code.Shr:          result = LLVM.BuildAShr(builder, value1, value2, string.Empty); break;
                            case Code.Shr_Un:       result = LLVM.BuildLShr(builder, value1, value2, string.Empty); break;
                            case Code.And:          result = LLVM.BuildAnd(builder, value1, value2, string.Empty); break;
                            case Code.Or:           result = LLVM.BuildOr(builder, value1, value2, string.Empty); break;
                            case Code.Xor:          result = LLVM.BuildXor(builder, value1, value2, string.Empty); break;
                            case Code.Add_Ovf:
                            case Code.Add_Ovf_Un:
                            case Code.Sub_Ovf:
                            case Code.Sub_Ovf_Un:
                            case Code.Mul_Ovf:
                            case Code.Mul_Ovf_Un:
                            {
                                Intrinsics intrinsicId;
                                switch (opcode)
                                {
                                    case Code.Add_Ovf:
                                        intrinsicId = Intrinsics.sadd_with_overflow;
                                        break;
                                    case Code.Add_Ovf_Un:
                                        intrinsicId = Intrinsics.uadd_with_overflow;
                                        break;
                                    case Code.Sub_Ovf:
                                        intrinsicId = Intrinsics.ssub_with_overflow;
                                        break;
                                    case Code.Sub_Ovf_Un:
                                        intrinsicId = Intrinsics.usub_with_overflow;
                                        break;
                                    case Code.Mul_Ovf:
                                        intrinsicId = Intrinsics.smul_with_overflow;
                                        break;
                                    case Code.Mul_Ovf_Un:
                                        intrinsicId = Intrinsics.umul_with_overflow;
                                        break;
                                    default:
                                        throw new ArgumentOutOfRangeException();
                                }
                                var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)intrinsicId, new[] { LLVM.TypeOf(value1) });
                                var overflowResult = LLVM.BuildCall(builder, intrinsic, new[] {value1, value2}, string.Empty);
                                var hasOverflow = LLVM.BuildExtractValue(builder, overflowResult, 1, string.Empty);

                                var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty);
                                var overflowBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "overflow");

                                LLVM.BuildCondBr(builder, hasOverflow, overflowBlock, nextBlock);

                                LLVM.PositionBuilderAtEnd(builder, overflowBlock);

                                // Create OverflowException object
                                var overflowExceptionClass = GetClass(corlib.MainModule.GetType(typeof(OverflowException).FullName));
                                EmitNewobj(functionContext, overflowExceptionClass.Type, overflowExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0));
                                var overflowException = stack.Pop();
                                GenerateInvoke(functionContext, throwExceptionFunction, new[] { LLVM.BuildPointerCast(builder, overflowException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) });
                                LLVM.BuildUnreachable(builder);

                                functionContext.BasicBlock = nextBlock;
                                LLVM.PositionBuilderAtEnd(builder, nextBlock);
                                result = LLVM.BuildExtractValue(builder, overflowResult, 0, string.Empty);

                                break;
                            }
                            default:
                                goto InvalidBinaryOperation;
                        }
                    }

                    Type outputType;

                    switch (outputStackType)
                    {
                        case StackValueType.Int32:
                        case StackValueType.Int64:
                        case StackValueType.Float:
                            // No output conversion required, as it could only have been from same input types (non-shift) or operand 1 (shift)
                            outputType = operand1.Type;
                            break;
                        case StackValueType.NativeInt:
                            outputType = intPtr;
                            result = LLVM.BuildIntToPtr(builder, result, intPtrType, string.Empty);
                            break;
                        case StackValueType.Reference:
                            result = LLVM.BuildIntToPtr(builder, result, intPtrType, string.Empty);

                            // Get type from one of its operand (if output is reference type, one of the two operand must be too)
                            if (operand1.StackType == StackValueType.Reference)
                                outputType = operand1.Type;
                            else if (operand2.StackType == StackValueType.Reference)
                                outputType = operand2.Type;
                            else
                                goto InvalidBinaryOperation;
                            break;
                        default:
                            goto InvalidBinaryOperation;
                    }

                    stack.Add(new StackValue(outputStackType, outputType, result));

                    break;

                InvalidBinaryOperation:
                    throw new InvalidOperationException(string.Format("Binary operation {0} between {1} and {2} is not supported.", opcode, operand1.StackType, operand2.StackType));
                }
                #endregion

                #region Exception handling opcodes (Leave, Endfinally, etc...)
                case Code.Throw:
                {
                    var exceptionObject = stack.Pop();

                    // Throw exception
                    // TODO: Update callstack
                    GenerateInvoke(functionContext, throwExceptionFunction, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) });
                    LLVM.BuildUnreachable(builder);

                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Rethrow:
                {
                    // Find exception that was on stack at beginning of this catch clause
                    var currentCatchClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset);

                    if (currentCatchClause == null || currentCatchClause.Source.HandlerType != ExceptionHandlerType.Catch)
                        throw new InvalidOperationException("Can't find catch clause matching this rethrow instruction.");

                    var catchClauseStack = functionContext.ForwardStacks[currentCatchClause.Source.HandlerStart.Offset];
                    var exceptionObject = catchClauseStack[0];

                    // Rethrow exception
                    GenerateInvoke(functionContext, throwExceptionFunction, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) });
                    LLVM.BuildUnreachable(builder);

                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;
                    break;
                }
                case Code.Leave:
                case Code.Leave_S:
                {
                    // Evaluation stack is cleared
                    stack.Clear();

                    // Default target (if we jump inside the exception clause)
                    var targetInstruction = (Instruction)instruction.Operand;

                    GenerateLeave(functionContext.ActiveTryHandlers, targetInstruction, functionContext.EndfinallyJumpTarget, functionContext.BasicBlocks);
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;

                    break;
                }
                case Code.Endfinally:
                {
                    var currentFinallyClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset);

                    if (currentFinallyClause == null || currentFinallyClause.Source.HandlerType != ExceptionHandlerType.Finally)
                        throw new InvalidOperationException("Can't find finally clause matching this endfinally instruction.");

                    // Basic block to continue exception handling (if endfinally.jumptarget is -1, but we simply set it on undefined cases)
                    var activeTryHandlers = functionContext.ActiveTryHandlers;
                    var nextActiveTryHandler = activeTryHandlers.Count > 0 ? activeTryHandlers[activeTryHandlers.Count - 1].CatchDispatch : functionContext.ResumeExceptionBlock;

                    // Generate dispatch code (with a switch/case)
                    var @switch = LLVM.BuildSwitch(builder, LLVM.BuildLoad(builder, functionContext.EndfinallyJumpTarget, string.Empty), nextActiveTryHandler, (uint)currentFinallyClause.LeaveTargets.Count);
                    for (int index = 0; index < currentFinallyClause.LeaveTargets.Count; index++)
                    {
                        var leaveTarget = currentFinallyClause.LeaveTargets[index];

                        LLVM.AddCase(@switch, LLVM.ConstInt(int32Type, (ulong)index, false), functionContext.BasicBlocks[leaveTarget.Offset]);
                    }

                    // Default is not to flow to next instruction, previous Leave instructions already tagged what was necessary
                    functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None;

                    break;
                }
                #endregion

                #region Instruction flags (Unaligned, Volatile)
                case Code.Volatile:
                    functionContext.InstructionFlags |= InstructionFlags.Volatile;
                    break;
                case Code.Unaligned:
                    functionContext.InstructionFlags |= InstructionFlags.Unaligned;
                    break;
                #endregion

                default:
                    throw new NotImplementedException(string.Format("Opcode {0} not implemented.", instruction.OpCode));
            }
        }
コード例 #42
0
ファイル: Compiler.Emit.cs プロジェクト: RainsSoft/SharpLang
        private void EmitLdind(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode)
        {
            var address = stack.Pop();

            // Determine type
            Type type;
            switch (opcode)
            {
                case Code.Ldind_I: type = intPtr; break;
                case Code.Ldind_I1: type = int8; break;
                case Code.Ldind_I2: type = int16; break;
                case Code.Ldind_I4: type = int32; break;
                case Code.Ldind_I8: type = int64; break;
                case Code.Ldind_U1: type = int8; break;
                case Code.Ldind_U2: type = int16; break;
                case Code.Ldind_U4: type = int32; break;
                case Code.Ldind_R4: type = @float; break;
                case Code.Ldind_R8: type = @double; break;
                case Code.Ldind_Ref:
                    type = GetType(((ByReferenceType)address.Type.TypeReferenceCecil).ElementType, TypeState.StackComplete);
                    break;
                default:
                    throw new ArgumentException("opcode");
            }

            if (CharUsesUTF8)
            {
                if (opcode == Code.Ldind_I2 && address.Type.TypeReferenceCecil.FullName == typeof(char*).FullName)
                {
                    type = int8;
                }
            }

            // Load value at address
            var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultTypeLLVM, 0), string.Empty);

            var loadInst = LoadValue(type.StackType, pointerCast, functionContext.InstructionFlags);
            functionContext.InstructionFlags = InstructionFlags.None;

            // Convert to stack type
            var value = ConvertFromLocalToStack(type, loadInst);

            // Add to stack
            stack.Add(new StackValue(type.StackType, type, value));
        }