/// <summary> /// Creates the function. /// </summary> /// <param name="method">The method.</param> /// <returns></returns> Function CreateFunction(MethodReference method) { Function function; if (functions.TryGetValue(method, out function)) return function; var resolvedMethod = method.Resolve(); var declaringType = GetType(ResolveGenericsVisitor.Process(method, method.DeclaringType), TypeState.Opaque); // Check if method is only defined in a parent class (can happen in some rare case, i.e. PCL TypeInfo.get_Assembly()). bool hasMatch = MetadataResolver.GetMethod(declaringType.TypeDefinitionCecil.Methods, method.GetElementMethod()) != null; if (resolvedMethod != null && !hasMatch) { var parentType = declaringType.TypeDefinitionCecil.BaseType != null ? ResolveGenericsVisitor.Process(declaringType.TypeReferenceCecil, declaringType.TypeDefinitionCecil.BaseType) : null; if (parentType == null) throw new InvalidOperationException(string.Format("Could not find a matching method in any of the type or its parent for {0}", method)); // Create function with parent type // TODO: Maybe we need to replace generic context with parent type? var parentMethod = method.ChangeDeclaringType(parentType); function = CreateFunction(parentMethod); // Register it so that it can be cached functions.Add(method, function); return function; } var returnType = GetType(ResolveGenericsVisitor.Process(method, method.ReturnType), TypeState.StackComplete); var parameterTypesBuilder = new List<Type>(); if (method.HasThis) { var parameterType = declaringType.TypeReferenceCecil; // Value type uses ByReference type for this if (declaringType.TypeDefinitionCecil.IsValueType) parameterType = parameterType.MakeByReferenceType(); parameterTypesBuilder.Add(GetType(parameterType, TypeState.StackComplete)); } foreach (var parameter in method.Parameters) { parameterTypesBuilder.Add(GetType(ResolveGenericsVisitor.Process(method, parameter.ParameterType), TypeState.StackComplete)); } var parameterTypes = parameterTypesBuilder.ToArray(); // Find calling convention var callingConvention = method.CallingConvention; PInvokeInfo pinvokeInfo = null; if (resolvedMethod != null && resolvedMethod.HasPInvokeInfo) { pinvokeInfo = resolvedMethod.PInvokeInfo; if (resolvedMethod.PInvokeInfo.IsCallConvStdCall || resolvedMethod.PInvokeInfo.IsCallConvWinapi) callingConvention = MethodCallingConvention.StdCall; else if (resolvedMethod.PInvokeInfo.IsCallConvFastcall) callingConvention = MethodCallingConvention.FastCall; } var functionSignature = new FunctionSignature(abi, returnType, parameterTypes, callingConvention, pinvokeInfo); var functionType = CreateFunctionTypeLLVM(functionSignature); // If we have an external with generic parameters, let's try to do some generic sharing (we can write only one in C++) bool isInternal = resolvedMethod != null && ((resolvedMethod.ImplAttributes & MethodImplAttributes.InternalCall) != 0); if (isInternal && resolvedMethod.HasGenericParameters && resolvedMethod.GenericParameters.All(x => x.HasReferenceTypeConstraint)) { // Check if this isn't the shareable method (in which case we should do normal processing) if (!((GenericInstanceMethod)method).GenericArguments.All(x => MemberEqualityComparer.Default.Equals(x, @object.TypeReferenceCecil))) { // Let's share it with default method var sharedGenericInstance = new GenericInstanceMethod(resolvedMethod); foreach (var genericParameter in resolvedMethod.GenericParameters) { sharedGenericInstance.GenericArguments.Add(@object.TypeReferenceCecil); } var sharedMethod = GetFunction(sharedGenericInstance); // Cast shared function to appropriate pointer type var sharedFunctionGlobal = LLVM.ConstPointerCast(sharedMethod.GeneratedValue, LLVM.PointerType(functionType, 0)); function = new Function(declaringType, method, functionType, sharedFunctionGlobal, functionSignature); functions.Add(method, function); return function; } } // Determine if type and function is local, and linkage type bool isLocal; var linkageType = GetLinkageType(method.DeclaringType, out isLocal); if (isInternal) { // Should be switched to non-weak when we have complete implementation of every internal calls linkageType = Linkage.ExternalWeakLinkage; } else if (resolvedMethod != null && resolvedMethod.HasGenericParameters) { isLocal = true; linkageType = Linkage.LinkOnceAnyLinkage; } bool isRuntime = resolvedMethod != null && ((resolvedMethod.ImplAttributes & MethodImplAttributes.Runtime) != 0); bool isInterfaceMethod = declaringType.TypeDefinitionCecil.IsInterface; var hasDefinition = resolvedMethod != null && (resolvedMethod.HasBody || isInternal || isRuntime); var methodMangledName = Regex.Replace(method.MangledName(), @"(\W)", "_"); var functionGlobal = hasDefinition ? LLVM.AddFunction(module, methodMangledName, functionType) : LLVM.ConstPointerNull(LLVM.PointerType(functionType, 0)); // Interface method uses a global so that we can have a unique pointer to use as IMT key if (isInterfaceMethod) { // For test code only: Use linkonce instead of linkageType so that we know if type was forced if (TestMode) { isLocal = true; linkageType = Linkage.LinkOnceAnyLinkage; } functionGlobal = LLVM.AddGlobal(module, LLVM.Int8TypeInContext(context), methodMangledName); if (isLocal) LLVM.SetInitializer(functionGlobal, LLVM.ConstNull(LLVM.Int8TypeInContext(context))); LLVM.SetLinkage(functionGlobal, linkageType); } if (hasDefinition) { ApplyFunctionAttributes(functionSignature, functionGlobal); } function = new Function(declaringType, method, functionType, functionGlobal, functionSignature); functions.Add(method, function); if (hasDefinition) { switch (callingConvention) { case MethodCallingConvention.StdCall: LLVM.SetFunctionCallConv(functionGlobal, (uint)CallConv.X86StdcallCallConv); break; case MethodCallingConvention.FastCall: LLVM.SetFunctionCallConv(functionGlobal, (uint)CallConv.X86FastcallCallConv); break; } if (isLocal && !isInternal) { // Need to compile EmitFunction(function); } // Apply linkage LLVM.SetLinkage(functionGlobal, linkageType); } return function; }