예제 #1
0
파일: Compiler.cs 프로젝트: frje/SharpLang
        void PrepareClassMethods(Type type)
        {
            var @class = GetClass(type);

            // Already processed?
            if (@class == null || @class.MethodCompiled)
            {
                return;
            }

            @class.MethodCompiled = true;

            // Array: no need to do anything (its type definition, Array, has already been processed)
            if (@class.Type.TypeReference is ArrayType)
            {
                return;
            }

            var typeDefinition = GetMethodTypeDefinition(@class.Type.TypeReference);

            bool isInterface = typeDefinition.IsInterface;

            // Process methods, Virtual first, then non virtual, then static
            foreach (var method in typeDefinition.Methods.OrderBy(x => x.IsVirtual ? 0 : (!x.IsStatic ? 1 : 2)))
            {
                var methodReference = ResolveGenericMethod(@class.Type.TypeReference, method);

                // If a method contains generic parameters, skip it
                // Its closed instantiations (with generic arguments) is what needs to be generated.
                // (except interface methods)
                // Using ResolveGenericsVisitor.ContainsGenericParameters because Cecil one's doesn't seem to match what .NET Type does.
                // TODO: Might need a more robust generic resolver/analyzer system soon.
                if (ResolveGenericsVisitor.ContainsGenericParameters(methodReference))
                {
                    continue;
                }

                var function = CreateFunction(methodReference);

                @class.Functions.Add(function);

                if (method.IsSpecialName && method.Name == ".cctor")
                {
                    @class.StaticCtor = function;
                }

                if (method.IsVirtual)
                {
                    if (isInterface)
                    {
                        // Store IMT slot
                        function.VirtualSlot = (int)(GetMethodId(methodReference) % InterfaceMethodTableSize);
                    }
                    else if (method.IsNewSlot)
                    {
                        // New slot
                        function.VirtualSlot = @class.VirtualTable.Count;
                        @class.VirtualTable.Add(function);
                    }
                    else
                    {
                        // Find slot in base types
                        var      baseType      = @class.BaseType;
                        Function matchedMethod = null;
                        while (baseType != null)
                        {
                            matchedMethod = CecilExtensions.TryMatchMethod(baseType, methodReference);
                            if (matchedMethod != null)
                            {
                                break;
                            }
                            baseType = baseType.BaseType;
                        }

                        if (matchedMethod == null)
                        {
                            throw new InvalidOperationException(string.Format("Could not find a slot for virtual function {0} in parents of class {1}", method, @class.Type.TypeReference));
                        }

                        function.VirtualSlot = matchedMethod.VirtualSlot;
                        @class.VirtualTable[function.VirtualSlot] = function;
                    }
                }
                else
                {
                    // New slot
                    function.VirtualSlot = @class.VirtualTable.Count;
                    @class.VirtualTable.Add(function);
                }
            }
        }
