public Field(FieldDefinition fieldDefinition, Class declaringClass, Type type, int structIndex) { FieldDefinition = fieldDefinition; DeclaringClass = declaringClass; Type = type; StructIndex = structIndex; }
private ValueRef BoxValueType(Class @class, StackValue valueType) { // Allocate object var allocatedObject = AllocateObject(@class.Type, StackValueType.Object); var dataPointer = GetDataPointer(allocatedObject); // Convert to local type var value = ConvertFromStackToLocal(@class.Type, valueType); // Copy data LLVM.BuildStore(builder, value, dataPointer); return allocatedObject; }
private ValueRef BoxValueType(Class @class, StackValue valueType) { // Allocate object var allocatedObject = AllocateObject(@class.Type, StackValueType.Object); var dataPointer = GetDataPointer(allocatedObject); // Convert to local type var value = ConvertFromStackToLocal(@class.Type, valueType); // Copy data var expectedPointerType = LLVM.PointerType(LLVM.TypeOf(value), 0); if (expectedPointerType != LLVM.TypeOf(dataPointer)) dataPointer = LLVM.BuildPointerCast(builder, dataPointer, expectedPointerType, string.Empty); LLVM.BuildStore(builder, value, dataPointer); return allocatedObject; }
private void EnsureClassInitialized(FunctionCompilerContext functionContext, Class @class) { // TODO: Add thread protection (with lock and actual class initialization in a separate method to improve code reuse) // If there was a type initializer, let's call it. // Even through the type initializer itself will check if type initialization is necessary inside a lock, // we do a quick check before here already (early exit). // if (!classInitialized) // { // EnsureClassInitialized(); // } // with: // void EnsureClassInitialized() // { // lock (initMutex) // { // if (!classInitialized) // { // InitializeClass(); // classInitialized = true; // } // } // } if (@class.InitializeType != ValueRef.Empty) { var functionGlobal = functionContext.Function.GeneratedValue; // Note: we temporarily ignore extern class // TODO: This will be reactivated as soon as we start linking multiple modules if ([email protected]) return; // Check if class is initialized var indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.TypeInitialized, false), // Type initialized flag }; var classInitializedAddress = LLVM.BuildInBoundsGEP(builder, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty); var classInitialized = LLVM.BuildLoad(builder, classInitializedAddress, string.Empty); var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); LLVM.BuildCondBr(builder, classInitialized, nextBlock, typeNeedInitBlock); // Initialize class (first time) LLVM.PositionBuilderAtEnd(builder, typeNeedInitBlock); LLVM.BuildCall(builder, @class.InitializeType, new ValueRef[0], string.Empty); // Set flag so that it won't be initialized again // Note: Inner function already does this, so commented out for now. // However, enabling it here might help LLVM performs additional optimization if happens multiple time in same function? (vs heavier code? need to test in practice) //LLVM.BuildStore(builder, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 1, false), classInitializedAddress); LLVM.BuildBr(builder, nextBlock); // Normal path LLVM.PositionBuilderAtEnd(builder, nextBlock); functionContext.BasicBlock = nextBlock; } }
private void EmitIsOrCastclass(FunctionCompilerContext functionContext, FunctionStack stack, Class @class, Code opcode, int instructionOffset) { var functionGlobal = functionContext.FunctionGlobal; var obj = stack.Pop(); // Force emission of class to be sure we have RTTI type generated // Another option would be to cast everything to object before querying RTTI object var objClass = GetClass(obj.Type); var currentBlock = LLVM.GetInsertBlock(builder); // Prepare basic blocks (for PHI instruction) var typeIsNotNullBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_not_null", instructionOffset)); var typeNotMatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_not_match", instructionOffset)); var typeCheckDoneBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_check_done", instructionOffset)); // Properly order block for easy LLVM bitcode reading LLVM.MoveBasicBlockAfter(typeIsNotNullBlock, currentBlock); LLVM.MoveBasicBlockAfter(typeNotMatchBlock, typeIsNotNullBlock); LLVM.MoveBasicBlockAfter(typeCheckDoneBlock, typeNotMatchBlock); var isObjNonNull = LLVM.BuildICmp(builder, IntPredicate.IntNE, obj.Value, LLVM.ConstPointerNull(LLVM.TypeOf(obj.Value)), string.Empty); LLVM.BuildCondBr(builder, isObjNonNull, typeIsNotNullBlock, opcode == Code.Castclass ? typeCheckDoneBlock : typeNotMatchBlock); LLVM.PositionBuilderAtEnd(builder, typeIsNotNullBlock); // Get RTTI pointer var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)ObjectFields.RuntimeTypeInfo, false), // Access RTTI }; var rttiPointer = LLVM.BuildInBoundsGEP(builder, obj.Value, indices, string.Empty); rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty); // castedPointerObject is valid only from typeCheckBlock var castedPointerType = LLVM.PointerType(@class.Type.ObjectTypeLLVM, 0); ValueRef castedPointerObject; BasicBlockRef typeCheckBlock; if (@class.Type.TypeReferenceCecil.Resolve().IsInterface) { // Cast as appropriate pointer type (for next PHI incoming if success) castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty); var inlineRuntimeTypeInfoType = LLVM.TypeOf(LLVM.GetParam(isInstInterfaceFunctionLLVM, 0)); var isInstInterfaceResult = LLVM.BuildCall(builder, isInstInterfaceFunctionLLVM, new[] { LLVM.BuildPointerCast(builder, rttiPointer, inlineRuntimeTypeInfoType, string.Empty), LLVM.BuildPointerCast(builder, @class.GeneratedEETypeTokenLLVM, inlineRuntimeTypeInfoType, string.Empty), }, string.Empty); LLVM.BuildCondBr(builder, isInstInterfaceResult, typeCheckDoneBlock, typeNotMatchBlock); typeCheckBlock = LLVM.GetInsertBlock(builder); } else { // TODO: Probably better to rewrite this in C, but need to make sure depth will be inlined as constant // Get super type count // Get method stored in IMT slot indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.SuperTypeCount, false), // Super type count }; typeCheckBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}_type_check", instructionOffset)); LLVM.MoveBasicBlockBefore(typeCheckBlock, typeNotMatchBlock); var superTypeCount = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); superTypeCount = LLVM.BuildLoad(builder, superTypeCount, string.Empty); var depthCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntSGE, superTypeCount, LLVM.ConstInt(int32LLVM, (ulong)@class.Depth, false), string.Empty); LLVM.BuildCondBr(builder, depthCompareResult, typeCheckBlock, typeNotMatchBlock); // Start new typeCheckBlock LLVM.PositionBuilderAtEnd(builder, typeCheckBlock); // Get super types indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.SuperTypes, false), // Super types }; var superTypes = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); superTypes = LLVM.BuildLoad(builder, superTypes, string.Empty); // Get actual super type indices = new[] { LLVM.ConstInt(int32LLVM, (ulong)@class.Depth, false), // Pointer indirection }; var superType = LLVM.BuildGEP(builder, superTypes, indices, string.Empty); superType = LLVM.BuildLoad(builder, superType, string.Empty); // Cast as appropriate pointer type (for next PHI incoming if success) castedPointerObject = LLVM.BuildPointerCast(builder, obj.Value, castedPointerType, string.Empty); // Compare super type in array at given depth with expected one var typeCompareResult = LLVM.BuildICmp(builder, IntPredicate.IntEQ, superType, LLVM.ConstPointerCast(@class.GeneratedEETypeRuntimeLLVM, intPtrLLVM), string.Empty); LLVM.BuildCondBr(builder, typeCompareResult, typeCheckDoneBlock, typeNotMatchBlock); } // Start new typeNotMatchBlock: set object to null and jump to typeCheckDoneBlock LLVM.PositionBuilderAtEnd(builder, typeNotMatchBlock); if (opcode == Code.Castclass) { // Create InvalidCastException object var invalidCastExceptionClass = GetClass(corlib.MainModule.GetType(typeof(InvalidCastException).FullName)); EmitNewobj(functionContext, invalidCastExceptionClass.Type, invalidCastExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0)); var invalidCastException = stack.Pop(); GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, invalidCastException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)}); LLVM.BuildUnreachable(builder); } else { LLVM.BuildBr(builder, typeCheckDoneBlock); } // Start new typeCheckDoneBlock LLVM.PositionBuilderAtEnd(builder, typeCheckDoneBlock); functionContext.BasicBlock = typeCheckDoneBlock; // Put back with appropriate type at end of stack ValueRef mergedVariable; if (opcode == Code.Castclass) { mergedVariable = LLVM.BuildPhi(builder, castedPointerType, string.Empty); LLVM.AddIncoming(mergedVariable, new[] { castedPointerObject, LLVM.ConstPointerNull(castedPointerType) }, new[] { typeCheckBlock, currentBlock }); } else { mergedVariable = LLVM.BuildPhi(builder, castedPointerType, string.Empty); LLVM.AddIncoming(mergedVariable, new[] {castedPointerObject, LLVM.ConstPointerNull(castedPointerType)}, new[] {typeCheckBlock, typeNotMatchBlock}); } stack.Add(new StackValue(obj.StackType, @class.Type, mergedVariable)); }
private ValueRef SetupVTableConstant(ValueRef @object, Class @class) { return LLVM.ConstInsertValue(@object, @class.GeneratedEETypeRuntimeLLVM, new [] { (uint)ObjectFields.RuntimeTypeInfo }); }
private void SetupVTable(ValueRef @object, Class @class) { // Store vtable global into first field of the object var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)ObjectFields.RuntimeTypeInfo, false), // Access RTTI }; var vtablePointer = LLVM.BuildInBoundsGEP(builder, @object, indices, string.Empty); LLVM.BuildStore(builder, @class.GeneratedEETypeRuntimeLLVM, vtablePointer); }
private ValueRef CreateInvokeMethodHelper(Class declaringClass, string nameSuffix) { // Reuse same signature as Invoke var invokeMethod = declaringClass.Functions.Single(x => x.MethodReference.Name == "Invoke"); // Create method var invokeMethodHelper = LLVM.AddFunction(module, LLVM.GetValueName(invokeMethod.GeneratedValue) + nameSuffix, invokeMethod.FunctionType); ApplyFunctionAttributes(invokeMethod.Signature, invokeMethodHelper); LLVM.PositionBuilderAtEnd(builder2, LLVM.AppendBasicBlockInContext(context, invokeMethodHelper, string.Empty)); return invokeMethodHelper; }
private ValueRef GenerateStaticInvokeThunk(Class declaringClass) { var invokeMethodHelper = CreateInvokeMethodHelper(declaringClass, "_StaticHelper"); LLVM.SetLinkage(invokeMethodHelper, declaringClass.Type.Linkage); EmitStaticInvokeCall(invokeMethodHelper); return invokeMethodHelper; }
private ValueRef GenerateMulticastInvokeThunk(Class declaringClass) { // Reuse same signature as Invoke var delegateType = corlib.MainModule.GetType(typeof(Delegate).FullName); var invokeMethod = declaringClass.Functions.Single(x => x.MethodReference.Name == "Invoke"); var invokeMethodHelper = LLVM.AddFunction(module, LLVM.GetValueName(invokeMethod.GeneratedValue) + "_MulticastHelper", invokeMethod.FunctionType); ApplyFunctionAttributes(invokeMethod.Signature, invokeMethodHelper); LLVM.SetLinkage(invokeMethodHelper, declaringClass.Type.Linkage); LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, invokeMethodHelper, string.Empty)); var invokeFunctionType = LLVM.GetElementType(LLVM.TypeOf(invokeMethodHelper)); bool hasRetValue = LLVM.GetReturnType(invokeFunctionType) != LLVM.VoidTypeInContext(context); var delegateArrayType = GetType(new ArrayType(delegateType), TypeState.TypeComplete); // Prepare basic blocks var forCodeBlock = LLVM.AppendBasicBlockInContext(context, invokeMethodHelper, string.Empty); var exitBlock = LLVM.AppendBasicBlockInContext(context, invokeMethodHelper, string.Empty); var stack = new FunctionStack(); // Load first argument and cast as Delegate[] var @this = LLVM.GetParam(invokeMethodHelper, 0); @this = LLVM.BuildPointerCast(builder, @this, delegateArrayType.DefaultTypeLLVM, string.Empty); // Create index (i = 0) var locals = new List<StackValue>(); locals.Add(new StackValue(StackValueType.Int32, int32, LLVM.BuildAlloca(builder, int32.DefaultTypeLLVM, "i"))); EmitI4(stack, 0); EmitStloc(stack, locals, 0); // length = invocationList.Length var delegateArray = new StackValue(StackValueType.Object, delegateArrayType, @this); stack.Add(delegateArray); EmitLdlen(stack); EmitConv(stack, Code.Conv_I4); var invocationCount = stack.Pop(); // Iterate over each element in array LLVM.BuildBr(builder, forCodeBlock); LLVM.PositionBuilderAtEnd(builder, forCodeBlock); // Get delegateArray[i] stack.Add(delegateArray); EmitLdloc(stack, locals, 0); EmitLdelem(stack); // Call var helperArgs = new ValueRef[LLVM.CountParams(invokeMethodHelper)]; var thisIndex = invokeMethod.Signature.GetParameterIndexForThis(); helperArgs[thisIndex] = LLVM.BuildPointerCast(builder, stack.Pop().Value, declaringClass.Type.DefaultTypeLLVM, string.Empty); for (int i = 0; i < helperArgs.Length; ++i) { if (i == thisIndex) continue; helperArgs[i] = LLVM.GetParam(invokeMethodHelper, (uint)i); } var retValue = LLVM.BuildCall(builder, invokeMethod.GeneratedValue, helperArgs, string.Empty); ApplyCallAttributes(invokeMethod.Signature, retValue); // i++ EmitLdloc(stack, locals, 0); var lastStack = stack[stack.Count - 1]; var incrementedValue = LLVM.BuildAdd(builder, lastStack.Value, LLVM.ConstInt(int32LLVM, 1, false), string.Empty); lastStack = new StackValue(StackValueType.Int32, int32, incrementedValue); stack[stack.Count - 1] = lastStack; EmitStloc(stack, locals, 0); // if (i < length) // goto forCodeBlock // else // return lastReturnValue; EmitLdloc(stack, locals, 0); stack.Add(invocationCount); EmitConditionalBranch(stack, forCodeBlock, exitBlock, Code.Blt_S); LLVM.PositionBuilderAtEnd(builder, exitBlock); // Return value if (hasRetValue) LLVM.BuildRet(builder, retValue); else LLVM.BuildRetVoid(builder); return invokeMethodHelper; }
private MethodBody GenerateDelegateMethod(MethodDefinition method, Class declaringClass) { // Delegate type var delegateType = corlib.MainModule.GetType(typeof(Delegate).FullName); // Delegate fields var targetField = delegateType.Fields.First(x => x.Name == "_target"); var methodPtrField = delegateType.Fields.First(x => x.Name == "_methodPtr"); var methodPtrAuxField = delegateType.Fields.First(x => x.Name == "_methodPtrAux"); var body = new MethodBody(method); var il = body.GetILProcessor(); if (method.Name == ".ctor") { // Mark //GenerateMulticastInvokeThunk(declaringClass); // Two main cases: // - Instance method: // this._methodPtr = fnptr; // this._target = target; // Result: this._methodPtr(this._target, arg1, ..) will directly work // - Static method: // this._target = this; // this._methodPtrAux = fnptr; // this._methodPtr = (delegate, arg1, ...) => { delegate->_methodPtrAux(arg1, ...); } // Result: this._methodPtr(this._target, arg1, ...) will call thunk, // which will call fnptr (from this._target._methodPtr) without the first argument var target = Instruction.Create(OpCodes.Ldarg_0); // if (target == null) // { il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[0])); il.Append(Instruction.Create(OpCodes.Brtrue, target)); // Generate thunk (for now, done using direct LLVM, not sure weither LLVM or IL is better) // this._methodPtr = (delegate, arg1, ...) => { delegate->_methodPtrAux(arg1, ...); } var invokeMethodHelper = GenerateStaticInvokeThunk(declaringClass); // Fake Nop to push this thunk on stack (TODO: Better way to do this? i.e. store it in some static field?) il.Append(Instruction.Create(OpCodes.Ldarg_0)); var loadFunctionPointerInstruction = Instruction.Create(OpCodes.Nop); InstructionActions.Add(loadFunctionPointerInstruction, (stack) => { // Push the generated method pointer on the stack stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, invokeMethodHelper, intPtrLLVM, string.Empty))); }); il.Append(loadFunctionPointerInstruction); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrField)); // this._methodPtrAux = method; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[1])); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrAuxField)); // this._target = this; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Stfld, targetField)); // return; // } il.Append(Instruction.Create(OpCodes.Ret)); // this._target = target; il.Append(target); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[0])); il.Append(Instruction.Create(OpCodes.Stfld, targetField)); // this._methodPtr = method; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[1])); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrField)); // return; il.Append(Instruction.Create(OpCodes.Ret)); } else if (method.Name == "GetMulticastDispatchMethod") { var invokeMethodHelper = GenerateMulticastInvokeThunk(declaringClass); var loadFunctionPointerInstruction = Instruction.Create(OpCodes.Nop); InstructionActions.Add(loadFunctionPointerInstruction, (stack) => { // Push the generated method pointer on the stack stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, invokeMethodHelper, intPtrLLVM, string.Empty))); }); il.Append(loadFunctionPointerInstruction); il.Append(Instruction.Create(OpCodes.Ret)); } else if (method.Name == "Invoke") { // For now, generate IL // Note that we could probably optimize at callsite too, // but probably not necessary if LLVM and sealed class are optimized/inlined well enough // ldarg_0 // ldfld _target il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldfld, targetField)); var callsite = new CallSite(method.ReturnType); callsite.Parameters.Add(new ParameterDefinition(targetField.FieldType)); foreach (var parameter in method.Parameters) { callsite.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, parameter.ParameterType)); // ldarg il.Append(Instruction.Create(OpCodes.Ldarg, parameter)); } // ldarg_0 // ldfld _methodPtr il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldfld, methodPtrField)); // calli il.Append(Instruction.Create(OpCodes.Calli, callsite)); // ret il.Append(Instruction.Create(OpCodes.Ret)); } else { LLVM.BuildUnreachable(builder); return null; } return body; }
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); }
private DIDescriptor GetOrCreateDebugClass(Class @class) { DIDescriptor debugClass; if (debugClasses.TryGetValue(@class, out debugClass)) return debugClass; var type = @class.Type; // Find namespace scope var debugNamespace = GetOrCreateDebugNamespace(type.TypeReferenceCecil.Namespace); // Create debug version of the class var structType = type.StackType == StackValueType.Object ? type.ObjectTypeLLVM : type.ValueTypeLLVM; var size = LLVM.ABISizeOfType(targetData, structType) * 8; var align = LLVM.ABIAlignmentOfType(targetData, structType) * 8; var emptyArray = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new DIDescriptor[0]); bool isLocal = type.IsLocal; if (isLocal) { var parentClass = @class.BaseType; var parentDebugClass = parentClass != null ? GetOrCreateDebugClass(parentClass) : DIDescriptor.Empty; debugClass = LLVM.DIBuilderCreateClassType(debugBuilder, debugNamespace, type.TypeReferenceCecil.Name, DIDescriptor.Empty, 0, size, align, 0, 0, parentDebugClass, emptyArray, DIDescriptor.Empty, DIDescriptor.Empty, type.TypeReferenceCecil.FullName); } else { debugClass = LLVM.DIBuilderCreateForwardDecl(debugBuilder, (int)DW_TAG.class_type, type.TypeReferenceCecil.Name, debugNamespace, DIDescriptor.Empty, 0, 0, size, align, type.TypeReferenceCecil.FullName); } debugClasses.Add(@class, debugClass); if (isLocal) { debugClassesToProcess.Enqueue(new KeyValuePair<Class, DIDescriptor>(@class, debugClass)); } return debugClass; }
private ValueRef GenerateStaticInvokeThunk(Class declaringClass, TypeDefinition delegateType, FieldDefinition methodPtrAuxField) { // Reuse same signature as Invoke var invokeMethod = declaringClass.Functions.Single(x => x.MethodReference.Name == "Invoke"); // Create method var invokeMethodHelper = LLVM.AddFunction(module, LLVM.GetValueName(invokeMethod.GeneratedValue) + "_Helper", invokeMethod.FunctionType); LLVM.PositionBuilderAtEnd(builder2, LLVM.AppendBasicBlockInContext(context, invokeMethodHelper, string.Empty)); // Ignore first arguments var helperArgs = new ValueRef[LLVM.CountParams(invokeMethodHelper) - 1]; var helperArgTypes = new TypeRef[helperArgs.Length]; for (int i = 0; i < helperArgs.Length; ++i) { helperArgs[i] = LLVM.GetParam(invokeMethodHelper, (uint) i + 1); helperArgTypes[i] = LLVM.TypeOf(helperArgs[i]); } var helperFunctionType = LLVM.FunctionType(LLVM.GetReturnType(invokeMethod.FunctionType), helperArgTypes, false); // 1. Load static function pointers (arg0->_methodPtrAux) var @this = LLVM.GetParam(invokeMethodHelper, 0); var indices = BuildFieldIndices(GetClass(delegateType).Fields[methodPtrAuxField], StackValueType.Object, GetType(declaringClass.Type.TypeReference)); // Find field address using GEP var fieldAddress = LLVM.BuildInBoundsGEP(builder2, @this, indices, string.Empty); // Load value from field var methodPtrAux = LLVM.BuildLoad(builder2, fieldAddress, string.Empty); methodPtrAux = LLVM.BuildPointerCast(builder2, methodPtrAux, LLVM.PointerType(helperFunctionType, 0), string.Empty); // 2. Call method var methodPtrAuxCall = LLVM.BuildCall(builder2, methodPtrAux, helperArgs, string.Empty); // Return value if (LLVM.GetReturnType(invokeMethod.FunctionType) != LLVM.VoidTypeInContext(context)) LLVM.BuildRet(builder2, methodPtrAuxCall); else LLVM.BuildRetVoid(builder2); return invokeMethodHelper; }