private void PInvokeEmitGlobals() { // Add ThunkPointers global, which contains the addresses of our thunk functions var thunkPointers = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, 4096), "ThunkPointers"); if (TestMode) { LLVM.SetInitializer(thunkPointers, LLVM.ConstNull(LLVM.GetElementType(LLVM.TypeOf(thunkPointers)))); return; } ValueRef[] thunkPointersData = new ValueRef[PInvokeThunkCount]; for (int i = 0; i < PInvokeThunkCount; ++i) { thunkPointersData[i] = LLVM.AddGlobal(module, LLVM.GetElementType(intPtrLLVM), string.Format("thunk{0}", i)); } LLVM.SetInitializer(thunkPointers, LLVM.ConstArray(intPtrLLVM, thunkPointersData)); // Add TLS variable for storing current thunk ID var thunkCurrentId = LLVM.AddGlobal(module, int32LLVM, "ThunkCurrentId"); LLVM.SetThreadLocal(thunkCurrentId, true); var pinvokeThunks = PInvokeEmitThunks(); LLVM.SetModuleInlineAsm(module, pinvokeThunks.ToString()); }
public static LLVMValueRef EmitGlobal(LLVMModuleRef module, FieldDesc field, NameMangler nameMangler) { if (field.IsStatic) { if (s_staticFieldMapping.TryGetValue(field, out LLVMValueRef existingValue)) { return(existingValue); } else { var valueType = LLVM.ArrayType(LLVM.Int8Type(), (uint)field.FieldType.GetElementSize().AsInt); var llvmValue = LLVM.AddGlobal(module, valueType, nameMangler.GetMangledFieldName(field).ToString()); LLVM.SetLinkage(llvmValue, LLVMLinkage.LLVMInternalLinkage); LLVM.SetInitializer(llvmValue, GetConstZeroArray(field.FieldType.GetElementSize().AsInt)); if (field.IsThreadStatic) { LLVM.SetThreadLocal(llvmValue, LLVMMisc.True); } s_staticFieldMapping.Add(field, llvmValue); return(llvmValue); } } else { throw new NotImplementedException(); } }
private ValueRef CreateStringConstant(string str, bool utf16, bool nullTerminate) { ValueRef stringConstantData; // Create string data global if (utf16) { var utf16String = str.Select(x => LLVM.ConstInt(LLVM.Int16TypeInContext(context), x, false)); if (nullTerminate) { utf16String = utf16String.Concat(new[] { LLVM.ConstNull(LLVM.Int16TypeInContext(context)) }); } stringConstantData = LLVM.ConstArray(LLVM.Int16TypeInContext(context), utf16String.ToArray()); } else // utf8 { stringConstantData = LLVM.ConstStringInContext(context, str, (uint)str.Length, !nullTerminate); } var stringConstantDataGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(stringConstantData), ".str"); // Cast from i8-array to i8* LLVM.SetInitializer(stringConstantDataGlobal, stringConstantData); var zero = LLVM.ConstInt(int32Type, 0, false); stringConstantDataGlobal = LLVM.ConstInBoundsGEP(stringConstantDataGlobal, new[] { zero, zero }); return(stringConstantDataGlobal); }
public LlvmGlobal CreateGlobal(string identifier, LlvmType type) { // Create and wrap the global value reference. LlvmGlobal reference = new LlvmGlobal(LLVM.AddGlobal(this.reference, type.Unwrap(), identifier)); // Return the reference. return(reference); }
public LLVMValueRef GetGlobal(string name, LLVMTypeRef type) { if (_globals.ContainsKey(name)) { return(_globals[name]); } LLVMValueRef @ref = LLVM.AddGlobal(Module, type, name); _globals[name] = @ref; return(@ref); }
void CompileString(StringExpression expr) { var strlen = (uint)expr.Value.Length; var arr = LLVM.ArrayType(LLVM.Int8Type(), strlen + 1); var val = LLVM.AddGlobal(LLVMModule, arr, "string"); LLVM.SetLinkage(val, LLVMLinkage.LLVMInternalLinkage); LLVM.SetGlobalConstant(val, true); var str = LLVM.ConstString(expr.Value, strlen, false); LLVM.SetInitializer(val, str); Stack.Push(Symbol.CreateAnonymous(val, SpecialValue.String)); }
private void GenSymbol(Symbol sym) { if (sym?.Type == null) { throw new ArgumentException(); } var type = types.Get(sym.Type); var value = LLVM.AddGlobal(module, type, sym.Name.ID); // LLVM.SetInitializer(value, LLVM.ConstAllOnes(type)); symbols.Add(sym, value); }
public void PrepareAssembly(AssemblyDefinition assembly) { this.assembly = assembly; RegisterExternalTypes(); // Resolve corlib assembly corlib = assembly.MainModule.Import(typeof(void)).Resolve().Module.Assembly; // Prepare LLVM context, module and data layouts context = LLVM.GetGlobalContext(); module = LLVM.ModuleCreateWithName(assembly.Name.Name); // Prepare system types, for easier access InitializeCommonTypes(); // TODO: Choose appropriate triple depending on target var target = LLVM.GetTarget(runtimeModule); LLVM.SetTarget(module, target); // Initialize ABI abi = new DefaultABI(context, targetData); // Prepare LLVM builders builder = LLVM.CreateBuilderInContext(context); builder2 = LLVM.CreateBuilderInContext(context); builderAlloca = LLVM.CreateBuilderInContext(context); InitializeDebug(); if (!TestMode) { // Register SharpLangModule objects for each module metadataPerModule = new Dictionary <Mono.Cecil.ModuleDefinition, ValueRef>(); var mangledModuleName = Regex.Replace(assembly.Name.Name + ".sharplangmodule", @"(\W)", "_"); var sharpLangModuleGlobal = LLVM.AddGlobal(module, sharpLangModuleType.ObjectTypeLLVM, mangledModuleName); metadataPerModule[assembly.MainModule] = sharpLangModuleGlobal; // Generate extern globals for SharpLangModule instances of other modules foreach (var referencedAssembly in referencedAssemblies) { mangledModuleName = Regex.Replace(referencedAssembly.Name.Name + ".sharplangmodule", @"(\W)", "_"); var externalSharpLangModuleGlobal = LLVM.AddGlobal(module, sharpLangModuleType.ObjectTypeLLVM, mangledModuleName); LLVM.SetLinkage(externalSharpLangModuleGlobal, Linkage.ExternalLinkage); metadataPerModule[referencedAssembly.MainModule] = externalSharpLangModuleGlobal; } } }
/// <summary> /// Declares a static field. /// </summary> /// <param name="fieldDef">The static field definition</param> public void DeclareField(FieldDefinition fieldDef) { var globalType = typeLookup.GetLLVMTypeRef(fieldDef.FieldType); var globalValue = LLVM.AddGlobal(moduleRef, globalType, fieldDef.FullName); fieldMap.Add(fieldDef, globalValue); // TODO: optimize using InitialValue? /*System.Console.WriteLine(fieldDef.InitialValue); * foreach(byte b in fieldDef.InitialValue) * System.Console.WriteLine(b);*/ LLVM.SetInitializer(globalValue, LLVM.ConstNull(globalType)); }
public LLVMValueRef Emit(LLVMModuleRef context) { // Create the global variable. LLVMValueRef globalVar = LLVM.AddGlobal(context, Type.Emit(), Name); // Set the linkage to common. globalVar.SetLinkage(LLVMLinkage.LLVMCommonLinkage); // Assign value if applicable. if (Value != null) { globalVar.SetInitializer(Value.Emit()); } return(globalVar); }
public LLVMValueRef Emit(PipeContext <Module> context) { // Create the global variable. LLVMValueRef globalVar = LLVM.AddGlobal(context.Target.Target, this.Type.Emit(), this.Identifier); // Set the linkage to common. globalVar.SetLinkage(LLVMLinkage.LLVMCommonLinkage); // Assign value if applicable. if (this.Value != null) { globalVar.SetInitializer(this.Value.Emit()); } return(globalVar); }
private void EmitLdstr(List <StackValue> stack, string operand) { var stringType = GetType(corlib.MainModule.GetType(typeof(string).FullName)); // Create string data global var stringConstantData = LLVM.ConstStringInContext(context, operand, (uint)operand.Length, true); var stringConstantDataGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(stringConstantData), string.Empty); // Cast from i8-array to i8* LLVM.SetInitializer(stringConstantDataGlobal, stringConstantData); var zero = LLVM.ConstInt(int32Type, 0, false); stringConstantDataGlobal = LLVM.ConstInBoundsGEP(stringConstantDataGlobal, new[] { zero, zero }); // Create string var stringConstant = LLVM.ConstNamedStruct(stringType.DefaultType, new[] { LLVM.ConstIntToPtr(LLVM.ConstInt(int64Type, (ulong)operand.Length, false), intPtrType), stringConstantDataGlobal }); // Push on stack stack.Add(new StackValue(StackValueType.Value, stringType, stringConstant)); }
private LLVMValueRef GetTypeMetadataTable(IType type, ModuleBuilder module) { var name = "vtable_" + module.Mangler.Mangle(type, true); if (!typesWithMeta.Add(type)) { return(LLVM.GetNamedGlobal(module.Module, name)); } // Compose the vtable's contents. var entries = new List <LLVMValueRef>(); // A unique type tag. entries.Add(GetTypeTagValue(type, module)); // Virtual function addresses. foreach (var method in GetVTableLayout(type)) { if (method.IsAbstract()) { entries.Add(LLVM.ConstNull(LLVM.PointerType(module.GetFunctionPrototype(method), 0))); } else { entries.Add(module.DeclareMethod(method)); } } var metadataTableContents = LLVM.ConstStructInContext( module.Context, entries.ToArray(), false); var metadataTable = LLVM.AddGlobal(module.Module, metadataTableContents.TypeOf(), name); metadataTable.SetInitializer(metadataTableContents); metadataTable.SetLinkage(LLVMLinkage.LLVMInternalLinkage); metadataTable.SetGlobalConstant(true); return(metadataTable); }
public LLVMValueRef DefineStaticField(IField field) { LLVMValueRef result; if (fieldDecls.TryGetValue(field, out result)) { return(result); } if (!field.IsStatic) { throw new InvalidOperationException($"Cannot define non-static field '{field.FullName}' as a global."); } var type = ImportType(field.FieldType); var name = Mangler.Mangle(field, true); result = LLVM.AddGlobal(Module, type, name); result.SetInitializer(LLVM.ConstNull(type)); result.SetLinkage(GetLinkageForLocal(field)); fieldDecls[field] = result; return(result); }
/// <summary> /// Creates the LLVM types. /// </summary> private void createTypes() { string typeName = NameHelper.CreateTypeName(mType); foreach (KeyValuePair <TypeDefinition, Dictionary <string, int> > names in mNameTable) { string name = string.Format("vtable_{0}_part_{1}", typeName, NameHelper.CreateTypeName(names.Key)); Dictionary <string, int> idDict = names.Value; // Initialize to pointers. TypeRef[] types = new TypeRef[idDict.Count]; for (int i = 0; i < names.Value.Count; i++) { types[i] = TypeHelper.VoidPtr; } TypeRef type = LLVM.StructTypeInContext(mCompiler.ModuleContext, types, false); ValueRef global = LLVM.AddGlobal(mCompiler.Module, type, name); LLVM.SetLinkage(global, Linkage.PrivateLinkage); mGeneratedTable.Add(names.Key, new Tuple <TypeRef, ValueRef>(type, global)); } }
public override int VisitMemberVariableDeclarationStatement([NotNull] ClepsParser.MemberVariableDeclarationStatementContext context) { string className = String.Join(".", CurrentNamespaceAndClass); bool isStatic = context.STATIC() != null; string variableName = context.FieldName.Name.Text; if (ClassManager.DoesClassContainMember(className, variableName)) { string errorMessage = String.Format("Class {0} has multiple definitions of member {1}", className, variableName); Status.AddError(new CompilerError(FileName, context.Start.Line, context.Start.Column, errorMessage)); //Don't process this member return(-1); } ClepsParser.TypenameContext variableTypeContext = context.typename(); ClepsType clepsVariableType = ClepsType.GetBasicType(variableTypeContext); // only static members are defined immediately. member variables are defined at the at end of parsing a class if (isStatic) { string fullyQualifiedName = String.Format("{0}.{1}", className, variableName); LLVMTypeRef?llvmMemberType = ClepsLLVMTypeConvertorInst.GetLLVMTypeOrNull(clepsVariableType); if (llvmMemberType == null) { string errorMessage = String.Format("Type {0} was not found", clepsVariableType.GetTypeName()); Status.AddError(new CompilerError(FileName, context.Start.Line, context.Start.Column, errorMessage)); return(-1); } LLVM.AddGlobal(Module, llvmMemberType.Value, fullyQualifiedName); } ClassManager.AddNewMember(className, variableName, isStatic, clepsVariableType); return(0); }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class GetClass(Type type) { bool processClass = false; bool processFields = false; var typeReference = type.TypeReference; switch (typeReference.MetadataType) { case MetadataType.ByReference: case MetadataType.Void: case MetadataType.Pointer: // Should return something similar to IntPtr? return(null); 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: { processClass = true; processFields = true; break; } case MetadataType.Array: case MetadataType.String: case MetadataType.TypedByReference: case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process class and instance fields processClass = true; processFields = true; break; } default: throw new NotImplementedException(); } // Create class version (boxed version with VTable) var boxedType = type.ObjectType; var dataType = type.DataType; var valueType = type.ValueType; if (type.Class != null) { return(type.Class); } var @class = type.Class = new Class(type); if (processClass) { var baseType = GetBaseTypeDefinition(typeReference); var typeDefinition = GetMethodTypeDefinition(typeReference); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = baseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, baseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent virtual methods @class.VirtualTable.AddRange(parentClass.VirtualTable.TakeWhile(x => x.MethodReference.Resolve().IsVirtual)); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } @class.Depth = parentClass.Depth + 1; } if (typeReference is ArrayType) { var elementType = ResolveGenericsVisitor.Process(typeReference, ((ArrayType)typeReference).ElementType); // Array types implicitely inherits from IList<T>, ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T> and IEnumerable<T> foreach (var interfaceType in new[] { typeof(IList <>), typeof(ICollection <>), typeof(IReadOnlyCollection <>), typeof(IReadOnlyList <>), typeof(IEnumerable <>) }) { var @interfaceGeneric = corlib.MainModule.GetType(interfaceType.FullName); var @interface = @interfaceGeneric.MakeGenericInstanceType(elementType); @class.Interfaces.Add(GetClass(@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) PrepareClassMethods(type); 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), valueType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } else { // Get parent type RTTI var parentRuntimeTypeInfoType = parentClass != null ? LLVM.TypeOf(parentClass.GeneratedRuntimeTypeInfoGlobal) : intPtrType; // Build vtable var vtableType = LLVM.StructTypeInContext(context, @class.VirtualTable.Select(x => LLVM.TypeOf(x.GeneratedValue)).ToArray(), false); 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 } // 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 staticFieldsType = LLVM.StructTypeInContext(context, fieldTypes.ToArray(), false); fieldTypes.Clear(); // Reused for non-static fields after var runtimeTypeInfoType = LLVM.StructTypeInContext(context, new [] { parentRuntimeTypeInfoType, int32Type, int32Type, LLVM.PointerType(intPtrType, 0), LLVM.PointerType(intPtrType, 0), LLVM.Int1TypeInContext(context), LLVM.ArrayType(intPtrType, InterfaceMethodTableSize), vtableType, staticFieldsType, }, false); var runtimeTypeInfoGlobal = LLVM.AddGlobal(module, runtimeTypeInfoType, typeReference.MangledName() + ".rtti"); @class.GeneratedRuntimeTypeInfoGlobal = runtimeTypeInfoGlobal; if (@class.Type.IsLocal) { BuildRuntimeType(@class); } else { LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalWeakLinkage); } LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(runtimeTypeInfoGlobal), valueType }, false); } // Prepare class initializer if (@class.StaticCtor != null || typeDefinition.Methods.Any(x => x.HasPInvokeInfo)) { // void EnsureClassInitialized() // { // //lock (initMutex) < TODO: not implemented yet // { // if (!classInitialized) // { // InitializeClass(); // classInitialized = true; // } // } // } var initTypeFunction = LLVM.AddFunction(module, typeReference.MangledName() + "_inittype", LLVM.FunctionType(LLVM.VoidTypeInContext(context), new TypeRef[0], false)); var block = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.PositionBuilderAtEnd(builder2, block); // 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(builder2, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty); var classInitialized = LLVM.BuildLoad(builder2, classInitializedAddress, string.Empty); var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.BuildCondBr(builder2, classInitialized, nextBlock, typeNeedInitBlock); // Initialize class (first time) LLVM.PositionBuilderAtEnd(builder2, typeNeedInitBlock); // Static ctor if (@class.StaticCtor != null) { LLVM.BuildCall(builder2, @class.StaticCtor.GeneratedValue, new ValueRef[0], string.Empty); } // TODO: PInvoke initialization foreach (var pinvokeModule in typeDefinition.Methods.Where(x => x.HasPInvokeInfo).GroupBy(x => x.PInvokeInfo.Module)) { var libraryName = CreateStringConstant(pinvokeModule.Key.Name, false, true); var pinvokeLoadLibraryResult = LLVM.BuildCall(builder2, pinvokeLoadLibraryFunction, new[] { libraryName }, string.Empty); foreach (var method in pinvokeModule) { var entryPoint = CreateStringConstant(method.PInvokeInfo.EntryPoint, false, true); var pinvokeGetProcAddressResult = LLVM.BuildCall(builder2, pinvokeGetProcAddressFunction, new[] { pinvokeLoadLibraryResult, entryPoint, }, string.Empty); // TODO: Resolve method using generic context. indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32Type, (ulong)GetFunction(method).VirtualSlot, false), // Access specific vtable slot }; // Get vtable slot and cast to proper type var vtableSlot = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty); pinvokeGetProcAddressResult = LLVM.BuildPointerCast(builder2, pinvokeGetProcAddressResult, LLVM.GetElementType(LLVM.TypeOf(vtableSlot)), string.Empty); // Store value LLVM.BuildStore(builder2, pinvokeGetProcAddressResult, vtableSlot); } } // Set flag so that it won't be initialized again LLVM.BuildStore(builder2, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 1, false), classInitializedAddress); LLVM.BuildBr(builder2, nextBlock); LLVM.PositionBuilderAtEnd(builder2, nextBlock); LLVM.BuildRetVoid(builder2); @class.InitializeType = initTypeFunction; } // Sometime, GetType might already define DataType (for standard CLR types such as int, enum, string, array, etc...). // In that case, do not process fields. if (processFields && LLVM.GetTypeKind(valueType) == TypeKind.StructTypeKind && LLVM.IsOpaqueStruct(valueType)) { // Build actual type data (fields) // Add fields and vtable slots from parent class if (parentClass != null && type.StackType == StackValueType.Object) { fieldTypes.Add(parentClass.Type.DataType); } // Special cases: Array if (typeReference.MetadataType == MetadataType.Array) { // String: length (native int) + first element pointer var arrayType = (ArrayType)typeReference; var elementType = CreateType(arrayType.ElementType); fieldTypes.Add(intPtrType); fieldTypes.Add(LLVM.PointerType(elementType.DefaultType, 0)); } else { 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(valueType, fieldTypes.ToArray(), false); } } return(@class); }
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); }
public static void EmitObject(string objectFilePath, IEnumerable <DependencyNode> nodes, NodeFactory factory, WebAssemblyCodegenCompilation compilation, IObjectDumper dumper) { WebAssemblyObjectWriter objectWriter = new WebAssemblyObjectWriter(objectFilePath, factory, compilation); bool succeeded = false; try { //ObjectNodeSection managedCodeSection = null; var listOfOffsets = new List <int>(); foreach (DependencyNode depNode in nodes) { ObjectNode node = depNode as ObjectNode; if (node == null) { continue; } if (node.ShouldSkipEmittingObjectNode(factory)) { continue; } if (node is EETypeNode) { DefType t = ((EETypeNode)node).Type.GetClosestDefType(); int iSlot = GetVTableSlotsCount(factory, t.BaseType); var pointerSize = factory.Target.PointerSize; foreach (MethodDesc m in factory.VTable(t).Slots) { // set _getslot variable to sizeof(EEType) + iSlot * pointer size string realSymbolName = factory.NameMangler.GetMangledMethodName(m).ToString(); var globalRefName = "__getslot__" + realSymbolName; LLVMValueRef slot = LLVM.GetNamedGlobal(compilation.Module, globalRefName); if (slot.Pointer == IntPtr.Zero) { slot = LLVM.AddGlobal(compilation.Module, LLVM.Int32Type(), globalRefName); } LLVM.SetInitializer(slot, LLVM.ConstInt(LLVM.Int32Type(), (ulong)((EETypeNode.GetVTableOffset(pointerSize) / pointerSize) + iSlot), LLVMMisc.False)); LLVM.SetGlobalConstant(slot, LLVMMisc.True); iSlot++; } } objectWriter.StartObjectNode(node); ObjectData nodeContents = node.GetData(factory); if (dumper != null) { dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); } #if DEBUG foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) { try { _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); } catch (ArgumentException) { ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; Debug.Fail("Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); } } #endif ObjectNodeSection section = node.Section; if (objectWriter.ShouldShareSymbol(node)) { section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); } // Ensure section and alignment for the node. objectWriter.SetSection(section); objectWriter.EmitAlignment(nodeContents.Alignment); objectWriter.ResetByteRunInterruptionOffsets(nodeContents.Relocs); // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); Relocation[] relocs = nodeContents.Relocs; int nextRelocOffset = -1; int nextRelocIndex = -1; if (relocs.Length > 0) { nextRelocOffset = relocs[0].Offset; nextRelocIndex = 0; } int i = 0; listOfOffsets.Clear(); listOfOffsets.AddRange(objectWriter._byteInterruptionOffsets); int offsetIndex = 0; while (i < nodeContents.Data.Length) { // Emit symbol definitions if necessary objectWriter.EmitSymbolDefinition(i); if (i == nextRelocOffset) { Relocation reloc = relocs[nextRelocIndex]; long delta; unsafe { fixed(void *location = &nodeContents.Data[i]) { delta = Relocation.ReadValue(reloc.RelocType, location); } } int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); /* * WebAssembly has no thumb * // Emit a copy of original Thumb2 instruction that came from RyuJIT * if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || * reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) * { * unsafe * { * fixed (void* location = &nodeContents.Data[i]) * { * objectWriter.EmitBytes((IntPtr)location, size); * } * } * }*/ // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { nextRelocOffset = relocs[nextRelocIndex].Offset; } else { // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. nextRelocOffset = -1; } i += size; } else { while (offsetIndex < listOfOffsets.Count && listOfOffsets[offsetIndex] <= i) { offsetIndex++; } int nextOffset = offsetIndex == listOfOffsets.Count ? nodeContents.Data.Length : listOfOffsets[offsetIndex]; unsafe { // Todo: Use Span<T> instead once it's available to us in this repo fixed(byte *pContents = &nodeContents.Data[i]) { objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); i += nextOffset - i; } } } } Debug.Assert(i == nodeContents.Data.Length); // It is possible to have a symbol just after all of the data. objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); objectWriter.DoneObjectNode(); } succeeded = true; } finally { objectWriter.Dispose(); if (!succeeded) { // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished // object file around. try { File.Delete(objectFilePath); } catch { } } } }
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); }
/// <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); }
/// <summary> /// Compiles a type. /// </summary> /// <param name="type">The type.</param> public void Compile(TypeDefinition type) { TypeKind typeKind = getTypeKind(type); Logger.LogVerbose(typeKind.GetColor(), "Compiling type {0}", type.FullName); // Enums can be fully generated during the declaration pass. Nothing to do. if (typeKind == TypeKind.Enum) { return; } // Add interface type to lookup? if (typeKind == TypeKind.Interface) { mLookup.AddInterface(type); } // VTable. VTable vtable = null; bool hasVTable = ((typeKind == TypeKind.Interface) || (typeKind == TypeKind.Class && mCompiler.Lookup.NeedsVirtualCall(type))); if (hasVTable) { vtable = new VTable(mCompiler, type); mLookup.AddVTable(vtable); vtable.Create(); vtable.Compile(); } // Create struct for this type. TypeRef data = mLookup.GetTypeRef(type); List <TypeRef> structData = new List <TypeRef>(); List <IStructEntry> fields = mLookup.GetStructLayout(type); // Fields. ulong fieldTotalSize = 0; TypeDefinition currentType = type; foreach (IStructEntry entry in fields) { // VTable for a class? if (entry.EntryType == StructEntryType.ClassVTable) { // Only if there are virtual calls on this type. if (hasVTable) { VTableEntry castedEntry = (VTableEntry)entry; structData.Add(LLVM.PointerType(vtable.GetEntry(castedEntry.Type).Item1, 0)); } } // Entry that points to a table of VTables for interfaces else if (entry.EntryType == StructEntryType.InterfaceVTablesTable) { structData.Add(TypeHelper.VoidPtr); } // Field entry else /*if(entry.EntryType == StructEntryType.Field)*/ { FieldDefinition field = ((StructFieldEntry)entry).Field; TypeRef fieldType = TypeHelper.GetTypeRefFromType(field.FieldType); currentType = field.DeclaringType; // Static field. if (field.IsStatic) { // Only add it if we don't have it already (is possible when inheriting classes). if (!mLookup.HasStaticField(field)) { ValueRef val = LLVM.AddGlobal(mCompiler.Module, fieldType, NameHelper.CreateFieldName(field.FullName)); // Note: the initializer may be changed later if the compiler sees that it can be constant. LLVM.SetInitializer(val, LLVM.ConstNull(fieldType)); mLookup.AddStaticField(field, val); } } // Field for type instance. else { structData.Add(fieldType); fieldTotalSize += LLVM.SizeOfTypeInBits(mCompiler.TargetData, fieldType) / 8; } } } // Packing? bool packed = (type.PackingSize != -1); if (type.PackingSize != 1 && type.PackingSize != -1 && type.PackingSize != 0) { throw new NotImplementedException("The packing size " + type.PackingSize + " is not implemented"); } // Fixed size? if (type.ClassSize > 0 && (int)fieldTotalSize < type.ClassSize) { if (typeKind != TypeKind.Struct) { throw new InvalidOperationException("Fixed size not on a struct?!"); } // Note: we treat char as 8-bit. So we need to check if we're dealing with chars. int classSize = type.ClassSize; if (type.Fields.Count == 1 && type.Fields[0].FieldType.FullName == "System.Char") { classSize /= 2; } // Add bytes until the needed size is reached. int needed = classSize - (int)fieldTotalSize; for (int i = 0; i < needed; i++) { structData.Add(TypeHelper.Int8); } } // Set struct data. LLVM.StructSetBody(data, structData.ToArray(), packed); // For classes, generate the "newobj" method. if (typeKind == TypeKind.Class) { ValueRef newobjFunc = createNewobjMethod(type); mCompiler.Lookup.AddNewobjMethod(type, newobjFunc); } }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class GetClass(Type type) { bool processClass = false; bool processFields = false; var typeReference = type.TypeReferenceCecil; // Need complete type GetType(type.TypeReferenceCecil, TypeState.TypeComplete); switch (typeReference.MetadataType) { case MetadataType.ByReference: case MetadataType.Void: case MetadataType.Pointer: // Should return something similar to IntPtr? return(null); 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: { processClass = true; break; } case MetadataType.Array: case MetadataType.String: case MetadataType.TypedByReference: case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process class and instance fields processClass = true; break; } default: throw new NotImplementedException(); } // Create class version (boxed version with VTable) var boxedType = type.ObjectTypeLLVM; var valueType = type.ValueTypeLLVM; if (type.Class != null) { return(type.Class); } var @class = type.Class = new Class(type); if (processClass) { var baseType = GetBaseTypeDefinition(typeReference); var typeDefinition = GetMethodTypeDefinition(typeReference); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = baseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, baseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent virtual methods @class.VirtualTable.AddRange(parentClass.VirtualTable.TakeWhile(x => x.MethodReference.Resolve().IsVirtual)); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } @class.Depth = parentClass.Depth + 1; } if (typeReference is ArrayType) { var elementType = ResolveGenericsVisitor.Process(typeReference, ((ArrayType)typeReference).ElementType); // Array types implicitely inherits from IList<T>, ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T> and IEnumerable<T> foreach (var interfaceType in new[] { typeof(IList <>), typeof(ICollection <>), typeof(IReadOnlyCollection <>), typeof(IReadOnlyList <>), typeof(IEnumerable <>) }) { var @interfaceGeneric = corlib.MainModule.GetType(interfaceType.FullName); var @interface = @interfaceGeneric.MakeGenericInstanceType(elementType); @class.Interfaces.Add(GetClass(@interface)); } } if (baseType != null && baseType.FullName == typeof(MulticastDelegate).FullName) { // Add GetMulticastDispatchMethod runtime class on delegates var getMulticastDispatchMethod = new MethodDefinition("GetMulticastDispatchMethod", MethodAttributes.Private, intPtr.TypeReferenceCecil); getMulticastDispatchMethod.HasThis = true; getMulticastDispatchMethod.Attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final; getMulticastDispatchMethod.ImplAttributes = MethodImplAttributes.Runtime; typeDefinition.Methods.Add(getMulticastDispatchMethod); } // 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) PrepareClassMethods(type); { // Get parent type RTTI var parentRuntimeTypeInfoType = parentClass != null ? LLVM.TypeOf(parentClass.GeneratedEETypeRuntimeLLVM) : intPtrLLVM; var runtimeTypeInfoFields = new List <TypeRef> { parentRuntimeTypeInfoType, LLVM.Int8TypeInContext(context), // IsConcreteType typeDefLLVM, intPtrLLVM, sharpLangTypeType.DefaultTypeLLVM, // CachedType }; bool isConcreteType = IsConcreteType(@class.Type); // Create runtime type (still opaque struct) var runtimeTypeInfoType = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".rtti_type"); // Remove invalid characters so that we can easily link against it from C++ var mangledRttiName = Regex.Replace(typeReference.MangledName() + ".rtti", @"(\W)", "_"); var runtimeTypeInfoGlobal = LLVM.AddGlobal(module, runtimeTypeInfoType, mangledRttiName); if (typeDefinition.IsInterface) { // Interface use object as execution engine type @class.GeneratedEETypeRuntimeLLVM = GetClass(@object).GeneratedEETypeTokenLLVM; } else { @class.GeneratedEETypeRuntimeLLVM = runtimeTypeInfoGlobal; } @class.GeneratedEETypeTokenLLVM = runtimeTypeInfoGlobal; if (isConcreteType) { // Build vtable @class.VTableTypeLLVM = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".vtable"); LLVM.StructSetBody(@class.VTableTypeLLVM, @class.VirtualTable.Select(x => LLVM.PointerType(x.VirtualFunctionType, 0)).ToArray(), false); 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 } // Build static fields foreach (var field in typeDefinition.Fields) { if (!field.IsStatic) { continue; } var fieldType = GetType(ResolveGenericsVisitor.Process(typeReference, field.FieldType), TypeState.StackComplete); @class.StaticFields.Add(field, new Field(field, type, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultTypeLLVM); } var staticFieldsType = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".static"); LLVM.StructSetBody(staticFieldsType, fieldTypes.ToArray(), false); fieldTypes.Clear(); // Reused for non-static fields after runtimeTypeInfoFields.AddRange(new[] { int32LLVM, // SuperTypesCount int32LLVM, // InterfacesCount LLVM.PointerType(intPtrLLVM, 0), // SuperTypes LLVM.PointerType(intPtrLLVM, 0), // InterfaceMap LLVM.Int8TypeInContext(context), // TypeInitialized LLVM.Int32TypeInContext(context), // ObjectSize LLVM.Int32TypeInContext(context), // ElementSize LLVM.ArrayType(intPtrLLVM, InterfaceMethodTableSize), // IMT int32LLVM, // VTableSize @class.VTableTypeLLVM, // VTable staticFieldsType, // StaticFields }); } LLVM.StructSetBody(runtimeTypeInfoType, runtimeTypeInfoFields.ToArray(), false); LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(@class.GeneratedEETypeRuntimeLLVM), valueType }, false); if (@class.Type.IsLocal) { BuildRuntimeType(@class); } // Apply linkage LLVM.SetLinkage(runtimeTypeInfoGlobal, @class.Type.Linkage); } // Prepare class initializer if (@class.StaticCtor != null || typeDefinition.Methods.Any(x => x.HasPInvokeInfo)) { // void EnsureClassInitialized() // { // //lock (initMutex) < TODO: not implemented yet // { // if (!classInitialized) // { // classInitialized = true; // InitializeClass(); // } // } // } var initTypeFunction = LLVM.AddFunction(module, typeReference.MangledName() + "_inittype", LLVM.FunctionType(LLVM.VoidTypeInContext(context), new TypeRef[0], false)); // TODO: Temporarily emit it multiple time (once per assembly), that should be fixed! LLVM.SetLinkage(initTypeFunction, Linkage.LinkOnceAnyLinkage); var block = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.PositionBuilderAtEnd(builder2, block); // Check if class is initialized var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.TypeInitialized, false), // Type initialized flag }; var classInitializedAddress = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty); var classInitialized = LLVM.BuildLoad(builder2, classInitializedAddress, string.Empty); classInitialized = LLVM.BuildIntCast(builder2, classInitialized, LLVM.Int1TypeInContext(context), string.Empty); var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.BuildCondBr(builder2, classInitialized, nextBlock, typeNeedInitBlock); // Initialize class (first time) LLVM.PositionBuilderAtEnd(builder2, typeNeedInitBlock); // Set flag so that it won't be initialized again LLVM.BuildStore(builder2, LLVM.ConstInt(LLVM.Int8TypeInContext(context), 1, false), classInitializedAddress); // TODO: PInvoke initialization foreach (var pinvokeModule in typeDefinition.Methods.Where(x => x.HasPInvokeInfo).GroupBy(x => x.PInvokeInfo.Module)) { var libraryName = CreateStringConstant(pinvokeModule.Key.Name, false, true); var pinvokeLoadLibraryResult = LLVM.BuildCall(builder2, pinvokeLoadLibraryFunctionLLVM, new[] { libraryName }, string.Empty); foreach (var method in pinvokeModule) { var entryPoint = CreateStringConstant(method.PInvokeInfo.EntryPoint, false, true); var pinvokeGetProcAddressResult = LLVM.BuildCall(builder2, pinvokeGetProcAddressFunctionLLVM, new[] { pinvokeLoadLibraryResult, entryPoint, LLVM.ConstInt(int16.DataTypeLLVM, (ushort)method.PInvokeInfo.Attributes, false), }, string.Empty); // TODO: Resolve method using generic context. indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32LLVM, (ulong)GetFunction(method).VirtualSlot, false), // Access specific vtable slot }; // Get vtable slot and cast to proper type var vtableSlot = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty); pinvokeGetProcAddressResult = LLVM.BuildPointerCast(builder2, pinvokeGetProcAddressResult, LLVM.GetElementType(LLVM.TypeOf(vtableSlot)), string.Empty); // Store value LLVM.BuildStore(builder2, pinvokeGetProcAddressResult, vtableSlot); } } // Static ctor if (@class.StaticCtor != null) { LLVM.BuildCall(builder2, @class.StaticCtor.GeneratedValue, new ValueRef[0], string.Empty); } LLVM.BuildBr(builder2, nextBlock); LLVM.PositionBuilderAtEnd(builder2, nextBlock); LLVM.BuildRetVoid(builder2); @class.InitializeType = initTypeFunction; } } 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); }
/// <summary> /// Compiles the code for the VTable. /// </summary> public void Compile() { // Create the VTable structs for this type containing the method pointers. foreach (KeyValuePair <TypeDefinition, Tuple <TypeRef, ValueRef> > pair in mGeneratedTable) { Dictionary <int, MethodDefinition> lookup = mTable[pair.Key]; int i = 0; ValueRef[] values = new ValueRef[lookup.Count]; foreach (KeyValuePair <int, MethodDefinition> entry in lookup) { ValueRef?function = mLookup.GetFunction(NameHelper.CreateMethodName(entry.Value)); if (!function.HasValue) { throw new InvalidOperationException("Could not find function for: " + entry.Value); } values[i++] = LLVM.ConstPointerCast(function.Value, TypeHelper.VoidPtr); } ValueRef initialValues = LLVM.ConstStruct(values, false); LLVM.SetInitializer(pair.Value.Item2, initialValues); } // For each interface type, we want to create a table that links to the corresponding VTables. if (Type.HasInterfaces) { // Create arrays for the element and their types. // We know the values and types for the tables that are generated for this type. // However, there are spots that will be blank, so we need to fill these with "null". int count = mLookup.MaxInterfaceID; ValueRef[] elementValues = new ValueRef[count]; TypeRef[] elementTypes = new TypeRef[count]; // Fill default value for blank spots. ValueRef nullValue = LLVM.ConstNull(TypeHelper.VoidPtr); for (int i = 0; i < count; i++) { elementValues[i] = nullValue; elementTypes[i] = TypeHelper.VoidPtr; } // Fill in existing values TypeDefinition[] allInterfaces = TypeHelper.GetAllInterfaces(Type); foreach (TypeDefinition type in allInterfaces) { uint id = mLookup.GetInterfaceID(type); Tuple <TypeRef, ValueRef> tuple = mGeneratedTable[type]; elementValues[id] = tuple.Item2; elementTypes[id] = LLVM.PointerType(tuple.Item1, 0); } // Add a global for this table. TypeRef tableType = LLVM.StructTypeInContext(mCompiler.ModuleContext, elementTypes, false); ValueRef table = LLVM.ConstStruct(elementValues, false); ValueRef global = LLVM.AddGlobal(mCompiler.Module, tableType, string.Format("interface_vtable_table_{0}", Type.FullName)); LLVM.SetInitializer(global, table); LLVM.SetLinkage(global, Linkage.PrivateLinkage); // Add indirection table to lookup. ValueRef interfaceIndirectionTable = global; mLookup.AddInterfaceIndirectionTable(Type, interfaceIndirectionTable); } }
/// <summary> /// Emits an ldtoken instruction. /// </summary> /// <param name="instruction">The instruction.</param> /// <param name="context">The context.</param> /// <param name="builder">The builder.</param> public void Emit(Instruction instruction, MethodContext context, BuilderRef builder) { object token = instruction.Operand; if (token is FieldReference) { FieldReference fieldRef = (FieldReference)token; FieldDefinition fieldDef = fieldRef.Resolve(); // We only support array initialization by "System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray" currently. if (instruction.Next.OpCode.Code == Code.Call) { MethodReference methodRef = (MethodReference)instruction.Next.Operand; if (methodRef.Name == "InitializeArray" && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers") { // Note that the top value on the stack is currently the destination array. // So that means if we have a char[] on the top, we need to interpret the array differently because we treat char as 8-bit... StackElement top = context.CurrentStack.Peek(); bool isCharArray = (top.ILType.FullName == "System.Char[]"); int dstArrayLength = (isCharArray) ? (fieldDef.InitialValue.Length / 2) : (fieldDef.InitialValue.Length); ValueRef[] values = new ValueRef[dstArrayLength]; // CIL creates this array as a byte[]. if (isCharArray) { for (int i = 0; i < dstArrayLength; i++) { values[i] = LLVM.ConstInt(TypeHelper.Int8, fieldDef.InitialValue[i * 2], false); } } else { for (int i = 0; i < dstArrayLength; i++) { values[i] = LLVM.ConstInt(TypeHelper.Int8, fieldDef.InitialValue[i], false); } } TypeRef globalType = LLVM.ArrayType(TypeHelper.Int8, (uint)dstArrayLength); ValueRef global = LLVM.AddGlobal(context.Compiler.Module, globalType, "initarray"); LLVM.SetInitializer(global, LLVM.ConstArray(TypeHelper.Int8, values)); // Push the reference and the size. context.CurrentStack.Push(new StackElement(global, null, globalType)); ValueRef size = LLVM.ConstInt(TypeHelper.NativeIntType, (ulong)dstArrayLength, false); context.CurrentStack.Push(new StackElement(size, null, TypeHelper.NativeIntType)); } else { throw new NotImplementedException("Ldtoken: " + token.GetType() + " not implemented"); } } else { throw new NotImplementedException("Ldtoken: " + token.GetType() + " not implemented"); } } else { throw new NotImplementedException("Ldtoken: " + token.GetType() + " not implemented"); } }