예제 #2
0
        void CompileClassMethods(Class @class)
        {
            // Already processed?
            if (@class.MethodCompiled)
            {
                return;
            }

            @class.MethodCompiled = true;

            var typeDefinition = @class.TypeReference.Resolve();

            bool isInterface = typeDefinition.IsInterface;

            // Process methods
            foreach (var method in typeDefinition.Methods)
            {
                // If a method contains generic parameters, skip it
                // Its closed instantiations (with generic arguments) is what needs to be generated.
                if (method.ContainsGenericParameter())
                {
                    continue;
                }

                var methodReference = ResolveGenericMethod(@class.TypeReference, method);
                var function        = CreateFunction(methodReference);

                @class.Functions.Add(function);

                if (method.IsVirtual)
                {
                    if (isInterface)
                    {
                        // Store IMT slot
                        function.VirtualSlot = (int)(GetMethodId(methodReference) % InterfaceMethodTableSize);
                    }
                    else if (method.IsNewSlot)
                    {
                        // New slot
                        function.VirtualSlot = @class.VirtualTable.Count;
                        @class.VirtualTable.Add(function);
                    }
                    else
                    {
                        // Find slot in base types
                        var      baseType      = @class.BaseType;
                        Function matchedMethod = null;
                        while (baseType != null)
                        {
                            matchedMethod = CecilExtensions.TryMatchMethod(baseType, method);
                            if (matchedMethod != null)
                            {
                                break;
                            }
                            baseType = baseType.BaseType;
                        }

                        if (matchedMethod == null)
                        {
                            throw new InvalidOperationException(string.Format("Could not find a slot for virtual function {0} in parents of class {1}", method, @class.TypeReference));
                        }

                        function.VirtualSlot = matchedMethod.VirtualSlot;
                        @class.VirtualTable[function.VirtualSlot] = function;
                    }
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Compiles the specified class.
        /// </summary>
        /// <param name="typeReference">The type definition.</param>
        /// <returns></returns>
        private Class CreateClass(TypeReference typeReference)
        {
            Class @class;

            if (classes.TryGetValue(typeReference, out @class))
            {
                return(@class);
            }

            bool processFields = false;

            switch (typeReference.MetadataType)
            {
            case MetadataType.Void:
            case MetadataType.Boolean:
            case MetadataType.Char:
            case MetadataType.Byte:
            case MetadataType.SByte:
            case MetadataType.Int16:
            case MetadataType.UInt16:
            case MetadataType.Int32:
            case MetadataType.UInt32:
            case MetadataType.Int64:
            case MetadataType.UInt64:
            case MetadataType.IntPtr:
            case MetadataType.UIntPtr:
            case MetadataType.Single:
            case MetadataType.Double:
            case MetadataType.String:
            {
                break;
            }

            case MetadataType.ValueType:
            case MetadataType.Class:
            case MetadataType.Object:
            case MetadataType.GenericInstance:
            {
                // Process non-static fields
                processFields = true;
                break;
            }

            default:
                throw new NotImplementedException();
            }

            var type = GetType(typeReference);

            // Create class version (boxed version with VTable)
            var boxedType = type.ObjectType;
            var dataType  = type.DataType;
            var stackType = type.StackType;

            @class = new Class(type, typeReference, dataType, boxedType, stackType);
            classes.Add(typeReference, @class);

            if (processFields)
            {
                var typeDefinition = typeReference.Resolve();

                var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count);

                var parentClass = typeDefinition.BaseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, typeDefinition.BaseType)) : null;

                // Add parent class
                if (parentClass != null)
                {
                    @class.BaseType = parentClass;

                    // Add parent classes
                    @class.VirtualTable.AddRange(parentClass.VirtualTable);
                    foreach (var @interface in parentClass.Interfaces)
                    {
                        @class.Interfaces.Add(@interface);
                    }
                }

                // Build methods slots
                // TODO: This will trigger their compilation, but maybe we might want to defer that later
                // (esp. since vtable is not built yet => recursion issues)
                CompileClassMethods(@class);

                if (typeDefinition.IsInterface)
                {
                    // Interface: No need for vtable, we can just use object's one
                    var vtableGlobal = GetClass(assembly.MainModule.Import(typeof(object))).GeneratedRuntimeTypeInfoGlobal;
                    LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false);
                    @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal;
                }
                else
                {
                    // Get parent type RTTI
                    var runtimeTypeInfoType   = LLVM.PointerType(LLVM.Int8TypeInContext(context), 0);
                    var parentRuntimeTypeInfo = parentClass != null
                        ? LLVM.ConstPointerCast(parentClass.GeneratedRuntimeTypeInfoGlobal, runtimeTypeInfoType)
                        : LLVM.ConstPointerNull(runtimeTypeInfoType);

                    // Build vtable
                    var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false);

                    // Build IMT
                    var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize];
                    foreach (var @interface in typeDefinition.Interfaces)
                    {
                        var resolvedInterface = ResolveGenericsVisitor.Process(typeReference, @interface);
                        @class.Interfaces.Add(GetClass(resolvedInterface));

                        // TODO: Add any inherited interface inherited by the resolvedInterface as well
                    }

                    foreach (var @interface in @class.Interfaces)
                    {
                        foreach (var interfaceMethod in @interface.TypeReference.Resolve().Methods)
                        {
                            var resolvedInterfaceMethod = ResolveGenericMethod(@interface.TypeReference, interfaceMethod);

                            var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod);

                            // If method is not found, it could be due to covariance/contravariance
                            if (resolvedFunction == null)
                            {
                                throw new InvalidOperationException("Interface method not found");
                            }

                            var methodId     = GetMethodId(resolvedInterfaceMethod);
                            var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length);

                            var imtSlot = interfaceMethodTable[imtSlotIndex];
                            if (imtSlot == null)
                            {
                                interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>();
                            }

                            imtSlot.AddLast(new InterfaceMethodTableEntry {
                                Function = resolvedFunction, MethodId = methodId, SlotIndex = imtSlotIndex
                            });
                        }
                    }
                    var interfaceMethodTableConstant = LLVM.ConstArray(imtEntryType, interfaceMethodTable.Select(imtSlot =>
                    {
                        if (imtSlot == null)
                        {
                            // No entries: null slot
                            return(LLVM.ConstNull(imtEntryType));
                        }

                        if (imtSlot.Count > 1)
                        {
                            throw new NotImplementedException("IMT with more than one entry per slot is not implemented yet.");
                        }

                        var imtEntry = imtSlot.First.Value;
                        return(LLVM.ConstNamedStruct(imtEntryType, new[]
                        {
                            LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType),                // i8* functionPtr
                            LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false),                          // i32 functionId
                            LLVM.ConstPointerNull(LLVM.PointerType(imtEntryType, 0)),                           // IMTEntry* nextSlot
                        }));
                    }).ToArray());

                    // Build static fields
                    foreach (var field in typeDefinition.Fields)
                    {
                        if (!field.IsStatic)
                        {
                            continue;
                        }

                        var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType)));
                        @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count));
                        fieldTypes.Add(fieldType.DefaultType);
                    }

                    var staticFieldsEmpty = LLVM.ConstNull(LLVM.StructTypeInContext(context, fieldTypes.ToArray(), false));
                    fieldTypes.Clear(); // Reused for non-static fields after

                    // Build RTTI
                    var runtimeTypeInfoConstant = LLVM.ConstStructInContext(context, new[] { parentRuntimeTypeInfo, interfaceMethodTableConstant, vtableConstant, staticFieldsEmpty }, false);
                    var vtableGlobal            = LLVM.AddGlobal(module, LLVM.TypeOf(runtimeTypeInfoConstant), string.Empty);
                    LLVM.SetInitializer(vtableGlobal, runtimeTypeInfoConstant);
                    LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false);
                    @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal;
                }

                // Build actual type data (fields)
                // Add fields and vtable slots from parent class
                if (parentClass != null && typeReference.MetadataType == MetadataType.Class)
                {
                    fieldTypes.Add(parentClass.DataType);
                }

                foreach (var field in typeDefinition.Fields)
                {
                    if (field.IsStatic)
                    {
                        continue;
                    }

                    var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType)));
                    @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count));
                    fieldTypes.Add(fieldType.DefaultType);
                }

                LLVM.StructSetBody(dataType, fieldTypes.ToArray(), false);
            }

            return(@class);
        }
