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); } } }
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; } } } }
/// <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); }
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); }
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); }