예제 #1
0
파일: Field.cs 프로젝트: frje/SharpLang
 public Field(FieldDefinition fieldDefinition, Class declaringClass, Type type, int structIndex)
 {
     FieldDefinition = fieldDefinition;
     DeclaringClass = declaringClass;
     Type = type;
     StructIndex = structIndex;
 }
예제 #2
0
        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;
        }
예제 #3
0
        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;
        }
예제 #4
0
        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;
            }
        }
예제 #5
0
        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));
        }
예제 #6
0
 private ValueRef SetupVTableConstant(ValueRef @object, Class @class)
 {
     return LLVM.ConstInsertValue(@object, @class.GeneratedEETypeRuntimeLLVM, new [] { (uint)ObjectFields.RuntimeTypeInfo });
 }
예제 #7
0
        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);
        }
예제 #8
0
        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;
        }
예제 #9
0
        private ValueRef GenerateStaticInvokeThunk(Class declaringClass)
        {
            var invokeMethodHelper = CreateInvokeMethodHelper(declaringClass, "_StaticHelper");
            LLVM.SetLinkage(invokeMethodHelper, declaringClass.Type.Linkage);

            EmitStaticInvokeCall(invokeMethodHelper);

            return invokeMethodHelper;
        }
예제 #10
0
        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;
        }
예제 #11
0
        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;
        }
예제 #12
0
        private void BuildRuntimeType(Class @class)
        {
            if (@class.IsEmitted)
                return;

            @class.IsEmitted = true;

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

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

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

                            resolvedFunction = GetFunction(matchingMethod);

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

            // Mark RTTI as external
            LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage);
        }
예제 #13
0
        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;
        }
예제 #14
0
        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;
        }