예제 #4
0
        private void BuildRuntimeType(Class @class)
        {
            if (@class.IsEmitted)
            {
                return;
            }

            //Console.WriteLine("Build type {0}", @class);

            @class.IsEmitted = true;

            var  zero           = LLVM.ConstInt(int32LLVM, 0, false);
            bool isConcreteType = IsConcreteType(@class.Type);

            // Prepare metadata info
            var sharpLangModule = TestMode ? LLVM.ConstNull(intPtrLLVM) : metadataPerModule[@class.Type.TypeDefinitionCecil.Module];
            var extraTypeInfo   = LLVM.ConstNull(intPtrLLVM);

            var elementTypeSize = zero;

            // Build RTTI
            var runtimeTypeInfoGlobal       = @class.GeneratedEETypeTokenLLVM;
            var runtimeTypeInfoType         = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal));
            var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)];

            LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements);

            var typeSpecification = @class.Type.TypeReferenceCecil as TypeSpecification;

            if (typeSpecification != null)
            {
                if (typeSpecification is ArrayType || typeSpecification is ByReferenceType || typeSpecification is PointerType)
                {
                    // Get element type
                    var elementType = GetType(typeSpecification.ElementType, TypeState.VTableEmitted);
                    elementTypeSize = LLVM.ConstIntCast(LLVM.SizeOf(elementType.DefaultTypeLLVM), int32LLVM, false);
                    extraTypeInfo   = LLVM.ConstPtrToInt(elementType.Class.GeneratedEETypeRuntimeLLVM, nativeIntLLVM);

                    if (typeSpecification is ArrayType)
                    {
                        extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Array, false));
                    }
                    else if (typeSpecification is ByReferenceType)
                    {
                        extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.ByRef, false));
                    }
                    else if (typeSpecification is PointerType)
                    {
                        extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Pointer, false));
                    }

                    extraTypeInfo = LLVM.ConstIntToPtr(extraTypeInfo, intPtrLLVM);
                }

                var genericInstanceType = typeSpecification as GenericInstanceType;
                if (genericInstanceType != null)
                {
                    // Build global with array of VTable (one for each generic argument)
                    var genericArgumentsTypes           = genericInstanceType.GenericArguments.Select(x => GetType(x, TypeState.VTableEmitted)).ToArray();
                    var genericArgumentesTypeGlobalType = LLVM.ArrayType(intPtrLLVM, (uint)genericArgumentsTypes.Length + 1);
                    var genericArgumentsTypesGlobal     = LLVM.AddGlobal(module, genericArgumentesTypeGlobalType, @class.Type.TypeReferenceCecil.MangledName() + ".genericarguments");
                    LLVM.SetLinkage(genericArgumentsTypesGlobal, Linkage.PrivateLinkage);
                    LLVM.SetInitializer(genericArgumentsTypesGlobal, LLVM.ConstArray(intPtrLLVM,
                                                                                     genericArgumentsTypes
                                                                                     .Select(x => LLVM.ConstPointerCast(x.Class.GeneratedEETypeTokenLLVM, intPtrLLVM))
                                                                                     .Concat(new[] { LLVM.ConstPointerNull(intPtrLLVM) })
                                                                                     .ToArray()));
                    extraTypeInfo = LLVM.ConstInBoundsGEP(genericArgumentsTypesGlobal, new[] { zero, zero });
                    extraTypeInfo = LLVM.ConstPointerCast(extraTypeInfo, intPtrLLVM);
                }
            }

            var runtimeTypeFields = new List <ValueRef>
            {
                @class.BaseType != null ? @class.BaseType.GeneratedEETypeTokenLLVM : LLVM.ConstPointerNull(intPtrLLVM),
                LLVM.ConstInt(LLVM.Int8TypeInContext(context), isConcreteType ? 1U : 0U, false),
                LLVM.ConstNamedStruct(typeDefLLVM, new[]
                {
                    sharpLangModule,
                    LLVM.ConstInt(int32LLVM, @class.Type.TypeDefinitionCecil.MetadataToken.ToUInt32(), false),
                }),
                extraTypeInfo,
                LLVM.ConstNull(sharpLangTypeType.DefaultTypeLLVM),
            };

            // Prepare the runtime type object
            var mangledTypeName     = Regex.Replace(@class.Type.TypeReferenceCecil.MangledName() + ".sharplangtype", @"(\W)", "_");
            var sharpLangTypeGlobal = LLVM.AddGlobal(module, sharpLangTypeType.ObjectTypeLLVM, mangledTypeName);

            LLVM.SetLinkage(sharpLangTypeGlobal, Linkage.PrivateLinkage);
            LLVM.SetInitializer(sharpLangTypeGlobal, LLVM.ConstNull(sharpLangTypeType.ObjectTypeLLVM));

            if (isConcreteType)
            {
                // Build IMT
                var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize];
                foreach (var @interface in @class.Interfaces)
                {
                    foreach (var interfaceMethod in @interface.Type.TypeReferenceCecil.Resolve().Methods)
                    {
                        var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReferenceCecil, interfaceMethod);

                        // If method is not fully resolved (generic method in interface), ignore it
                        // We are waiting for actual closed uses.
                        if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod))
                        {
                            continue;
                        }

                        var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod);
                        if (resolvedFunction == null && @class.Type.TypeReferenceCecil is ArrayType)
                        {
                            var arrayType      = corlib.MainModule.GetType(typeof(Array).FullName);
                            var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name));
                            if (matchingMethod != null)
                            {
                                if (matchingMethod.HasGenericParameters)
                                {
                                    matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReferenceCecil).ElementType);
                                }

                                resolvedFunction = GetFunction(matchingMethod);

                                // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists).
                                EmitFunction(resolvedFunction);
                                LLVM.SetLinkage(resolvedFunction.GeneratedValue, Linkage.LinkOnceAnyLinkage);
                            }
                        }

                        if (resolvedFunction == null)
                        {
                            throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class));
                        }

                        var isInterface = resolvedFunction.DeclaringType.TypeReferenceCecil.Resolve().IsInterface;
                        if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1)
                        {
                            // We might have found a base virtual method matching this interface method.
                            // Let's get the actual method override for this virtual slot.
                            resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot];
                        }

                        // If method is not found, it could be due to covariance/contravariance
                        if (resolvedFunction == null)
                        {
                            throw new InvalidOperationException("Interface method not found");
                        }

                        var methodId     = GetMethodId(resolvedInterfaceMethod);
                        var imtSlotIndex = (int)(methodId % InterfaceMethodTableSize);

                        var imtSlot = interfaceMethodTable[imtSlotIndex];
                        if (imtSlot == null)
                        {
                            interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>();
                        }

                        imtSlot.AddLast(new InterfaceMethodTableEntry
                        {
                            Function = resolvedFunction,
                            MethodId = GetFunction(resolvedInterfaceMethod).GeneratedValue, // Should be a fake global, that we use as IMT key
                        });
                    }
                }
                var interfaceMethodTableConstant = LLVM.ConstArray(intPtrLLVM, interfaceMethodTable.Select(imtSlot =>
                {
                    if (imtSlot == null)
                    {
                        // No entries: null slot
                        return(LLVM.ConstNull(intPtrLLVM));
                    }

                    if (imtSlot.Count == 1)
                    {
                        // Single entry
                        var imtEntry = imtSlot.First.Value;
                        return(LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM));
                    }
                    else
                    {
                        // Multiple entries, create IMT array with all entries
                        // TODO: Support covariance/contravariance?
                        var imtEntries = LLVM.ConstArray(imtEntryLLVM, imtSlot.Select(imtEntry =>
                        {
                            return(LLVM.ConstNamedStruct(imtEntryLLVM, new[]
                            {
                                imtEntry.MethodId,                                                      // i8* functionId
                                LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM), // i8* functionPtr
                            }));
                        })
                                                         .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryLLVM), 1)).ToArray()); // Append { 0, 0 } terminator
                        var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReferenceCecil.MangledName() + ".imt");
                        LLVM.SetLinkage(imtEntryGlobal, Linkage.PrivateLinkage);
                        LLVM.SetInitializer(imtEntryGlobal, imtEntries);

                        // Add 1 to differentiate between single entry and IMT array
                        return(LLVM.ConstIntToPtr(
                                   LLVM.ConstAdd(
                                       LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntLLVM),
                                       LLVM.ConstInt(nativeIntLLVM, 1, false)),
                                   intPtrLLVM));
                    }
                }).ToArray());


                // Build list of super types
                var superTypes   = new List <Class>(@class.Depth);
                var currentClass = @class;
                while (currentClass != null)
                {
                    superTypes.Add(currentClass);
                    currentClass = currentClass.BaseType;
                }

                // Reverse so that the list start with most inherited object
                // (allows faster type checking since a given type will always be at a given index)
                superTypes.Reverse();

                // Build super types
                // Helpful for fast is/as checks on class hierarchy
                var superTypeCount  = LLVM.ConstInt(int32LLVM, (ulong)@class.Depth + 1, false);
                var interfacesCount = LLVM.ConstInt(int32LLVM, (ulong)@class.Interfaces.Count, false);

                // Super types global
                var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)superTypes.Count),
                                                              @class.Type.TypeReferenceCecil.MangledName() + ".supertypes");
                LLVM.SetLinkage(superTypesConstantGlobal, Linkage.PrivateLinkage);
                var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero });

                // Interface map global
                var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)@class.Interfaces.Count),
                                                              @class.Type.TypeReferenceCecil.MangledName() + ".interfaces");
                LLVM.SetLinkage(interfacesConstantGlobal, Linkage.PrivateLinkage);
                var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero });

                // Build VTable
                var vtableConstant = LLVM.ConstNamedStruct(@class.VTableTypeLLVM, @class.VirtualTable.Select(x => GetVirtualMethod(x)).ToArray());

                var staticFieldsInitializer = LLVM.ConstNamedStruct(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields], @class.StaticFields.Select(field =>
                {
                    var fieldType = field.Value.Type;

                    if ((field.Key.Attributes & FieldAttributes.HasFieldRVA) != 0)
                    {
                        var initialValue = field.Key.InitialValue;

                        // Seems like if type size is 8, it uses int64 as backing type
                        // Maybe at some point it might be better to encode static fields in a big byte array and use casts instead?
                        if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) == TypeKind.IntegerTypeKind)
                        {
                            unsafe
                            {
                                fixed(byte *initalValueStart = initialValue)
                                {
                                    if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 64)
                                    {
                                        return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(ulong *)initalValueStart, false));
                                    }
                                    if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 32)
                                    {
                                        return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(uint *)initalValueStart, false));
                                    }
                                }
                            }
                        }

                        // Otherwise, for now we assume that if there was a RVA, it was a type with custom layout (default type is i8[]),
                        // as currently generated by compiler in <PrivateImplementationDetails> class.
                        if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) != TypeKind.ArrayTypeKind)
                        {
                            throw new NotSupportedException();
                        }

                        var arrayElementType = LLVM.Int8TypeInContext(context);
                        return(LLVM.ConstArray(arrayElementType, initialValue.Select(x => LLVM.ConstInt(arrayElementType, x, false)).ToArray()));
                    }

                    return(LLVM.ConstNull(fieldType.DefaultTypeLLVM));
                }).ToArray());

                runtimeTypeFields.AddRange(new[]
                {
                    superTypeCount,
                    interfacesCount,
                    superTypesGlobal,
                    interfacesGlobal,
                    LLVM.ConstInt(LLVM.Int8TypeInContext(context), 0, false), // Class initialized?
                    LLVM.ConstIntCast(LLVM.SizeOf(@class.Type.ObjectTypeLLVM), int32LLVM, false),
                    elementTypeSize,
                    interfaceMethodTableConstant,

                    LLVM.ConstInt(int32LLVM, (ulong)@class.VirtualTable.Count, false),
                    vtableConstant,
                    staticFieldsInitializer,
                });

                var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray());
                LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant);

                // Build super type list (after RTTI since we need pointer to RTTI)
                var superTypesConstant = LLVM.ConstArray(intPtrLLVM,
                                                         superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedEETypeTokenLLVM, intPtrLLVM))
                                                         .ToArray());
                LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant);

                // Build interface map
                var interfacesConstant = LLVM.ConstArray(intPtrLLVM,
                                                         @class.Interfaces.Select(
                                                             @interface => LLVM.ConstPointerCast(@interface.GeneratedEETypeTokenLLVM, intPtrLLVM)).ToArray());
                LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant);
            }
            else
            {
                // Non-concrete type, create type with current fields
                var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray());
                LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant);
            }

            // Mark RTTI as external
            LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage);
        }
