/// <summary> /// Emits a newobj 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) { MethodReference ctor = (MethodReference)instruction.Operand; TypeDefinition objType = ctor.DeclaringType.Resolve(); TypeRef type = TypeHelper.GetTypeRefFromType(ctor.DeclaringType); ValueRef objPtr; bool ptr = (objType.IsClass && !objType.IsValueType); if (ptr) { // This type is a class, therefor we have a specialised "newobj" method. objPtr = LLVM.BuildCall(builder, context.Compiler.Lookup.GetNewobjMethod(ctor.DeclaringType.Resolve()), new ValueRef[0], "newobj"); } else { // Not a class, allocate on stack. objPtr = LLVM.BuildAlloca(builder, type, "newobj"); } // Constructor. ValueRef?ctorFunc = context.Compiler.Lookup.GetFunction(NameHelper.CreateMethodName(ctor)); if (!ctorFunc.HasValue) { throw new Exception("Constructor not found: " + ctor); } // Get .ctor parameters. int paramCount = 1 + ctor.Parameters.Count; ValueRef[] values = new ValueRef[paramCount]; values[0] = objPtr; for (int i = paramCount - 1; i >= 1; i--) { StackElement element = context.CurrentStack.Pop(); values[i] = element.Value; // Cast needed? TypeRef paramType = TypeHelper.GetTypeRefFromType(ctor.Parameters[i - 1].ParameterType); if (element.Type != paramType) { CastHelper.HelpIntAndPtrCast(builder, ref values[i], ref element.Type, paramType, "ctorcallcast"); } } // Call .ctor. LLVM.BuildCall(builder, ctorFunc.Value, values, string.Empty); // Load and push object on stack. ValueRef obj = (ptr) ? objPtr : LLVM.BuildLoad(builder, objPtr, "obj"); context.CurrentStack.Push(new StackElement(obj, ctor.DeclaringType, type)); }
/// <summary> /// Compiles a method. /// </summary> /// <param name="methodDef">The method definition.</param> public void Compile(MethodDefinition methodDef) { string methodName = NameHelper.CreateMethodName(methodDef); ValueRef?function = mCompiler.Lookup.GetFunction(methodName); // It is possible we didn't create a declaration because we don't want to generate this method. // In that case, don't continue. if (!function.HasValue) { return; } // A method has internal linkage if one (or both) of the following is true: // 1) The method is a private method. // 2) The compiler got an option to set the linkage of instance methods to internal. if (!methodDef.IsStatic && (methodDef.IsPrivate || mCompiler.Options.InstanceMethodInternalLinkage)) { LLVM.SetLinkage(function.Value, Linkage.InternalLinkage); if (mCompiler.Options.InternalMethodsFastCC) { LLVM.SetFunctionCallConv(function.Value, 8); } } // Only generate if it has a body. if (!methodDef.HasBody || methodDef.Body.CodeSize == 0) { LLVM.SetLinkage(function.Value, Linkage.ExternalLinkage); return; } // Compile instructions. MethodContext ctx = new MethodContext(mCompiler, methodDef, function.Value); InstructionEmitter emitter = new InstructionEmitter(ctx); try { emitter.EmitInstructions(mCompiler.CodeGen); mCompiler.VerifyAndOptimizeFunction(function.Value); } catch (Exception e) { Logger.LogError("Exception inside method {0}: {1}", methodDef, e.Message); Logger.LogDetail("----------"); Logger.LogInfo(e.StackTrace); } }
/// <summary> /// Creates the declaration for a method. /// </summary> /// <param name="methodDef">The method definition.</param> public void CreateDeclaration(MethodDefinition methodDef) { string methodName = NameHelper.CreateMethodName(methodDef); int paramCount = methodDef.Parameters.Count; // CIL runtime method? if (methodDef.IsRuntime && !mRuntimeCompiler.MustHandleMethod(methodDef)) { return; } // If we expect an instance reference as first argument, then we need to make sure our for loop has an offset. int offset = 0; if (methodDef.HasThis) { paramCount++; offset = 1; } // Fill in arguments. TypeRef[] argTypes = new TypeRef[paramCount]; for (int i = offset; i < paramCount; i++) { TypeReference type = methodDef.Parameters[i - offset].ParameterType; argTypes[i] = TypeHelper.GetTypeRefFromType(type); } // If needed, fill in the instance reference. if (methodDef.HasThis) { argTypes[0] = TypeHelper.GetTypeRefFromType(methodDef.DeclaringType); // We need to pass the valuetype as a pointer because we need to modify its contents in the constructor. if (methodDef.DeclaringType.IsValueType) { argTypes[0] = LLVM.PointerType(argTypes[0], 0); } } // Create LLVM function type and add function to the lookup table. TypeRef functionType = LLVM.FunctionType(TypeHelper.GetTypeRefFromType(methodDef.ReturnType), argTypes, false); ValueRef function = LLVM.AddFunction(mCompiler.Module, methodName, functionType); mCompiler.Lookup.AddFunction(methodName, function); }
/// <summary> /// Compiles the init cctors method. /// </summary> private void compileInitCctorsMethod() { TypeRef type = LLVM.FunctionType(TypeHelper.Void, new TypeRef[0], false); ValueRef func = LLVM.AddFunction(Module, "initCctors", type); BuilderRef builder = LLVM.CreateBuilderInContext(ModuleContext); LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(ModuleContext, func, string.Empty)); MethodDefinition[] cctors = Lookup.GetStaticConstructors(); foreach (MethodDefinition method in cctors) { LLVM.BuildCall(builder, Lookup.GetFunction(NameHelper.CreateMethodName(method)).Value, new ValueRef[0], string.Empty); } LLVM.BuildRetVoid(builder); LLVM.DisposeBuilder(builder); }
/// <summary> /// Emits a ldftn 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) { MethodDefinition method = (MethodDefinition)instruction.Operand; ValueRef result = LLVM.BuildIntToPtr(builder, context.Compiler.Lookup.GetFunction(NameHelper.CreateMethodName(method)).Value, TypeHelper.NativeIntType, "ldftn"); StackElement element = new StackElement(result, typeof(IntPtr).GetTypeReference(), TypeHelper.NativeIntType); context.CurrentStack.Push(element); }
/// <summary> /// Emits a callvirt 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) { MethodReference methodRef = (MethodReference)instruction.Operand; TypeRef returnType = TypeHelper.GetTypeRefFromType(methodRef.ReturnType); Lookup lookup = context.Compiler.Lookup; bool needsVirtualCall = lookup.NeedsVirtualCall(methodRef.DeclaringType); // Build parameter value and types arrays. int paramCount = 1 + methodRef.Parameters.Count; // Get the method, if it is null, create a new empty one, otherwise reference it. string methodName = NameHelper.CreateMethodName(methodRef); ValueRef?func = lookup.GetFunction(methodName); // Process arguments. // Note: backwards for loop because stack is backwards! ValueRef[] argVals = new ValueRef[paramCount]; TypeRef[] paramTypes = new TypeRef[paramCount]; for (int i = paramCount - 1; i >= 0; i--) { TypeReference type; StackElement element = context.CurrentStack.Pop(); argVals[i] = element.Value; // Get type of parameter. if (i == 0) { type = methodRef.DeclaringType; } else { type = methodRef.Parameters[i - 1].ParameterType; } paramTypes[i] = TypeHelper.GetTypeRefFromType(type); if (type.IsValueType && i == 0) { paramTypes[i] = LLVM.PointerType(paramTypes[i], 0); } // Cast needed? if (element.Type != paramTypes[i]) { CastHelper.HelpIntAndPtrCast(builder, ref argVals[i], ref element.Type, paramTypes[i], "callvirtcast"); } } // Function does not exist, create a declaration for the function. TypeRef functionType = LLVM.FunctionType(returnType, paramTypes, false); if (!func.HasValue) { func = LLVM.AddFunction(context.Compiler.Module, methodName, functionType); context.Compiler.Lookup.AddFunction(methodName, func.Value); } // Call. ValueRef method; if (needsVirtualCall && !lookup.IsMethodUnique(methodRef)) { // We need a virtual call. TypeDefinition methodParent = methodRef.DeclaringType.Resolve(); TypeRef funcPtrType = LLVM.PointerType(functionType, 0); VTable vTable = lookup.GetVTable(methodParent); ValueRef methodGep; // Two cases: // 1) The parent of the method is an interface. // In this case, we need to first get the VTable from the indirection table. // 2) The parent of the method is a class. // In this case, we can directly now the offset to the VTable from the object pointer. if (!methodParent.IsInterface) { uint index = lookup.GetClassVTableIndex(methodParent); ValueRef vTableGep = LLVM.BuildInBoundsGEP(builder, argVals[0], new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, index, false) }, "vtablegep"); ValueRef vTableInstance = LLVM.BuildLoad(builder, vTableGep, "vtable"); methodGep = LLVM.BuildInBoundsGEP(builder, vTableInstance, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, (uint)vTable.GetMethodIndex(methodParent, methodRef), false) }, "methodgep"); } else { uint index = lookup.GetInterfaceID(methodParent); ValueRef indirectionGep = LLVM.BuildInBoundsGEP(builder, argVals[0], new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false) }, "indirectiongep"); indirectionGep = LLVM.BuildPointerCast(builder, indirectionGep, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(TypeHelper.VoidPtr, 0), 0), 0), string.Empty); ValueRef indirectionTable = LLVM.BuildLoad(builder, indirectionGep, "indirectiontable"); ValueRef vTableGep = LLVM.BuildInBoundsGEP(builder, indirectionTable, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, index, false) }, "vtablegep"); ValueRef vTableInstance = LLVM.BuildLoad(builder, vTableGep, "vtable"); methodGep = LLVM.BuildInBoundsGEP(builder, vTableInstance, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, (uint)vTable.GetMethodIndex(methodParent, methodRef), false) }, "methodgep"); } // Indirect call. ValueRef methodPtr = LLVM.BuildLoad(builder, methodGep, "methodptr"); method = LLVM.BuildPointerCast(builder, methodPtr, funcPtrType, "method"); } else { // We can call it directly without VTable lookup. method = func.Value; } ValueRef retVal = LLVM.BuildCall(builder, method, argVals, string.Empty); if (instruction.HasPrefix(Code.Tail)) { LLVM.SetTailCall(retVal, true); } // Push return value on stack if it has one. if (methodRef.ReturnType.MetadataType != MetadataType.Void) { context.CurrentStack.Push(new StackElement(retVal, methodRef.ReturnType)); } }
/// <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 a call 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) { MethodReference methodRef = (MethodReference)instruction.Operand; TypeRef returnType = TypeHelper.GetTypeRefFromType(methodRef.ReturnType); // Check for special cases. if (instruction.Previous != null && instruction.Previous.OpCode.Code == Code.Ldtoken) { emitFromLdtoken(instruction, context, builder); return; } // Call System.Object::.ctor() ? else if (methodRef.FullName == "System.Void System.Object::.ctor()") { // Delete object reference from stack and ignore this. context.CurrentStack.Pop(); return; } // Build parameter value and types arrays. int paramCount = methodRef.Parameters.Count; if (methodRef.HasThis) { paramCount++; } // Get the method, if it is null, create a new empty one, otherwise reference it. string methodName = NameHelper.CreateMethodName(methodRef); ValueRef?func = context.Compiler.Lookup.GetFunction(methodName); // Process arguments. // Note: backwards for loop because stack is backwards! ValueRef[] argVals = new ValueRef[paramCount]; TypeRef[] paramTypes = new TypeRef[paramCount]; for (int i = paramCount - 1; i >= 0; i--) { TypeReference type; StackElement element = context.CurrentStack.Pop(); argVals[i] = element.Value; // Note: the instance pointer is not included in the parameters explicitely. if (methodRef.HasThis) { if (i == 0) { type = methodRef.DeclaringType; } else { type = methodRef.Parameters[i - 1].ParameterType; } } else { type = methodRef.Parameters[i].ParameterType; } paramTypes[i] = TypeHelper.GetTypeRefFromType(type); if (type.IsValueType && methodRef.HasThis && i == 0) { paramTypes[i] = LLVM.PointerType(paramTypes[i], 0); } // Cast needed? if (element.Type != paramTypes[i]) { CastHelper.HelpIntAndPtrCast(builder, ref argVals[i], ref element.Type, paramTypes[i], "callcast"); } } // Function does not exist, create a declaration for the function. if (!func.HasValue) { TypeRef functionType = LLVM.FunctionType(returnType, paramTypes, false); func = LLVM.AddFunction(context.Compiler.Module, methodName, functionType); context.Compiler.Lookup.AddFunction(methodName, func.Value); } // Call. ValueRef retVal = LLVM.BuildCall(builder, func.Value, argVals, string.Empty); if (instruction.HasPrefix(Code.Tail)) { LLVM.SetTailCall(retVal, true); } // Push return value on stack if it has one. if (methodRef.ReturnType.MetadataType != MetadataType.Void) { context.CurrentStack.Push(new StackElement(retVal, methodRef.ReturnType)); } }