예제 #5
0
        private void BuildRuntimeType(Class @class)
        {
            if (@class.IsEmitted)
            {
                return;
            }

            @class.IsEmitted = true;

            // Build IMT
            var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize];

            foreach (var @interface in @class.Interfaces)
            {
                foreach (var interfaceMethod in @interface.Type.TypeReference.Resolve().Methods)
                {
                    var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReference, interfaceMethod);

                    // If method is not fully resolved (generic method in interface), ignore it
                    // We are waiting for actual closed uses.
                    if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod))
                    {
                        continue;
                    }

                    var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod);
                    if (resolvedFunction == null && @class.Type.TypeReference is ArrayType)
                    {
                        var arrayType      = corlib.MainModule.GetType(typeof(Array).FullName);
                        var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name));
                        if (matchingMethod != null)
                        {
                            if (matchingMethod.HasGenericParameters)
                            {
                                matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReference).ElementType);
                            }

                            resolvedFunction = GetFunction(matchingMethod);

                            // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists).
                            EmitFunction(resolvedFunction);
                        }
                    }

                    if (resolvedFunction == null)
                    {
                        throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class));
                    }

                    var isInterface = resolvedFunction.DeclaringType.TypeReference.Resolve().IsInterface;
                    if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1)
                    {
                        // We might have found a base virtual method matching this interface method.
                        // Let's get the actual method override for this virtual slot.
                        resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot];
                    }

                    // If method is not found, it could be due to covariance/contravariance
                    if (resolvedFunction == null)
                    {
                        throw new InvalidOperationException("Interface method not found");
                    }

                    var methodId     = GetMethodId(resolvedInterfaceMethod);
                    var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length);

                    var imtSlot = interfaceMethodTable[imtSlotIndex];
                    if (imtSlot == null)
                    {
                        interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>();
                    }

                    imtSlot.AddLast(new InterfaceMethodTableEntry
                    {
                        Function  = resolvedFunction,
                        MethodId  = methodId,
                        SlotIndex = imtSlotIndex
                    });
                }
            }
            var interfaceMethodTableConstant = LLVM.ConstArray(intPtrType, interfaceMethodTable.Select(imtSlot =>
            {
                if (imtSlot == null)
                {
                    // No entries: null slot
                    return(LLVM.ConstNull(intPtrType));
                }

                if (imtSlot.Count == 1)
                {
                    // Single entry
                    var imtEntry = imtSlot.First.Value;
                    return(LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType));
                }
                else
                {
                    // Multiple entries, create IMT array with all entries
                    // TODO: Support covariance/contravariance?
                    var imtEntries = LLVM.ConstArray(imtEntryType, imtSlot.Select(imtEntry =>
                    {
                        return(LLVM.ConstNamedStruct(imtEntryType, new[]
                        {
                            LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false),           // i32 functionId
                            LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType), // i8* functionPtr
                        }));
                    })
                                                     .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryType), 1)).ToArray()); // Append { 0, 0 } terminator
                    var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReference.MangledName() + ".imt");
                    LLVM.SetInitializer(imtEntryGlobal, imtEntries);

                    // Add 1 to differentiate between single entry and IMT array
                    return(LLVM.ConstIntToPtr(
                               LLVM.ConstAdd(
                                   LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntType),
                                   LLVM.ConstInt(nativeIntType, 1, false)),
                               intPtrType));
                }
            }).ToArray());


            // Build list of super types
            var superTypes   = new List <Class>(@class.Depth);
            var currentClass = @class;

            while (currentClass != null)
            {
                superTypes.Add(currentClass);
                currentClass = currentClass.BaseType;
            }

            // Reverse so that the list start with most inherited object
            // (allows faster type checking since a given type will always be at a given index)
            superTypes.Reverse();

            // Build super types
            // Helpful for fast is/as checks on class hierarchy
            var superTypeCount  = LLVM.ConstInt(int32Type, (ulong)@class.Depth + 1, false);
            var interfacesCount = LLVM.ConstInt(int32Type, (ulong)@class.Interfaces.Count, false);

            var zero = LLVM.ConstInt(int32Type, 0, false);

            // Super types global
            var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)superTypes.Count),
                                                          @class.Type.TypeReference.MangledName() + ".supertypes");
            var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero });

            // Interface map global
            var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)@class.Interfaces.Count),
                                                          @class.Type.TypeReference.MangledName() + ".interfaces");
            var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero });

            // Build VTable
            var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false);

            // Build RTTI
            var runtimeTypeInfoGlobal       = @class.GeneratedRuntimeTypeInfoGlobal;
            var runtimeTypeInfoType         = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal));
            var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)];

            LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements);
            var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, new[]
            {
                @class.BaseType != null ? @class.BaseType.GeneratedRuntimeTypeInfoGlobal : LLVM.ConstPointerNull(intPtrType),
                superTypeCount,
                interfacesCount,
                superTypesGlobal,
                interfacesGlobal,
                LLVM.ConstInt(LLVM.Int1TypeInContext(context), 0, false), // Class initialized?
                interfaceMethodTableConstant,
                vtableConstant,
                LLVM.ConstNull(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields]),
            });

            LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant);

            // Build super type list (after RTTI since we need pointer to RTTI)
            var superTypesConstant = LLVM.ConstArray(intPtrType,
                                                     superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedRuntimeTypeInfoGlobal, intPtrType))
                                                     .ToArray());

            LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant);

            // Build interface map
            var interfacesConstant = LLVM.ConstArray(intPtrType,
                                                     @class.Interfaces.Select(
                                                         @interface => LLVM.ConstPointerCast(@interface.GeneratedRuntimeTypeInfoGlobal, intPtrType)).ToArray());

            LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant);

            // Mark RTTI as external
            LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage);
        }