private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor) { var stack = functionContext.Stack; // Make sure .cctor has been called EnsureClassInitialized(functionContext, GetClass(type)); var allocatedObject = AllocateObject(type); // Add it to stack, right before arguments var ctorNumParams = ctor.ParameterTypes.Length; stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject)); // Invoke ctor EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue); if (type.StackType != StackValueType.Object) { allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty); } // Add created object on the stack stack.Add(new StackValue(type.StackType, type, allocatedObject)); }
private ValueRef CreateDebugType(FunctionCompilerContext functionContext, Type type) { var size = LLVM.ABISizeOfType(targetData, type.DefaultType) * 8; var align = LLVM.ABIAlignmentOfType(targetData, type.DefaultType) * 8; switch (type.TypeReference.MetadataType) { case MetadataType.Boolean: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "bool", size, align, (uint)DW_ATE.Boolean)); case MetadataType.SByte: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "sbyte", size, align, (uint)DW_ATE.Signed)); case MetadataType.Byte: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "byte", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.Int16: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "short", size, align, (uint)DW_ATE.Signed)); case MetadataType.UInt16: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "ushort", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.Int32: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "int", size, align, (uint)DW_ATE.Signed)); case MetadataType.UInt32: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "uint", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.Int64: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "long", size, align, (uint)DW_ATE.Signed)); case MetadataType.UInt64: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "ulong", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.Single: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "float", size, align, (uint)DW_ATE.Float)); case MetadataType.Double: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "double", size, align, (uint)DW_ATE.Float)); case MetadataType.Char: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "char", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.IntPtr: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "IntPtr", size, align, (uint)DW_ATE.Signed)); case MetadataType.UIntPtr: return(LLVM.DIBuilderCreateBasicType(debugBuilder, "UIntPtr", size, align, (uint)DW_ATE.Unsigned)); case MetadataType.Pointer: var elementType = GetType(((PointerType)type.TypeReference).ElementType); return(LLVM.DIBuilderCreatePointerType(debugBuilder, CreateDebugType(functionContext, elementType), size, align, type.TypeReference.Name)); default: // For now, let's have a fallback since lot of types are not supported yet. return(CreateDebugType(functionContext, intPtr)); } }
private void PrepareScopes(FunctionCompilerContext functionContext) { var function = functionContext.Function; var methodReference = function.MethodReference; var body = functionContext.Body; // Add root scope variables // Note: could be null var newScope = new Scope(body.Scope); functionContext.Scopes.Add(newScope); // Update debug information var startSequencePoint = body.Instructions[0].SequencePoint; if (startSequencePoint != null) { var url = startSequencePoint.Document.Url; var line = startSequencePoint.StartLine; functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url)); var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]); var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes); newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, functionContext.DebugFile, methodReference.FullName, LLVM.GetValueName(function.GeneratedValue), functionContext.DebugFile, (uint)line, functionType, false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty); } SetupDebugLocation(body.Instructions[0], newScope); if (body.Scope != null) { EnterScope(functionContext, newScope); } else { // Emit locals (if no scopes) for (int index = 0; index < body.Variables.Count; index++) { var variable = functionContext.Locals[index]; var variableName = body.Variables[index].Name; EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } // Emit args for (int index = 0; index < function.ParameterTypes.Length; index++) { var arg = functionContext.Arguments[index]; var argName = LLVM.GetValueName(arg.Value); EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1); } }
private void PrepareScopes(FunctionCompilerContext functionContext) { var function = functionContext.Function; var methodReference = function.MethodReference; var body = functionContext.Body; // Add root scope variables // Note: could be null var newScope = new Scope(body.Scope); functionContext.Scopes.Add(newScope); // Update debug information var startSequencePoint = body.Instructions[0].SequencePoint; if (startSequencePoint != null) { var url = startSequencePoint.Document.Url; var line = startSequencePoint.StartLine; functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url)); var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]); var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes); newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, functionContext.DebugFile, methodReference.FullName, LLVM.GetValueName(function.GeneratedValue), functionContext.DebugFile, (uint)line, functionType, false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty); } SetupDebugLocation(body.Instructions[0], newScope); if (body.Scope != null) { EnterScope(functionContext, newScope); } else { // Emit locals (if no scopes) for (int index = 0; index < body.Variables.Count; index++) { var variable = functionContext.Locals[index]; var variableName = body.Variables[index].Name; EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } // Emit args for (int index = 0; index < function.ParameterTypes.Length; index++) { var arg = functionContext.Arguments[index]; var argName = LLVM.GetValueName(arg.Value); EmitDebugVariable(functionContext, body.Instructions[0], newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1); } }
private Scope CreateScope(FunctionCompilerContext functionContext, Scope parentScope, Mono.Cecil.Cil.Scope cecilScope) { var newScope = new Scope(cecilScope); var sequencePoint = newScope.Source.Start.SequencePoint; if (sequencePoint != null) { newScope.GeneratedScope = LLVM.DIBuilderCreateLexicalBlock(debugBuilder, parentScope.GeneratedScope, functionContext.DebugFile, (uint)sequencePoint.StartLine, (uint)sequencePoint.StartColumn, 0); } return(newScope); }
private void EmitDebugVariable(FunctionCompilerContext functionContext, Instruction start, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0) { var sequencePoint = start.SequencePoint; var debugType = CreateDebugType(functionContext, variable.Type); // TODO: Detect where variable is actually declared (first use of local?) var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder, (uint)dwarfType, generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType, true, 0, (uint)argIndex); var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable, LLVM.GetInsertBlock(builder)); LLVM.SetInstDebugLocation(builder, debugVariableDeclare); }
private void ProcessScopes(FunctionCompilerContext functionContext, Instruction instruction) { var scopes = functionContext.Scopes; // Exit finished scopes for (int index = scopes.Count - 1; index >= 0; index--) { var scope = scopes[index]; if (scope.Source != null && instruction.Offset > scope.Source.End.Offset) { scopes.RemoveAt(index); } else { break; } } var lastScope = scopes[scopes.Count - 1]; bool foundNewScope = true; while (foundNewScope) { foundNewScope = false; if (lastScope.Source != null && lastScope.Source.HasScopes) { foreach (var childScope in lastScope.Source.Scopes) { if (instruction == childScope.Start) { lastScope = CreateScope(functionContext, lastScope, childScope); scopes.Add(lastScope); EnterScope(functionContext, lastScope); foundNewScope = true; break; } } } } if (instruction.SequencePoint != null) { SetupDebugLocation(instruction.SequencePoint, lastScope); } }
private void EnterScope(FunctionCompilerContext functionContext, Scope newScope) { if (newScope.Source != null) { SetupDebugLocation(newScope.Source.Start, newScope); if (newScope.Source.HasVariables) { foreach (var local in newScope.Source.Variables) { var variable = functionContext.Locals[local.Index]; var variableName = local.Name; EmitDebugVariable(functionContext, newScope.Source.Start, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } } }
/// <summary> /// Generates invoke if inside a try block, otherwise a call. /// </summary> /// <param name="functionContext">The function context.</param> /// <param name="function">The function.</param> /// <param name="args">The arguments.</param> /// <returns></returns> private ValueRef GenerateInvoke(FunctionCompilerContext functionContext, ValueRef function, ValueRef[] args) { ValueRef callResult; if (functionContext.LandingPadBlock.Value != IntPtr.Zero) { var nextBlock = LLVM.AppendBasicBlockInContext(context, functionContext.Function.GeneratedValue, string.Empty); callResult = LLVM.BuildInvoke(builder, function, args, nextBlock, functionContext.LandingPadBlock, string.Empty); LLVM.PositionBuilderAtEnd(builder, nextBlock); functionContext.BasicBlock = nextBlock; } else { callResult = LLVM.BuildCall(builder, function, args, string.Empty); } return(callResult); }
private void EmitDebugVariable(FunctionCompilerContext functionContext, SequencePoint sequencePoint, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0) { var debugType = CreateDebugType(variable.Type); // Process fields and other dependent debug types ProcessMissingDebugTypes(); // Read it again in case it was mutated debugType = CreateDebugType(variable.Type); // TODO: Detect where variable is actually declared (first use of local?) var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder, (uint)dwarfType, generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType, true, 0, (uint)argIndex); var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable, LLVM.GetInsertBlock(builder)); LLVM.SetInstDebugLocation(builder, debugVariableDeclare); }
private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod) { var stack = functionContext.Stack; // Build argument list var targetNumParams = targetMethod.ParameterTypes.Length; var args = new ValueRef[targetNumParams]; for (int index = 0; index < targetNumParams; index++) { // TODO: Casting/implicit conversion? var stackItem = stack[stack.Count - targetNumParams + index]; args[index] = ConvertFromStackToLocal(targetMethod.ParameterTypes[index], stackItem); } // Remove arguments from stack stack.RemoveRange(stack.Count - targetNumParams, targetNumParams); // Invoke method ValueRef callResult; var actualMethod = overrideMethod; callResult = GenerateInvoke(functionContext, actualMethod, args); // Mark method as needed (if non-virtual call) if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero) { LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage); } // Push return result on stack if (targetMethod.ReturnType.TypeReference.MetadataType != MetadataType.Void) { // Convert return value from local to stack value var returnValue = ConvertFromLocalToStack(targetMethod.ReturnType, callResult); // Add value to stack stack.Add(new StackValue(targetMethod.ReturnType.StackType, targetMethod.ReturnType, returnValue)); } }
private void ProcessScopes(FunctionCompilerContext functionContext, Instruction instruction) { var scopes = functionContext.Scopes; // Exit finished scopes for (int index = scopes.Count - 1; index >= 0; index--) { var scope = scopes[index]; if (scope.Source != null && instruction.Offset > scope.Source.End.Offset) scopes.RemoveAt(index); else break; } var lastScope = scopes[scopes.Count - 1]; bool foundNewScope = true; while (foundNewScope) { foundNewScope = false; if (lastScope.Source != null && lastScope.Source.HasScopes) { foreach (var childScope in lastScope.Source.Scopes) { if (instruction == childScope.Start) { lastScope = CreateScope(functionContext, lastScope, childScope); scopes.Add(lastScope); EnterScope(functionContext, lastScope); foundNewScope = true; break; } } } } SetupDebugLocation(instruction, lastScope); }
/// <summary> /// Generates invoke if inside a try block, otherwise a call. /// </summary> /// <param name="functionContext">The function context.</param> /// <param name="function">The function.</param> /// <param name="args">The arguments.</param> /// <returns></returns> private ValueRef GenerateInvoke(FunctionCompilerContext functionContext, ValueRef function, ValueRef[] args) { ValueRef callResult; if (functionContext.LandingPadBlock.Value != IntPtr.Zero) { var nextBlock = LLVM.AppendBasicBlockInContext(context, functionContext.FunctionGlobal, string.Empty); LLVM.MoveBasicBlockAfter(nextBlock, LLVM.GetInsertBlock(builder)); callResult = LLVM.BuildInvoke(builder, function, args, nextBlock, functionContext.LandingPadBlock, string.Empty); LLVM.PositionBuilderAtEnd(builder, nextBlock); functionContext.BasicBlock = nextBlock; } else { callResult = LLVM.BuildCall(builder, function, args, string.Empty); } return callResult; }
private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod) { var stack = functionContext.Stack; bool hasStructValueReturn = targetMethod.ReturnType.ABIParameterInfo.Kind == ABIParameterInfoKind.Indirect; // Build argument list var targetNumParams = targetMethod.ParameterTypes.Length; var args = new ValueRef[targetNumParams + (hasStructValueReturn ? 1 : 0)]; for (int index = 0; index < targetNumParams; index++) { var llvmIndex = index + (hasStructValueReturn ? 1 : 0); var parameterType = targetMethod.ParameterTypes[index]; // TODO: Casting/implicit conversion? var stackItem = stack[stack.Count - targetNumParams + index]; args[llvmIndex] = ConvertFromStackToLocal(parameterType.Type, stackItem); if (stackItem.StackType == StackValueType.Value) { switch (parameterType.ABIParameterInfo.Kind) { case ABIParameterInfoKind.Direct: args[llvmIndex] = LLVM.BuildLoad(builder, args[llvmIndex], string.Empty); break; case ABIParameterInfoKind.Indirect: // Make a copy of indirect aggregate values args[llvmIndex] = LoadValue(stackItem.Type.StackType, args[llvmIndex], InstructionFlags.None); break; case ABIParameterInfoKind.Coerced: // Coerce to integer type args[llvmIndex] = LLVM.BuildPointerCast(builder, args[llvmIndex], LLVM.PointerType(parameterType.ABIParameterInfo.CoerceType, 0), string.Empty); args[llvmIndex] = LLVM.BuildLoad(builder, args[llvmIndex], string.Empty); break; default: throw new ArgumentOutOfRangeException(); } } } // Remove arguments from stack stack.RemoveRange(stack.Count - targetNumParams, targetNumParams); // Prepare return value storage (in case of struct) if (hasStructValueReturn) { // Allocate storage args[0] = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty); } // Invoke method ValueRef callResult; var actualMethod = overrideMethod; callResult = GenerateInvoke(functionContext, actualMethod, args); switch (targetMethod.CallingConvention) { case MethodCallingConvention.StdCall: LLVM.SetInstructionCallConv(callResult, (uint)CallConv.X86StdcallCallConv); break; case MethodCallingConvention.FastCall: LLVM.SetInstructionCallConv(callResult, (uint)CallConv.X86FastcallCallConv); break; } // Mark method as needed (if non-virtual call) if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero) { LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage); } // Push return result on stack if (targetMethod.ReturnType.Type.TypeReferenceCecil.MetadataType != MetadataType.Void) { ValueRef returnValue; if (targetMethod.ReturnType.Type.StackType == StackValueType.Value) { switch (targetMethod.ReturnType.ABIParameterInfo.Kind) { case ABIParameterInfoKind.Direct: returnValue = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty); LLVM.BuildStore(builder, callResult, returnValue); break; case ABIParameterInfoKind.Indirect: returnValue = args[0]; break; case ABIParameterInfoKind.Coerced: returnValue = LLVM.BuildAlloca(builderAlloca, targetMethod.ReturnType.Type.DefaultTypeLLVM, string.Empty); var returnValueCasted = LLVM.BuildPointerCast(builder, returnValue, LLVM.PointerType(targetMethod.ReturnType.ABIParameterInfo.CoerceType, 0), string.Empty); LLVM.BuildStore(builder, callResult, returnValueCasted); break; default: throw new ArgumentOutOfRangeException(); } } else { returnValue = callResult; } // Convert return value from local to stack value returnValue = ConvertFromLocalToStack(targetMethod.ReturnType.Type, returnValue); // Add value to stack stack.Add(new StackValue(targetMethod.ReturnType.Type.StackType, targetMethod.ReturnType.Type, returnValue)); } // Apply attributes to the call instruction ApplyCallAttributes(targetMethod, callResult); }
private void EmitRet(FunctionCompilerContext functionContext) { var method = functionContext.MethodReference; if (method.ReturnType.MetadataType == MetadataType.Void) { // Emit ret void LLVM.BuildRetVoid(builder); } else { // Get last item from stack var stackItem = functionContext.Stack.Pop(); // Get return type var returnType = GetType(ResolveGenericsVisitor.Process(method, method.ReturnType), TypeState.StackComplete); var returnValue = ConvertFromStackToLocal(returnType, stackItem); if (returnType.StackType == StackValueType.Value) { switch (functionContext.Signature.ReturnType.ABIParameterInfo.Kind) { case ABIParameterInfoKind.Coerced: returnValue = LLVM.BuildPointerCast(builder, returnValue, LLVM.PointerType(functionContext.Signature.ReturnType.ABIParameterInfo.CoerceType, 0), string.Empty); returnValue = LLVM.BuildLoad(builder, returnValue, string.Empty); LLVM.BuildRet(builder, returnValue); break; case ABIParameterInfoKind.Direct: returnValue = LLVM.BuildLoad(builder, returnValue, string.Empty); LLVM.BuildRet(builder, returnValue); break; case ABIParameterInfoKind.Indirect: StoreValue(returnType.StackType, returnValue, LLVM.GetParam(functionContext.FunctionGlobal, 0), InstructionFlags.None); LLVM.BuildRetVoid(builder); break; default: throw new ArgumentOutOfRangeException(); } } else { LLVM.BuildRet(builder, returnValue); } } }
private void EmitEndfinally(FunctionCompilerContext functionContext, ExceptionHandlerInfo currentFinallyClause) { if (currentFinallyClause.Source.HandlerType == ExceptionHandlerType.Finally) { // Basic block to continue exception handling (if endfinally.jumptarget is -1, but we simply set it on undefined cases) var activeTryHandlers = functionContext.ActiveTryHandlers; var nextActiveTryHandler = activeTryHandlers.Count > 0 ? activeTryHandlers[activeTryHandlers.Count - 1].CatchDispatch : functionContext.ResumeExceptionBlock; // Generate dispatch code (with a switch/case) var @switch = LLVM.BuildSwitch(builder, LLVM.BuildLoad(builder, functionContext.EndfinallyJumpTarget, string.Empty), nextActiveTryHandler, (uint)currentFinallyClause.LeaveTargets.Count); for (int index = 0; index < currentFinallyClause.LeaveTargets.Count; index++) { var leaveTarget = currentFinallyClause.LeaveTargets[index]; LLVM.AddCase(@switch, LLVM.ConstInt(int32LLVM, (ulong)index, false), functionContext.BasicBlocks[leaveTarget.Offset]); } } else if (currentFinallyClause.Source.HandlerType == ExceptionHandlerType.Fault) { var exceptionObject = LLVM.BuildLoad(builder, functionContext.ExceptionSlot, string.Empty); // Rethrow exception GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, exceptionObject, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)}); LLVM.BuildUnreachable(builder); } else { throw new InvalidOperationException("Exception clause containing a endfinally/endfault is not a finally or fault clause."); } // Default is not to flow to next instruction, previous Leave instructions already tagged what was necessary functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; }
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", LLVM.DIGetDebugMetadataVersion()); // 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 globalCtorSignature = new FunctionSignature(abi, @void, new Type[0], MethodCallingConvention.Default, null); var globalCtorFunctionType = CreateFunctionTypeLLVM(globalCtorSignature); var globalCtor = LLVM.AddFunction(module, "initializeSharpLangModule", globalCtorFunctionType); LLVM.SetLinkage(globalCtor, Linkage.PrivateLinkage); LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlockInContext(context, globalCtor, string.Empty)); Function entryPoint = null; if (assembly.EntryPoint != null) functions.TryGetValue(assembly.EntryPoint, out entryPoint); 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, globalCtorSignature); functionContext.Stack = new FunctionStack(); 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); } // Register unmanaged delegate callbacks var delegateWrappers = assembly.MainModule.GetType("DelegateWrappers"); if (delegateWrappers != null) { var marshalHelper = GetType(corlib.MainModule.GetType("SharpLang.Marshalling.MarshalHelper"), TypeState.VTableEmitted); var marshalHelperRegisterDelegateWrapper = marshalHelper.Class.Functions.First(x => x.DeclaringType == marshalHelper && x.MethodReference.Name == "RegisterDelegateWrapper"); foreach (var delegateWrapper in delegateWrappers.Methods) { var method = GetFunction(delegateWrapper); // Determine delegate type from MarshalFunctionAttribute var marshalFunctionAttribute = method.MethodDefinition.CustomAttributes.First(x => x.AttributeType.FullName == "SharpLang.Marshalling.MarshalFunctionAttribute"); var delegateType = GetType((TypeReference)marshalFunctionAttribute.ConstructorArguments[0].Value, TypeState.VTableEmitted); // TODO: Rename those method, and set their linking to linkonce so that it can be merged? // Of course, we would also need to make sure it works when called from external assemblies functionContext.Stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.ConstPointerCast(delegateType.Class.GeneratedEETypeRuntimeLLVM, intPtrLLVM))); EmitLdftn(functionContext.Stack, method); EmitCall(functionContext, marshalHelperRegisterDelegateWrapper.Signature, marshalHelperRegisterDelegateWrapper.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) if (entryPoint != null) { 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)); // Emit PInvoke Thunks and Globals needed in entry point module PInvokeEmitGlobals(); } LLVM.DIBuilderFinalize(debugBuilder); LLVM.DIBuilderDispose(debugBuilder); LLVM.DisposeBuilder(builder); return module; }
private Scope CreateScope(FunctionCompilerContext functionContext, Scope parentScope, Mono.Cecil.Cil.Scope cecilScope) { var newScope = new Scope(cecilScope); var sequencePoint = newScope.Source.Start.SequencePoint; if (sequencePoint != null) { newScope.GeneratedScope = LLVM.DIBuilderCreateLexicalBlock(debugBuilder, parentScope.GeneratedScope, functionContext.DebugFile, (uint) sequencePoint.StartLine, (uint) sequencePoint.StartColumn, 0); } return newScope; }
private void EmitDebugVariable(FunctionCompilerContext functionContext, SequencePoint sequencePoint, DIDescriptor generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0) { var debugType = CreateDebugType(variable.Type); // Process fields and other dependent debug types ProcessMissingDebugTypes(); // Read it again in case it was mutated debugType = CreateDebugType(variable.Type); // TODO: Detect where variable is actually declared (first use of local?) var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder, (uint)dwarfType, generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType, true, 0, (uint)argIndex); var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable, debugEmptyExpression, LLVM.GetInsertBlock(builder)); LLVM.SetInstDebugLocation(builder, debugVariableDeclare); }
private void PrepareScopes(FunctionCompilerContext functionContext, Function function) { var methodReference = function.MethodReference; var body = functionContext.Body; // Add root scope variables // Note: could be null var newScope = new Scope(body.Scope); functionContext.Scopes.Add(newScope); // Find class scope var debugClass = GetOrCreateDebugClass(GetClass(function.DeclaringType)); // Update debug information int line = 0; var startSequencePoint = body.Instructions[0].SequencePoint; string url; if (startSequencePoint != null) { url = startSequencePoint.Document.Url; line = startSequencePoint.StartLine; } else { url = assembly.MainModule.FullyQualifiedName; } functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url)); var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new DIDescriptor[0]); var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes); // Replace . with :: so that gdb properly understands it is namespaces. var methodDebugName = string.Format("{0}::{1}", methodReference.DeclaringType.FullName.Replace(".", "::"), methodReference.Name); newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, debugClass, methodReference.Name, methodDebugName, functionContext.DebugFile, (uint)line, functionType, false, true, (uint)line, 0, false, function.GeneratedValue, DIDescriptor.Empty, DIDescriptor.Empty); SetupDebugLocation(body.Instructions[0].SequencePoint, newScope); if (body.Scope != null) { EnterScope(functionContext, newScope); } else { // Emit locals (if no scopes) for (int index = 0; index < body.Variables.Count; index++) { var variable = functionContext.Locals[index]; var variableName = body.Variables[index].Name; if (string.IsNullOrEmpty(variableName)) variableName = "var" + index; EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } // Emit args for (int index = 0; index < function.ParameterTypes.Length; index++) { var arg = functionContext.Arguments[index]; var argName = LLVM.GetValueName(arg.Value); EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1); } }
private ValueRef CreateDebugType(FunctionCompilerContext functionContext, Type type) { var size = LLVM.ABISizeOfType(targetData, type.DefaultType) * 8; var align = LLVM.ABIAlignmentOfType(targetData, type.DefaultType) * 8; switch (type.TypeReference.MetadataType) { case MetadataType.Boolean: return LLVM.DIBuilderCreateBasicType(debugBuilder, "bool", size, align, (uint)DW_ATE.Boolean); case MetadataType.SByte: return LLVM.DIBuilderCreateBasicType(debugBuilder, "sbyte", size, align, (uint)DW_ATE.Signed); case MetadataType.Byte: return LLVM.DIBuilderCreateBasicType(debugBuilder, "byte", size, align, (uint)DW_ATE.Unsigned); case MetadataType.Int16: return LLVM.DIBuilderCreateBasicType(debugBuilder, "short", size, align, (uint)DW_ATE.Signed); case MetadataType.UInt16: return LLVM.DIBuilderCreateBasicType(debugBuilder, "ushort", size, align, (uint)DW_ATE.Unsigned); case MetadataType.Int32: return LLVM.DIBuilderCreateBasicType(debugBuilder, "int", size, align, (uint)DW_ATE.Signed); case MetadataType.UInt32: return LLVM.DIBuilderCreateBasicType(debugBuilder, "uint", size, align, (uint)DW_ATE.Unsigned); case MetadataType.Int64: return LLVM.DIBuilderCreateBasicType(debugBuilder, "long", size, align, (uint)DW_ATE.Signed); case MetadataType.UInt64: return LLVM.DIBuilderCreateBasicType(debugBuilder, "ulong", size, align, (uint)DW_ATE.Unsigned); case MetadataType.Single: return LLVM.DIBuilderCreateBasicType(debugBuilder, "float", size, align, (uint)DW_ATE.Float); case MetadataType.Double: return LLVM.DIBuilderCreateBasicType(debugBuilder, "double", size, align, (uint)DW_ATE.Float); case MetadataType.Char: return LLVM.DIBuilderCreateBasicType(debugBuilder, "char", size, align, (uint)DW_ATE.Unsigned); case MetadataType.IntPtr: return LLVM.DIBuilderCreateBasicType(debugBuilder, "IntPtr", size, align, (uint)DW_ATE.Signed); case MetadataType.UIntPtr: return LLVM.DIBuilderCreateBasicType(debugBuilder, "UIntPtr", size, align, (uint)DW_ATE.Unsigned); case MetadataType.Pointer: var elementType = GetType(((PointerType)type.TypeReference).ElementType); return LLVM.DIBuilderCreatePointerType(debugBuilder, CreateDebugType(functionContext, elementType), size, align, type.TypeReference.Name); default: // For now, let's have a fallback since lot of types are not supported yet. return CreateDebugType(functionContext, intPtr); } }
private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor) { var stack = functionContext.Stack; // Make sure .cctor has been called EnsureClassInitialized(functionContext, GetClass(type)); var allocatedObject = AllocateObject(type); // Add it to stack, right before arguments var ctorNumParams = ctor.ParameterTypes.Length; stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject)); // Invoke ctor EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue); if (type.StackType != StackValueType.Object) { allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty); } // Add created object on the stack stack.Add(new StackValue(type.StackType, type, allocatedObject)); }
private void EmitDebugVariable(FunctionCompilerContext functionContext, Instruction start, ValueRef generatedScope, StackValue variable, DW_TAG dwarfType, string variableName, int argIndex = 0) { var sequencePoint = start.SequencePoint; var debugType = CreateDebugType(functionContext, variable.Type); // TODO: Detect where variable is actually declared (first use of local?) var debugLocalVariable = LLVM.DIBuilderCreateLocalVariable(debugBuilder, (uint)dwarfType, generatedScope, variableName, functionContext.DebugFile, sequencePoint != null ? (uint)sequencePoint.StartLine : 0, debugType, true, 0, (uint)argIndex); var debugVariableDeclare = LLVM.DIBuilderInsertDeclareAtEnd(debugBuilder, variable.Value, debugLocalVariable, LLVM.GetInsertBlock(builder)); LLVM.SetInstDebugLocation(builder, debugVariableDeclare); }
private void EnterScope(FunctionCompilerContext functionContext, Scope newScope) { if (newScope.Source != null) { SetupDebugLocation(newScope.Source.Start, newScope); if (newScope.Source.HasVariables) { foreach (var local in newScope.Source.Variables) { var variable = functionContext.Locals[local.Index]; var variableName = local.Name; EmitDebugVariable(functionContext, newScope.Source.Start, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } } }
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 void PrepareScopes(FunctionCompilerContext functionContext, Function function) { var methodReference = function.MethodReference; var body = functionContext.Body; // Add root scope variables // Note: could be null var newScope = new Scope(body.Scope); functionContext.Scopes.Add(newScope); // Find class scope var debugClass = GetOrCreateDebugClass(GetClass(function.DeclaringType)); // Update debug information int line = 0; var startSequencePoint = body.Instructions[0].SequencePoint; string url; if (startSequencePoint != null) { url = startSequencePoint.Document.Url; line = startSequencePoint.StartLine; } else { url = assembly.MainModule.FullyQualifiedName; } functionContext.DebugFile = LLVM.DIBuilderCreateFile(debugBuilder, Path.GetFileName(url), Path.GetDirectoryName(url)); var functionParameterTypes = LLVM.DIBuilderGetOrCreateArray(debugBuilder, new ValueRef[0]); var functionType = LLVM.DIBuilderCreateSubroutineType(debugBuilder, functionContext.DebugFile, functionParameterTypes); // Replace . with :: so that gdb properly understands it is namespaces. var methodDebugName = string.Format("{0}::{1}", methodReference.DeclaringType.FullName.Replace(".", "::"), methodReference.Name); newScope.GeneratedScope = LLVM.DIBuilderCreateFunction(debugBuilder, debugClass, methodReference.Name, methodDebugName, functionContext.DebugFile, (uint)line, functionType, false, true, (uint)line, 0, false, function.GeneratedValue, ValueRef.Empty, ValueRef.Empty); SetupDebugLocation(body.Instructions[0].SequencePoint, newScope); if (body.Scope != null) { EnterScope(functionContext, newScope); } else { // Emit locals (if no scopes) for (int index = 0; index < body.Variables.Count; index++) { var variable = functionContext.Locals[index]; var variableName = body.Variables[index].Name; if (string.IsNullOrEmpty(variableName)) { variableName = "var" + index; } EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, variable, DW_TAG.auto_variable, variableName); } } // Emit args for (int index = 0; index < function.ParameterTypes.Length; index++) { var arg = functionContext.Arguments[index]; var argName = LLVM.GetValueName(arg.Value); EmitDebugVariable(functionContext, body.Instructions[0].SequencePoint, newScope.GeneratedScope, arg, DW_TAG.arg_variable, argName, index + 1); } }
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; } }
/// <summary> /// Compiles the given method definition. /// </summary> /// <param name="method">The method.</param> /// <param name="function">The function.</param> /// <exception cref="System.NotSupportedException"></exception> /// <exception cref="System.NotImplementedException"></exception> /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception> private void CompileFunction(MethodReference methodReference, Function function) { var method = methodReference.Resolve(); var body = method.Body; var codeSize = body != null ? body.CodeSize : 0; var functionGlobal = function.GeneratedValue; var functionContext = new FunctionCompilerContext(function); // Create default blocks (alloca one, and entry one) var allocaBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); functionContext.BasicBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); { LLVM.PositionBuilderAtEnd(builderAlloca, allocaBlock); var allocaExitJump = LLVM.BuildBr(builderAlloca, functionContext.BasicBlock); LLVM.PositionBuilderBefore(builderAlloca, allocaExitJump); } LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock); if (methodReference.DeclaringType.Name == "EncodingHelper" && methodReference.Name == "LoadGetStringPlatform") { body = new MethodBody(method); body.Instructions.Add(Instruction.Create(OpCodes.Ldnull)); body.Instructions.Add(Instruction.Create(OpCodes.Ret)); } if ((method.ImplAttributes & MethodImplAttributes.Runtime) != 0) { var declaringClass = GetClass(function.DeclaringType); // Generate IL for various methods if (declaringClass.BaseType != null && declaringClass.BaseType.Type.TypeReferenceCecil.FullName == typeof(MulticastDelegate).FullName) { body = GenerateDelegateMethod(method, declaringClass); if (body == null) return; codeSize = body.UpdateInstructionOffsets(); } // Reposition builder at end, in case it was used LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock); } if (body == null) return; functionContext.Body = body; var numParams = method.Parameters.Count; // Create stack, locals and args var stack = new FunctionStack(body.MaxStackSize); var locals = new List<StackValue>(body.Variables.Count); var args = new List<StackValue>(numParams); var exceptionHandlers = new List<ExceptionHandlerInfo>(); var activeTryHandlers = new List<ExceptionHandlerInfo>(); functionContext.Stack = stack; functionContext.Locals = locals; functionContext.Arguments = args; functionContext.Scopes = new List<Scope>(); functionContext.ExceptionHandlers = exceptionHandlers; functionContext.ActiveTryHandlers = activeTryHandlers; // Process locals foreach (var local in body.Variables) { // TODO: Anything to do on pinned objects? //if (local.IsPinned) // throw new NotSupportedException(); var type = GetType(ResolveGenericsVisitor.Process(methodReference, local.VariableType), TypeState.StackComplete); locals.Add(new StackValue(type.StackType, type, LLVM.BuildAlloca(builderAlloca, type.DefaultTypeLLVM, local.Name))); // Force value types to be emitted right away if (type.TypeDefinitionCecil.IsValueType) GetClass(type); } bool hasStructValueReturn = function.Signature.ReturnType.ABIParameterInfo.Kind == ABIParameterInfoKind.Indirect; // Process args for (int index = 0; index < function.ParameterTypes.Length; index++) { var argType = function.ParameterTypes[index]; var llvmIndex = hasStructValueReturn ? index + 1 : index; var arg = LLVM.GetParam(functionGlobal, (uint)llvmIndex); // Force value types to be emitted right away if (argType.TypeDefinitionCecil.IsValueType) GetClass(argType); var parameterIndex = index - (functionContext.Method.HasThis ? 1 : 0); var parameterName = parameterIndex == -1 ? "this" : method.Parameters[parameterIndex].Name; ValueRef storage; if (argType.StackType == StackValueType.Value) { var parameterType = function.Signature.ParameterTypes[index]; switch (parameterType.ABIParameterInfo.Kind) { case ABIParameterInfoKind.Direct: storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName); LLVM.BuildStore(builder, arg, storage); break; case ABIParameterInfoKind.Indirect: // Directly use byval area as storage (caller made an allocation & copy for this parameter) storage = arg; LLVM.SetValueName(arg, parameterName); break; case ABIParameterInfoKind.Coerced: // Coerce to integer type storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName); var castedStorage = LLVM.BuildPointerCast(builderAlloca, storage, LLVM.PointerType(parameterType.ABIParameterInfo.CoerceType, 0), string.Empty); LLVM.BuildStore(builderAlloca, arg, castedStorage); break; default: throw new ArgumentOutOfRangeException(); } } else { // Copy argument on stack storage = LLVM.BuildAlloca(builderAlloca, argType.DefaultTypeLLVM, parameterName); LLVM.BuildStore(builderAlloca, arg, storage); } args.Add(new StackValue(argType.StackType, argType, storage)); } // Some wasted space due to unused offsets, but we only keep one so it should be fine. // TODO: Reuse same allocated instance per thread, and grow it only if necessary var branchTargets = new bool[codeSize]; var basicBlocks = new BasicBlockRef[codeSize]; var forwardStacks = new StackValue[codeSize][]; functionContext.BasicBlocks = basicBlocks; functionContext.ForwardStacks = forwardStacks; // Find branch targets (which will require PHI node for stack merging) for (int index = 0; index < body.Instructions.Count; index++) { var instruction = body.Instructions[index]; var flowControl = instruction.OpCode.FlowControl; // Process branch targets if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) { var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand }; foreach (var target in targets) { // Operand Target can be reached branchTargets[target.Offset] = true; } } // Need to enforce a block to be created for the next instruction after a conditional branch // TODO: Break? if (flowControl == FlowControl.Cond_Branch) { if (instruction.Next != null) branchTargets[instruction.Next.Offset] = true; } } // Setup exception handling if (body.HasExceptionHandlers) { // Add an "ehselector.slot" i32 local, and a "exn.slot" Object reference local functionContext.ExceptionHandlerSelectorSlot = LLVM.BuildAlloca(builderAlloca, int32LLVM, "ehselector.slot"); functionContext.ExceptionSlot = LLVM.BuildAlloca(builderAlloca, @object.DefaultTypeLLVM, "exn.slot"); functionContext.EndfinallyJumpTarget = LLVM.BuildAlloca(builderAlloca, int32LLVM, "endfinally.jumptarget"); // Create resume exception block functionContext.ResumeExceptionBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "eh.resume"); LLVM.PositionBuilderAtEnd(builder2, functionContext.ResumeExceptionBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, "exn"); var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel"); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, intPtrLLVM, "exn"); var landingPadValue = LLVM.BuildInsertValue(builder2, LLVM.GetUndef(caughtResultLLVM), exceptionObject, 0, "lpad.val"); landingPadValue = LLVM.BuildInsertValue(builder2, landingPadValue, ehselectorValue, 1, "lpad.val"); LLVM.BuildResume(builder2, landingPadValue); // Exception handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { branchTargets[exceptionHandler.HandlerStart.Offset] = true; } } // Create basic block // TODO: Could be done during previous pass for (int offset = 0; offset < branchTargets.Length; offset++) { // Create a basic block if this was a branch target or an instruction after a conditional branch if (branchTargets[offset]) { basicBlocks[offset] = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}", offset)); } } // Create catch clause stack if (body.HasExceptionHandlers) { // Exception catch handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { exceptionHandlers.Add(new ExceptionHandlerInfo(exceptionHandler)); if (exceptionHandler.HandlerType != ExceptionHandlerType.Catch) continue; var handlerStart = exceptionHandler.HandlerStart.Offset; var catchBlock = basicBlocks[handlerStart]; var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.CatchType)); // Extract exception LLVM.PositionBuilderAtEnd(builder2, catchBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, string.Empty); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, catchClass.Type.DefaultTypeLLVM, string.Empty); // Erase exception from exn.slot (it has been handled) LLVM.BuildStore(builder2, LLVM.ConstNull(@object.DefaultTypeLLVM), functionContext.ExceptionSlot); LLVM.BuildStore(builder2, LLVM.ConstInt(int32LLVM, 0, false), functionContext.ExceptionHandlerSelectorSlot); forwardStacks[handlerStart] = new[] { new StackValue(catchClass.Type.StackType, catchClass.Type, exceptionObject) }; } } PrepareScopes(functionContext, function); foreach (var instruction in body.Instructions) { try { // Check if any exception handlers might have changed if (body.HasExceptionHandlers) UpdateExceptionHandlers(functionContext, instruction); if (branchTargets[instruction.Offset]) UpdateBranching(functionContext, instruction); ProcessScopes(functionContext, instruction); // Reset states functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Implicit; EmitInstruction(functionContext, instruction); // If we do a jump, let's merge stack var flowControl = instruction.OpCode.FlowControl; if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) MergeStacks(functionContext, instruction); } catch (Exception e) { throw new InvalidOperationException(string.Format("Error while processing instruction {0} of method {1}", instruction, methodReference), e); } } if (body.HasExceptionHandlers) { // Place eh.resume block at the very end LLVM.MoveBasicBlockAfter(functionContext.ResumeExceptionBlock, LLVM.GetLastBasicBlock(functionGlobal)); } }
private ValueRef ResolveVirtualMethod(FunctionCompilerContext functionContext, ref Function targetMethod, ref StackValue thisObject) { // Get constrained class var constrainedClass = functionContext.ConstrainedClass; if (constrainedClass != null) functionContext.ConstrainedClass = null; var method = targetMethod.MethodReference.Resolve(); ValueRef resolvedMethod; if ((method.Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual) { // Build indices for GEP var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int) ObjectFields.RuntimeTypeInfo, false), // Access RTTI }; Class @class; // Process constrained class if (constrainedClass != null) { if (!constrainedClass.Type.TypeDefinitionCecil.IsValueType) { // If thisType is a reference type, dereference thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type, LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty), constrainedClass.Type.DefaultTypeLLVM, string.Empty)); } else { var matchingMethod = CecilExtensions.TryMatchMethod(constrainedClass, targetMethod.MethodReference, false); if (matchingMethod != null) { // If thisType is a value type and implements method, then ptr is passed unmodified targetMethod = matchingMethod; // Convert to appropriate type (if necessary) var refType = GetType(constrainedClass.Type.TypeReferenceCecil.MakeByReferenceType(), TypeState.Opaque); if (thisObject.StackType != StackValueType.Reference || thisObject.Type != refType) { thisObject = new StackValue(refType.StackType, refType, LLVM.BuildPointerCast(builder, thisObject.Value, refType.DefaultTypeLLVM, string.Empty)); } } else { // If thisType is a value type and doesn't implement method, dereference (note: already a pointer), box and pass as this if (constrainedClass.Type.StackType == StackValueType.Value) thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type, LLVM.BuildPointerCast(builder, thisObject.Value, LLVM.PointerType(constrainedClass.Type.DefaultTypeLLVM, 0), string.Empty)); else thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type, LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty), constrainedClass.Type.DefaultTypeLLVM, string.Empty)); thisObject = new StackValue(StackValueType.Object, constrainedClass.Type, BoxValueType(constrainedClass.Type, thisObject)); } } @class = constrainedClass; } else { @class = GetClass(thisObject.Type); } // TODO: Checking actual type stored in thisObject we might be able to statically resolve method? // If it's a byref value type, emit a normal call if (thisObject.Type.TypeReferenceCecil.IsByReference && GetType(((ByReferenceType)thisObject.Type.TypeReferenceCecil).ElementType, TypeState.Opaque).TypeDefinitionCecil.IsValueType && MemberEqualityComparer.Default.Equals(targetMethod.DeclaringType.TypeReferenceCecil, ((ByReferenceType)thisObject.Type.TypeReferenceCecil).ElementType)) { resolvedMethod = targetMethod.GeneratedValue; } else { // Get RTTI pointer var rttiPointer = LLVM.BuildInBoundsGEP(builder, thisObject.Value, indices, string.Empty); rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty); if (targetMethod.MethodReference.DeclaringType.Resolve().IsInterface) { // Interface call // Cast to object type (enough to have IMT) rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(GetClass(@object).GeneratedEETypeRuntimeLLVM), string.Empty); // Get method stored in IMT slot indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.InterfaceMethodTable, false), // Access IMT LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific IMT slot }; var imtEntry = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); var methodPointer = LLVM.BuildLoad(builder, imtEntry, string.Empty); // Resolve interface call // TODO: Improve resolveInterfaceCall(): if no match is found, it's likely due to covariance/contravariance, so we will need a fallback resolvedMethod = LLVM.BuildCall(builder, resolveInterfaceCallFunctionLLVM, new[] { //LLVM.ConstInt(int32LLVM, methodId, false), LLVM.ConstPointerCast(targetMethod.GeneratedValue, intPtrLLVM), methodPointer, }, string.Empty); resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty); } else { // Cast to expected RTTI type rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(@class.GeneratedEETypeRuntimeLLVM), string.Empty); // Virtual table call if (targetMethod.VirtualSlot == -1) { throw new InvalidOperationException("Trying to call a virtual method without slot."); } // Get method stored in vtable slot indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot }; var vtable = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty); resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty); } } } else if (method.HasPInvokeInfo) { // PInvoke behaves almost like a virtual call, but directly use given class vtable var @class = GetClass(targetMethod.DeclaringType); // Get method stored in vtable slot var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32LLVM, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot }; var vtable = LLVM.BuildInBoundsGEP(builder, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty); resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty); resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty); } else { // Normal call // Callvirt on non-virtual function is only done to force "this" NULL check // However, that's probably a part of the .NET spec that we want to skip for performance reasons, // so maybe we should keep this as is? resolvedMethod = targetMethod.GeneratedValue; } return resolvedMethod; }
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); }
private void EmitInstruction(FunctionCompilerContext functionContext, Instruction instruction) { var methodReference = functionContext.MethodReference; var stack = functionContext.Stack; var args = functionContext.Arguments; var locals = functionContext.Locals; var exceptionHandlers = functionContext.ExceptionHandlers; var opcode = instruction.OpCode.Code; switch (opcode) { case Code.Nop: { // TODO: Insert nop? Debugger step? // Check if there is a custom action Action<FunctionStack> instructionAction; if (InstructionActions.TryGetValue(instruction, out instructionAction)) { instructionAction(stack); } break; } case Code.Pop: { // Pop and discard last stack value stack.Pop(); break; } case Code.Dup: { // Readd last stack value var lastStackValue = stack[stack.Count - 1]; stack.Add(new StackValue(lastStackValue.StackType, lastStackValue.Type, lastStackValue.Value)); break; } case Code.Ret: { EmitRet(functionContext); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Call: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); // If calling a static method, make sure .cctor has been called if (!targetMethodReference.HasThis) EnsureClassInitialized(functionContext, GetClass(targetMethod.DeclaringType)); var overrideMethod = targetMethod.GeneratedValue; // PInvoke: go through ResolveVirtualMethod var resolvedMethod = targetMethodReference.Resolve(); if (resolvedMethod != null && resolvedMethod.HasPInvokeInfo) { StackValue thisObject = null; overrideMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject); } EmitCall(functionContext, targetMethod.Signature, overrideMethod); break; } case Code.Calli: { var callSite = (CallSite)instruction.Operand; // Generate function type var functionSignature = CreateFunctionSignature(methodReference, callSite); var functionType = CreateFunctionTypeLLVM(functionSignature); var methodPtr = stack.Pop(); var castedMethodPtr = LLVM.BuildPointerCast(builder, methodPtr.Value, LLVM.PointerType(functionType, 0), string.Empty); EmitCall(functionContext, functionSignature, castedMethodPtr); break; } case Code.Callvirt: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); var thisObject = stack[stack.Count - targetMethod.ParameterTypes.Length]; var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject); stack[stack.Count - targetMethod.ParameterTypes.Length] = thisObject; // Emit call EmitCall(functionContext, targetMethod.Signature, resolvedMethod); break; } case Code.Constrained: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); functionContext.ConstrainedClass = GetClass(typeReference); break; } case Code.Readonly: { break; } #region Obj opcodes (Initobj, Newobj, Stobj, Ldobj, etc...) case Code.Initobj: { var address = stack.Pop(); var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var type = GetType(typeReference, TypeState.StackComplete); EmitInitobj(address, type); break; } case Code.Newobj: { var ctorReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var ctor = GetFunction(ctorReference); var type = GetType(ctorReference.DeclaringType, TypeState.TypeComplete); EmitNewobj(functionContext, type, ctor); break; } case Code.Stobj: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete); EmitStobj(stack, type, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Ldobj: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete); EmitLdobj(stack, type, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } #endregion case Code.Cpblk: { var size = stack.Pop(); var source = stack.Pop(); var destination = stack.Pop(); var destinationValue = destination.Value; var sourceValue = source.Value; var sizeValue = size.Value; // Adjust types if (LLVM.TypeOf(destinationValue) != intPtrLLVM) destinationValue = LLVM.BuildPointerCast(builder, destinationValue, intPtrLLVM, string.Empty); if (LLVM.TypeOf(sourceValue) != intPtrLLVM) sourceValue = LLVM.BuildPointerCast(builder, sourceValue, intPtrLLVM, string.Empty); sizeValue = ConvertToInt(sizeValue, int32LLVM); var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)Intrinsics.memcpy, new[] { intPtrLLVM, intPtrLLVM, int32LLVM }); LLVM.BuildCall(builder, intrinsic, new[] { destinationValue, sourceValue, sizeValue, LLVM.ConstInt(int32LLVM, (functionContext.InstructionFlags & InstructionFlags.Unaligned) != 0 ? 1 : (ulong)intPtrSize, false), // alignment LLVM.ConstInt(LLVM.Int1TypeInContext(context), (functionContext.InstructionFlags & InstructionFlags.Volatile) != 0 ? 1UL : 0, false) // volatile } , string.Empty); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Sizeof: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var type = GetType(typeReference, TypeState.StackComplete); // Use type because @class might be null (i.e. void*) var objectSize = LLVM.SizeOf(type.DefaultTypeLLVM); objectSize = LLVM.BuildIntCast(builder, objectSize, int32LLVM, string.Empty); stack.Add(new StackValue(StackValueType.Int32, int32, objectSize)); break; } case Code.Localloc: { EmitLocalloc(stack); break; } case Code.Castclass: case Code.Isinst: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var @class = GetClass(typeReference); EmitIsOrCastclass(functionContext, stack, @class, opcode, instruction.Offset); break; } case Code.Ldtoken: { var token = instruction.Operand; Class runtimeHandleClass; var runtimeHandleValue = ValueRef.Empty; if (token is TypeReference) { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)token), TypeState.VTableEmitted); runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeTypeHandle).FullName)); if (type != null && type.Class != null) { runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM); runtimeHandleValue = LLVM.BuildInsertValue(builder, runtimeHandleValue, LLVM.ConstPointerCast(type.Class.GeneratedEETypeTokenLLVM, intPtrLLVM), 0, string.Empty); } else { // TODO: Support generic open types and special types such as void // We should issue a warning } } else if (token is FieldReference) { runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeFieldHandle).FullName)); var fieldReference = (FieldReference)token; var fieldDefinition = fieldReference.Resolve(); // HACK: Temporary hack so that RuntimeHelpers.InitialiseArray can properly initialize array constants from static struct with initial data. // For now we just pass the field address (instead of a real RuntimeFieldHandle. if (fieldDefinition.IsStatic && (fieldDefinition.Attributes & FieldAttributes.HasFieldRVA) != 0) { // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.StaticFields[fieldDefinition]; EmitLdsflda(stack, field); var fieldAddress = stack.Pop().Value; fieldAddress = LLVM.BuildPointerCast(builder, fieldAddress, intPtrLLVM, string.Empty); runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM); runtimeHandleValue = LLVM.BuildInsertValue(builder, runtimeHandleValue, fieldAddress, 0, string.Empty); } } else if (token is MethodReference) { runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeMethodHandle).FullName)); } else { throw new NotSupportedException("Invalid ldtoken operand."); } // Setup default value if (runtimeHandleValue == ValueRef.Empty) runtimeHandleValue = LLVM.ConstNull(runtimeHandleClass.Type.DataTypeLLVM); // Add indirection var runtimeHandleValueIndirect = LLVM.BuildAlloca(builderAlloca, runtimeHandleClass.Type.DataTypeLLVM, string.Empty); LLVM.BuildStore(builder, runtimeHandleValue, runtimeHandleValueIndirect); // TODO: Actually transform type to RTTI token. stack.Add(new StackValue(runtimeHandleClass.Type.StackType, runtimeHandleClass.Type, runtimeHandleValueIndirect)); break; } case Code.Ldftn: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); EmitLdftn(stack, targetMethod); break; } case Code.Ldvirtftn: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); var thisObject = stack.Pop(); var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject); stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, resolvedMethod, intPtrLLVM, string.Empty))); break; } #region Box/Unbox opcodes case Code.Box: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var type = GetType(typeReference, TypeState.VTableEmitted); // Only value types need to be boxed if (type.TypeDefinitionCecil.IsValueType) { EmitBoxValueType(stack, type); } break; } case Code.Unbox_Any: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var type = GetType(typeReference, TypeState.VTableEmitted); if (type.TypeDefinitionCecil.IsValueType) { EmitUnboxAnyValueType(stack, type); } else { // Should act as "castclass" on reference types goto case Code.Castclass; } break; } #endregion #region Array opcodes (Newarr, Ldlen, Stelem_Ref, etc...) case Code.Newarr: { var elementType = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.StackComplete); EmitNewarr(stack, elementType); break; } case Code.Ldlen: { EmitLdlen(stack); break; } case Code.Ldelema: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand), TypeState.Opaque); EmitLdelema(stack, type); break; } case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Any: case Code.Ldelem_Ref: { // TODO: Properly use opcode for type conversion EmitLdelem(stack); break; } case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Any: case Code.Stelem_Ref: { // TODO: Properly use opcode for type conversion EmitStelem(stack); break; } #endregion #region Argument opcodes (Ldarg, Ldarga, Starg, etc...) // Ldarg case Code.Ldarg_0: case Code.Ldarg_1: case Code.Ldarg_2: case Code.Ldarg_3: { var value = opcode - Code.Ldarg_0; EmitLdarg(stack, args, value); break; } case Code.Ldarg_S: case Code.Ldarg: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitLdarg(stack, args, value); break; } case Code.Ldarga: case Code.Ldarga_S: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitLdarga(stack, args, value); break; } case Code.Starg: case Code.Starg_S: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitStarg(stack, args, value); break; } case Code.Arglist: { // TODO: Implement this opcode //var value = LLVM.BuildVAArg(builder, , , string.Empty); var runtimeHandleType = GetType(corlib.MainModule.GetType(typeof(RuntimeArgumentHandle).FullName), TypeState.StackComplete); stack.Add(new StackValue(runtimeHandleType.StackType, runtimeHandleType, LLVM.ConstNull(runtimeHandleType.DataTypeLLVM))); break; } #endregion #region Load opcodes (Ldc, Ldstr, Ldloc, etc...) // Ldc_I4 case Code.Ldc_I4_M1: case Code.Ldc_I4_0: case Code.Ldc_I4_1: case Code.Ldc_I4_2: case Code.Ldc_I4_3: case Code.Ldc_I4_4: case Code.Ldc_I4_5: case Code.Ldc_I4_6: case Code.Ldc_I4_7: case Code.Ldc_I4_8: { var value = opcode - Code.Ldc_I4_0; EmitI4(stack, value); break; } case Code.Ldc_I4_S: { var value = (sbyte)instruction.Operand; EmitI4(stack, value); break; } case Code.Ldc_I4: { var value = (int)instruction.Operand; EmitI4(stack, value); break; } // Ldc_I8 case Code.Ldc_I8: { var value = (long)instruction.Operand; EmitI8(stack, value); break; } case Code.Ldc_R4: { var value = (float)instruction.Operand; EmitR4(stack, value); break; } case Code.Ldc_R8: { var value = (double)instruction.Operand; EmitR8(stack, value); break; } case Code.Ldstr: { var operand = (string)instruction.Operand; EmitLdstr(stack, operand); break; } case Code.Ldnull: { EmitLdnull(stack); break; } // Ldloc case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: { var localIndex = opcode - Code.Ldloc_0; EmitLdloc(stack, locals, localIndex); break; } case Code.Ldloc: case Code.Ldloc_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitLdloc(stack, locals, localIndex); break; } case Code.Ldloca: case Code.Ldloca_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitLdloca(stack, locals, localIndex); break; } case Code.Ldfld: case Code.Ldflda: { var fieldReference = (FieldReference)instruction.Operand; // Resolve type and field var type = GetType(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType), TypeState.TypeComplete); var field = type.Fields[fieldReference.Resolve()]; if (opcode == Code.Ldflda) { EmitLdflda(stack, field); } else { EmitLdfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; } break; } case Code.Ldsfld: case Code.Ldsflda: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.StaticFields[fieldReference.Resolve()]; EnsureClassInitialized(functionContext, @class); if (opcode == Code.Ldsflda) { EmitLdsflda(stack, field); } else { EmitLdsfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; } break; } #endregion #region Indirect opcodes (Stind, Ldind, etc...) case Code.Stind_I: case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: { EmitStind(functionContext, stack, opcode); break; } case Code.Ldind_I: case Code.Ldind_I1: case Code.Ldind_I2: case Code.Ldind_I4: case Code.Ldind_I8: case Code.Ldind_U1: case Code.Ldind_U2: case Code.Ldind_U4: case Code.Ldind_R4: case Code.Ldind_R8: case Code.Ldind_Ref: { EmitLdind(functionContext, stack, opcode); break; } #endregion #region Store opcodes (Stloc, etc...) // Stloc case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: { var localIndex = opcode - Code.Stloc_0; EmitStloc(stack, locals, localIndex); break; } case Code.Stloc: case Code.Stloc_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitStloc(stack, locals, localIndex); break; } case Code.Stfld: { var fieldReference = (FieldReference)instruction.Operand; // Resolve type and field var type = GetType(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType), TypeState.TypeComplete); var field = type.Fields[fieldReference.Resolve()]; EmitStfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Stsfld: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.StaticFields[fieldReference.Resolve()]; EnsureClassInitialized(functionContext, @class); EmitStsfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } #endregion #region Branching (Brtrue, Brfalse, Switch, etc...) case Code.Br: case Code.Br_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBr(functionContext.BasicBlocks[targetInstruction.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Brfalse: case Code.Brfalse_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBrfalse(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } case Code.Brtrue: case Code.Brtrue_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBrtrue(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } case Code.Switch: { var targets = (Instruction[])instruction.Operand; var operand = stack.Pop(); var @switch = LLVM.BuildSwitch(builder, operand.Value, functionContext.BasicBlocks[instruction.Next.Offset], (uint)targets.Length); for (int i = 0; i < targets.Length; ++i) { var target = targets[i]; LLVM.AddCase(@switch, LLVM.ConstInt(int32LLVM, (ulong)i, false), functionContext.BasicBlocks[target.Offset]); } functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } #endregion #region Conditional branching (Beq, Bgt, etc...) case Code.Beq: case Code.Beq_S: case Code.Bge: case Code.Bge_S: case Code.Bgt: case Code.Bgt_S: case Code.Ble: case Code.Ble_S: case Code.Blt: case Code.Blt_S: case Code.Bne_Un: case Code.Bne_Un_S: case Code.Bge_Un: case Code.Bge_Un_S: case Code.Bgt_Un: case Code.Bgt_Un_S: case Code.Ble_Un: case Code.Ble_Un_S: case Code.Blt_Un: case Code.Blt_Un_S: { var targetInstruction = (Instruction)instruction.Operand; EmitConditionalBranch(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset], opcode); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } #endregion #region Comparison opcodes (Ceq, Cgt, etc...) case Code.Ceq: case Code.Cgt: case Code.Cgt_Un: case Code.Clt: case Code.Clt_Un: { EmitComparison(stack, opcode); break; } #endregion #region Conversion opcodes (Conv_U, Conv_I, etc...) case Code.Conv_U: case Code.Conv_I: case Code.Conv_U1: case Code.Conv_I1: case Code.Conv_U2: case Code.Conv_I2: case Code.Conv_U4: case Code.Conv_I4: case Code.Conv_U8: case Code.Conv_I8: case Code.Conv_R4: case Code.Conv_R8: case Code.Conv_R_Un: case Code.Conv_Ovf_U: case Code.Conv_Ovf_I: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_I1: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_U_Un: case Code.Conv_Ovf_I_Un: case Code.Conv_Ovf_U1_Un: case Code.Conv_Ovf_I1_Un: case Code.Conv_Ovf_U2_Un: case Code.Conv_Ovf_I2_Un: case Code.Conv_Ovf_U4_Un: case Code.Conv_Ovf_I4_Un: case Code.Conv_Ovf_U8_Un: case Code.Conv_Ovf_I8_Un: { EmitConv(stack, opcode); break; } #endregion #region Unary operation opcodes (Neg, Not, etc...) case Code.Neg: case Code.Not: { EmitUnaryOperation(stack, opcode); break; } #endregion #region Binary operation opcodes (Add, Sub, etc...) case Code.Add: case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.Sub: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Mul: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: case Code.Div: case Code.Div_Un: case Code.Rem: case Code.Rem_Un: case Code.Shl: case Code.Shr: case Code.Shr_Un: case Code.Xor: case Code.Or: case Code.And: { EmitBinaryOperation(functionContext, stack, opcode); break; } #endregion #region Exception handling opcodes (Leave, Endfinally, etc...) case Code.Throw: { var exceptionObject = stack.Pop(); // Throw exception // TODO: Update callstack GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Rethrow: { // Find exception that was on stack at beginning of this catch clause var currentCatchClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset); if (currentCatchClause == null || currentCatchClause.Source.HandlerType != ExceptionHandlerType.Catch) throw new InvalidOperationException("Can't find catch clause matching this rethrow instruction."); var catchClauseStack = functionContext.ForwardStacks[currentCatchClause.Source.HandlerStart.Offset]; var exceptionObject = catchClauseStack[0]; // Rethrow exception GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Leave: case Code.Leave_S: { // Evaluation stack is cleared stack.Clear(); // Default target (if we jump inside the exception clause) var targetInstruction = (Instruction)instruction.Operand; GenerateLeave(functionContext.ActiveTryHandlers, targetInstruction, functionContext.EndfinallyJumpTarget, functionContext.BasicBlocks); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Endfinally: { var currentFinallyClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset); if (currentFinallyClause == null) throw new InvalidOperationException("Can't find exception clause matching this endfinally/endfault instruction."); EmitEndfinally(functionContext, currentFinallyClause); break; } #endregion #region Instruction flags (Unaligned, Volatile) case Code.Volatile: functionContext.InstructionFlags |= InstructionFlags.Volatile; break; case Code.Unaligned: functionContext.InstructionFlags |= InstructionFlags.Unaligned; break; #endregion default: throw new NotImplementedException(string.Format("Opcode {0} not implemented.", instruction.OpCode)); } }
private void UpdateExceptionHandlers(FunctionCompilerContext functionContext, Instruction instruction) { var functionGlobal = functionContext.Function.GeneratedValue; var exceptionHandlers = functionContext.ExceptionHandlers; var activeTryHandlers = functionContext.ActiveTryHandlers; var methodReference = functionContext.MethodReference; bool exceptionHandlersChanged = false; // Exit finished exception handlers for (int index = activeTryHandlers.Count - 1; index >= 0; index--) { var exceptionHandler = activeTryHandlers[index]; if (instruction == exceptionHandler.Source.TryEnd) { activeTryHandlers.RemoveAt(index); exceptionHandlersChanged = true; } else break; } // Add new exception handlers for (int index = exceptionHandlers.Count - 1; index >= 0; index--) { var exceptionHandler = exceptionHandlers[index]; if (instruction == exceptionHandler.Source.TryStart) { var catchDispatchBlock = new BasicBlockRef(); if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Catch) { catchDispatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "catch.dispatch"); LLVM.PositionBuilderAtEnd(builder2, catchDispatchBlock); var catchBlock = functionContext.BasicBlocks[exceptionHandler.Source.HandlerStart.Offset]; var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.Source.CatchType)); // Compare exception type var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel"); var ehtypeIdFor = LLVM.IntrinsicGetDeclaration(module, (uint)Intrinsics.eh_typeid_for, new TypeRef[0]); var ehtypeid = LLVM.BuildCall(builder2, ehtypeIdFor, new[] { LLVM.ConstBitCast(catchClass.GeneratedRuntimeTypeInfoGlobal, intPtrType) }, string.Empty); // Jump to catch clause if type matches. // Otherwise, go to next exception handler dispatch block (if any), or resume exception block (TODO) var ehtypeComparisonResult = LLVM.BuildICmp(builder2, IntPredicate.IntEQ, ehselectorValue, ehtypeid, string.Empty); LLVM.BuildCondBr(builder2, ehtypeComparisonResult, catchBlock, activeTryHandlers.Count > 0 ? activeTryHandlers.Last().CatchDispatch : functionContext.ResumeExceptionBlock); } else if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Finally) { catchDispatchBlock = functionContext.BasicBlocks[exceptionHandler.Source.HandlerStart.Offset]; } exceptionHandler.CatchDispatch = catchDispatchBlock; activeTryHandlers.Add(exceptionHandler); exceptionHandlersChanged = true; } } if (exceptionHandlersChanged) { // Need to generate a new landing pad for (int index = activeTryHandlers.Count - 1; index >= 0; index--) { var exceptionHandler = activeTryHandlers[index]; switch (exceptionHandler.Source.HandlerType) { case ExceptionHandlerType.Catch: break; } } if (activeTryHandlers.Count > 0) { //var handlerStart = exceptionHandlers.Last().HandlerStart.Offset; //functionContext.LandingPadBlock = basicBlocks[handlerStart]; // Prepare landing pad block functionContext.LandingPadBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "landingpad"); LLVM.PositionBuilderAtEnd(builder2, functionContext.LandingPadBlock); var landingPad = LLVM.BuildLandingPad(builder2, caughtResultType, sharpPersonalityFunction, 1, string.Empty); // Extract exception, and store it in exn.slot var exceptionObject = LLVM.BuildExtractValue(builder2, landingPad, 0, string.Empty); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, @object.Class.Type.DefaultType, string.Empty); LLVM.BuildStore(builder2, exceptionObject, functionContext.ExceptionSlot); // Extract selector slot, and store it in ehselector.slot var exceptionType = LLVM.BuildExtractValue(builder2, landingPad, 1, string.Empty); LLVM.BuildStore(builder2, exceptionType, functionContext.ExceptionHandlerSelectorSlot); // Let future finally clause know that we need to propage exception after they are executed // A future Leave instruction should clear that if necessary LLVM.BuildStore(builder2, LLVM.ConstInt(int32Type, unchecked((ulong)-1), true), functionContext.EndfinallyJumpTarget); // Add jump to catch dispatch block or finally block var lastActiveTryHandler = activeTryHandlers.Last(); LLVM.BuildBr(builder2, lastActiveTryHandler.CatchDispatch); // Filter exceptions type by type for (int index = activeTryHandlers.Count - 1; index >= 0; index--) { var exceptionHandler = activeTryHandlers[index]; // Add landing pad type clause if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Catch) { var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.Source.CatchType)); LLVM.AddClause(landingPad, LLVM.ConstBitCast(catchClass.GeneratedRuntimeTypeInfoGlobal, intPtrType)); } else if (exceptionHandler.Source.HandlerType == ExceptionHandlerType.Finally) { LLVM.SetCleanup(landingPad, true); } } } else { functionContext.LandingPadBlock = new BasicBlockRef(); } } }
/// <summary> /// Compiles the given method definition. /// </summary> /// <param name="method">The method.</param> /// <param name="function">The function.</param> /// <exception cref="System.NotSupportedException"></exception> /// <exception cref="System.NotImplementedException"></exception> /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception> private void CompileFunction(MethodReference methodReference, Function function) { var method = methodReference.Resolve(); var body = method.Body; var codeSize = body != null ? body.CodeSize : 0; var functionGlobal = function.GeneratedValue; var functionContext = new FunctionCompilerContext(function); functionContext.BasicBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock); if (body == null && (method.ImplAttributes & MethodImplAttributes.Runtime) != 0) { var declaringClass = GetClass(function.DeclaringType); // Generate IL for various methods if (declaringClass.BaseType != null && declaringClass.BaseType.Type.TypeReference.FullName == typeof(MulticastDelegate).FullName) { body = GenerateDelegateMethod(method, declaringClass); if (body == null) return; codeSize = UpdateOffsets(body); } } if (body == null) return; var numParams = method.Parameters.Count; // Create stack, locals and args var stack = new List<StackValue>(body.MaxStackSize); var locals = new List<StackValue>(body.Variables.Count); var args = new List<StackValue>(numParams); var exceptionHandlers = new List<ExceptionHandlerInfo>(); var activeTryHandlers = new List<ExceptionHandlerInfo>(); functionContext.Stack = stack; functionContext.Locals = locals; functionContext.Arguments = args; functionContext.ExceptionHandlers = exceptionHandlers; functionContext.ActiveTryHandlers = activeTryHandlers; // Process locals foreach (var local in body.Variables) { // TODO: Anything to do on pinned objects? //if (local.IsPinned) // throw new NotSupportedException(); var type = CreateType(ResolveGenericsVisitor.Process(methodReference, local.VariableType)); locals.Add(new StackValue(type.StackType, type, LLVM.BuildAlloca(builder, type.DefaultType, local.Name))); } // Process args for (int index = 0; index < function.ParameterTypes.Length; index++) { var argType = function.ParameterTypes[index]; var arg = LLVM.GetParam(functionGlobal, (uint)index); // Copy argument on stack var storage = LLVM.BuildAlloca(builder, argType.DefaultType, "arg" + index.ToString()); LLVM.BuildStore(builder, arg, storage); args.Add(new StackValue(argType.StackType, argType, storage)); } // Some wasted space due to unused offsets, but we only keep one so it should be fine. // TODO: Reuse same allocated instance per thread, and grow it only if necessary var branchTargets = new bool[codeSize]; var basicBlocks = new BasicBlockRef[codeSize]; var forwardStacks = new StackValue[codeSize][]; functionContext.BasicBlocks = basicBlocks; functionContext.ForwardStacks = forwardStacks; // Find branch targets (which will require PHI node for stack merging) for (int index = 0; index < body.Instructions.Count; index++) { var instruction = body.Instructions[index]; var flowControl = instruction.OpCode.FlowControl; // Process branch targets if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) { var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand }; foreach (var target in targets) { // Operand Target can be reached branchTargets[target.Offset] = true; } } // Need to enforce a block to be created for the next instruction after a conditional branch // TODO: Break? if (flowControl == FlowControl.Cond_Branch) { if (instruction.Next != null) branchTargets[instruction.Next.Offset] = true; } } // Setup exception handling if (body.HasExceptionHandlers) { // Add an "ehselector.slot" i32 local, and a "exn.slot" Object reference local functionContext.ExceptionHandlerSelectorSlot = LLVM.BuildAlloca(builder, int32Type, "ehselector.slot"); functionContext.ExceptionSlot = LLVM.BuildAlloca(builder, @object.DefaultType, "exn.slot"); functionContext.EndfinallyJumpTarget = LLVM.BuildAlloca(builder, int32Type, "endfinally.jumptarget"); // Create resume exception block functionContext.ResumeExceptionBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "eh.resume"); LLVM.PositionBuilderAtEnd(builder2, functionContext.ResumeExceptionBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, "exn"); var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel"); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, intPtrType, "exn"); var landingPadValue = LLVM.BuildInsertValue(builder2, LLVM.GetUndef(caughtResultType), exceptionObject, 0, "lpad.val"); landingPadValue = LLVM.BuildInsertValue(builder2, landingPadValue, ehselectorValue, 1, "lpad.val"); LLVM.BuildResume(builder2, landingPadValue); // Exception handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { branchTargets[exceptionHandler.HandlerStart.Offset] = true; } } // Create basic block // TODO: Could be done during previous pass for (int offset = 0; offset < branchTargets.Length; offset++) { // Create a basic block if this was a branch target or an instruction after a conditional branch if (branchTargets[offset]) { basicBlocks[offset] = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}", offset)); } } // Create catch clause stack if (body.HasExceptionHandlers) { // Exception handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { exceptionHandlers.Add(new ExceptionHandlerInfo(exceptionHandler)); if (exceptionHandler.HandlerType != ExceptionHandlerType.Catch) continue; var handlerStart = exceptionHandler.HandlerStart.Offset; var catchBlock = basicBlocks[handlerStart]; var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.CatchType)); // Extract exception LLVM.PositionBuilderAtEnd(builder2, catchBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, string.Empty); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, catchClass.Type.DefaultType, string.Empty); // Erase exception from exn.slot (it has been handled) LLVM.BuildStore(builder2, LLVM.ConstNull(@object.DefaultType), functionContext.ExceptionSlot); LLVM.BuildStore(builder2, LLVM.ConstInt(int32Type, 0, false), functionContext.ExceptionHandlerSelectorSlot); forwardStacks[handlerStart] = new[] { new StackValue(catchClass.Type.StackType, catchClass.Type, exceptionObject) }; } } foreach (var instruction in body.Instructions) { try { // Check if any exception handlers might have changed if (body.HasExceptionHandlers) UpdateExceptionHandlers(functionContext, instruction); if (branchTargets[instruction.Offset]) UpdateBranching(functionContext, instruction); // Reset states functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Implicit; EmitInstruction(functionContext, instruction); // If we do a jump, let's merge stack var flowControl = instruction.OpCode.FlowControl; if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) MergeStacks(functionContext, instruction); } catch (Exception e) { throw new InvalidOperationException(string.Format("Error while processing instruction {0} of method {1}", instruction, methodReference), e); } } }
private void EmitCall(FunctionCompilerContext functionContext, FunctionSignature targetMethod, ValueRef overrideMethod) { var stack = functionContext.Stack; // Build argument list var targetNumParams = targetMethod.ParameterTypes.Length; var args = new ValueRef[targetNumParams]; for (int index = 0; index < targetNumParams; index++) { // TODO: Casting/implicit conversion? var stackItem = stack[stack.Count - targetNumParams + index]; args[index] = ConvertFromStackToLocal(targetMethod.ParameterTypes[index], stackItem); } // Remove arguments from stack stack.RemoveRange(stack.Count - targetNumParams, targetNumParams); // Invoke method ValueRef callResult; var actualMethod = overrideMethod; callResult = GenerateInvoke(functionContext, actualMethod, args); // Mark method as needed (if non-virtual call) if (LLVM.IsAGlobalVariable(actualMethod).Value != IntPtr.Zero) { LLVM.SetLinkage(actualMethod, Linkage.ExternalLinkage); } // Push return result on stack if (targetMethod.ReturnType.TypeReference.MetadataType != MetadataType.Void) { // Convert return value from local to stack value var returnValue = ConvertFromLocalToStack(targetMethod.ReturnType, callResult); // Add value to stack stack.Add(new StackValue(targetMethod.ReturnType.StackType, targetMethod.ReturnType, returnValue)); } }
private void EmitBinaryOperation(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode) { var functionGlobal = functionContext.FunctionGlobal; var operand2 = stack.Pop(); var operand1 = stack.Pop(); var value1 = operand1.Value; var value2 = operand2.Value; StackValue outputOperandType; bool isShiftOperation = false; bool isIntegerOperation = false; // Detect shift and integer operations switch (opcode) { case Code.Shl: case Code.Shr: case Code.Shr_Un: isShiftOperation = true; break; case Code.Xor: case Code.Or: case Code.And: case Code.Div_Un: case Code.Not: isIntegerOperation = true; break; } if (isShiftOperation) // Shift operations are specials { switch (operand2.StackType) { case StackValueType.Int32: break; case StackValueType.NativeInt: value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty); break; default: goto InvalidBinaryOperation; } // Check first operand, and convert second operand to match first one switch (operand1.StackType) { case StackValueType.Int32: value2 = LLVM.BuildIntCast(builder, value2, int32LLVM, string.Empty); break; case StackValueType.Int64: value2 = LLVM.BuildIntCast(builder, value2, int64LLVM, string.Empty); break; case StackValueType.NativeInt: value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty); value2 = LLVM.BuildIntCast(builder, value2, nativeIntLLVM, string.Empty); break; default: goto InvalidBinaryOperation; } // Output type is determined by first operand outputOperandType = operand1; } else if (operand1.StackType == operand2.StackType) // Diagonal { // Check type switch (operand1.StackType) { case StackValueType.Int32: case StackValueType.Int64: case StackValueType.Float: outputOperandType = operand1; break; case StackValueType.NativeInt: value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty); value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty); outputOperandType = operand1; break; case StackValueType.Reference: if (opcode != Code.Sub && opcode != Code.Sub_Ovf_Un) goto InvalidBinaryOperation; value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty); value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty); outputOperandType = new StackValue(StackValueType.NativeInt, intPtr, ValueRef.Empty); break; default: throw new InvalidOperationException(string.Format("Binary operations are not allowed on {0}.", operand1.StackType)); } } else if (operand1.StackType == StackValueType.NativeInt && operand2.StackType == StackValueType.Int32) { value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntLLVM, string.Empty); value2 = LLVM.BuildIntCast(builder, value2, nativeIntLLVM, string.Empty); outputOperandType = operand1; } else if (operand1.StackType == StackValueType.Int32 && operand2.StackType == StackValueType.NativeInt) { value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntLLVM, string.Empty); value1 = LLVM.BuildIntCast(builder, value1, nativeIntLLVM, string.Empty); outputOperandType = operand2; } else if (!isIntegerOperation && (operand1.StackType == StackValueType.Reference || operand2.StackType == StackValueType.Reference)) // ref + [i32, nativeint] or [i32, nativeint] + ref { StackValue operandRef, operandInt; ValueRef valueRef, valueInt; if (operand2.StackType == StackValueType.Reference) { operandRef = operand2; operandInt = operand1; valueRef = value2; valueInt = value1; } else { operandRef = operand1; operandInt = operand2; valueRef = value1; valueInt = value2; } switch (operandInt.StackType) { case StackValueType.Int32: break; case StackValueType.NativeInt: valueInt = LLVM.BuildPtrToInt(builder, valueInt, nativeIntLLVM, string.Empty); break; default: goto InvalidBinaryOperation; } switch (opcode) { case Code.Add: case Code.Add_Ovf_Un: break; case Code.Sub: case Code.Sub_Ovf: if (operand2.StackType == StackValueType.Reference) goto InvalidBinaryOperation; valueInt = LLVM.BuildNeg(builder, valueInt, string.Empty); break; default: goto InvalidBinaryOperation; } // If necessary, cast to i8* var valueRefType = LLVM.TypeOf(valueRef); if (valueRefType != intPtrLLVM) valueRef = LLVM.BuildPointerCast(builder, valueRef, intPtrLLVM, string.Empty); valueRef = LLVM.BuildGEP(builder, valueRef, new[] {valueInt}, string.Empty); // Cast back to original type if (valueRefType != intPtrLLVM) valueRef = LLVM.BuildPointerCast(builder, valueRef, valueRefType, string.Empty); stack.Add(new StackValue(StackValueType.Reference, operandRef.Type, valueRef)); // Early exit return; } else { goto InvalidBinaryOperation; } ValueRef result; // Perform binary operation if (operand1.StackType == StackValueType.Float) { switch (opcode) { case Code.Add: result = LLVM.BuildFAdd(builder, value1, value2, string.Empty); break; case Code.Sub: result = LLVM.BuildFSub(builder, value1, value2, string.Empty); break; case Code.Mul: result = LLVM.BuildFMul(builder, value1, value2, string.Empty); break; case Code.Div: result = LLVM.BuildFDiv(builder, value1, value2, string.Empty); break; case Code.Rem: result = LLVM.BuildFRem(builder, value1, value2, string.Empty); break; default: goto InvalidBinaryOperation; } } else { // Special case: char is size 1, not 2! if (CharUsesUTF8) { if (opcode == Code.Add && operand1.Type.TypeReferenceCecil.FullName == typeof(char*).FullName) { value2 = LLVM.BuildLShr(builder, value2, LLVM.ConstInt(int32LLVM, 1, false), string.Empty); } else if (opcode == Code.Add && operand2.Type.TypeReferenceCecil.FullName == typeof(char*).FullName) { value1 = LLVM.BuildLShr(builder, value1, LLVM.ConstInt(int32LLVM, 1, false), string.Empty); } } switch (opcode) { case Code.Add: result = LLVM.BuildAdd(builder, value1, value2, string.Empty); break; case Code.Sub: result = LLVM.BuildSub(builder, value1, value2, string.Empty); break; case Code.Mul: result = LLVM.BuildMul(builder, value1, value2, string.Empty); break; case Code.Div: result = LLVM.BuildSDiv(builder, value1, value2, string.Empty); break; case Code.Div_Un: result = LLVM.BuildUDiv(builder, value1, value2, string.Empty); break; case Code.Rem: result = LLVM.BuildSRem(builder, value1, value2, string.Empty); break; case Code.Rem_Un: result = LLVM.BuildURem(builder, value1, value2, string.Empty); break; case Code.Shl: result = LLVM.BuildShl(builder, value1, value2, string.Empty); break; case Code.Shr: result = LLVM.BuildAShr(builder, value1, value2, string.Empty); break; case Code.Shr_Un: result = LLVM.BuildLShr(builder, value1, value2, string.Empty); break; case Code.And: result = LLVM.BuildAnd(builder, value1, value2, string.Empty); break; case Code.Or: result = LLVM.BuildOr(builder, value1, value2, string.Empty); break; case Code.Xor: result = LLVM.BuildXor(builder, value1, value2, string.Empty); break; case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: { Intrinsics intrinsicId; switch (opcode) { case Code.Add_Ovf: intrinsicId = Intrinsics.sadd_with_overflow; break; case Code.Add_Ovf_Un: intrinsicId = Intrinsics.uadd_with_overflow; break; case Code.Sub_Ovf: intrinsicId = Intrinsics.ssub_with_overflow; break; case Code.Sub_Ovf_Un: intrinsicId = Intrinsics.usub_with_overflow; break; case Code.Mul_Ovf: intrinsicId = Intrinsics.smul_with_overflow; break; case Code.Mul_Ovf_Un: intrinsicId = Intrinsics.umul_with_overflow; break; default: throw new ArgumentOutOfRangeException(); } var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)intrinsicId, new[] {LLVM.TypeOf(value1)}); var overflowResult = LLVM.BuildCall(builder, intrinsic, new[] {value1, value2}, string.Empty); var hasOverflow = LLVM.BuildExtractValue(builder, overflowResult, 1, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); var overflowBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "overflow"); LLVM.MoveBasicBlockAfter(overflowBlock, LLVM.GetInsertBlock(builder)); LLVM.MoveBasicBlockAfter(nextBlock, overflowBlock); LLVM.BuildCondBr(builder, hasOverflow, overflowBlock, nextBlock); LLVM.PositionBuilderAtEnd(builder, overflowBlock); // Create OverflowException object var overflowExceptionClass = GetClass(corlib.MainModule.GetType(typeof(OverflowException).FullName)); EmitNewobj(functionContext, overflowExceptionClass.Type, overflowExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0)); var overflowException = stack.Pop(); GenerateInvoke(functionContext, throwExceptionFunctionLLVM, new[] {LLVM.BuildPointerCast(builder, overflowException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunctionLLVM, 0)), string.Empty)}); LLVM.BuildUnreachable(builder); functionContext.BasicBlock = nextBlock; LLVM.PositionBuilderAtEnd(builder, nextBlock); result = LLVM.BuildExtractValue(builder, overflowResult, 0, string.Empty); break; } default: goto InvalidBinaryOperation; } if (CharUsesUTF8) { if (opcode == Code.Sub && operand1.Type.TypeReferenceCecil.FullName == typeof(char*).FullName && operand2.Type.TypeReferenceCecil.FullName == typeof(char*).FullName) { result = LLVM.BuildLShr(builder, result, LLVM.ConstInt(int32LLVM, 1, false), string.Empty); } } } Type outputType; switch (outputOperandType.StackType) { case StackValueType.Int32: case StackValueType.Int64: case StackValueType.Float: // No output conversion required, as it could only have been from same input types (non-shift) or operand 1 (shift) outputType = operand1.Type; break; case StackValueType.NativeInt: outputType = intPtr; result = LLVM.BuildIntToPtr(builder, result, intPtrLLVM, string.Empty); break; case StackValueType.Reference: result = LLVM.BuildIntToPtr(builder, result, intPtrLLVM, string.Empty); // Get type from one of its operand (if output is reference type, one of the two operand must be too) if (operand1.StackType == StackValueType.Reference) outputType = operand1.Type; else if (operand2.StackType == StackValueType.Reference) outputType = operand2.Type; else goto InvalidBinaryOperation; break; default: goto InvalidBinaryOperation; } stack.Add(new StackValue(outputOperandType.StackType, outputOperandType.Type, result)); return; InvalidBinaryOperation: throw new InvalidOperationException(string.Format("Binary operation {0} between {1} and {2} is not supported.", opcode, operand1.StackType, operand2.StackType)); }
/// <summary> /// Merges all the stacks of this instruction targets. /// </summary> /// <param name="functionContext">The function context.</param> /// <param name="instruction">The instruction.</param> /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception> private void MergeStacks(FunctionCompilerContext functionContext, Instruction instruction) { var forwardStacks = functionContext.ForwardStacks; var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand }; foreach (var target in targets) { // Backward jump? Make sure stack was properly created by a previous forward jump, or empty if (target.Offset < instruction.Offset) { var forwardStack = forwardStacks[target.Offset]; if (forwardStack != null && forwardStack.Length > 0) throw new InvalidOperationException("Backward jump with a non-empty stack unknown target."); } // Merge stack (add PHI incoming) MergeStack(functionContext.Stack, functionContext.BasicBlock, ref forwardStacks[target.Offset], functionContext.BasicBlocks[target.Offset]); } }
private ValueRef ResolveVirtualMethod(FunctionCompilerContext functionContext, ref Function targetMethod, ref StackValue thisObject) { ValueRef resolvedMethod; if ((targetMethod.MethodReference.Resolve().Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual) { // Build indices for GEP var indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int) ObjectFields.RuntimeTypeInfo, false), // Access RTTI }; Class @class; var constrainedClass = functionContext.ConstrainedClass; if (constrainedClass != null) { // Reset state functionContext.ConstrainedClass = null; if (!constrainedClass.Type.TypeReference.IsValueType) { // If thisType is a reference type, dereference thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type, LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty), constrainedClass.Type.DefaultType, string.Empty)); } else { var matchingMethod = CecilExtensions.TryMatchMethod(constrainedClass, targetMethod.MethodReference, false); if (matchingMethod != null) { // If thisType is a value type and implements method, then ptr is passed unmodified targetMethod = matchingMethod; // Convert to appropriate type (if necessary) var refType = GetType(constrainedClass.Type.TypeReference.MakeByReferenceType()); if (thisObject.StackType != StackValueType.Reference || thisObject.Type != refType) { thisObject = new StackValue(refType.StackType, refType, LLVM.BuildPointerCast(builder, thisObject.Value, refType.DefaultType, string.Empty)); } } else { // If thisType is a value type and doesn't implement method, dereference, box and pass as this thisObject = new StackValue(constrainedClass.Type.StackType, constrainedClass.Type, LLVM.BuildPointerCast(builder, LLVM.BuildLoad(builder, thisObject.Value, string.Empty), constrainedClass.Type.DefaultType, string.Empty)); thisObject = new StackValue(StackValueType.Object, constrainedClass.Type, BoxValueType(constrainedClass, thisObject)); } } @class = constrainedClass; } else { @class = GetClass(thisObject.Type); } // TODO: Checking actual type stored in thisObject we might be able to statically resolve method? // If it's a byref value type, emit a normal call if (thisObject.Type.TypeReference.IsByReference && thisObject.Type.TypeReference.GetElementType().IsValueType && MemberEqualityComparer.Default.Equals(targetMethod.DeclaringType.TypeReference, thisObject.Type.TypeReference.GetElementType())) { resolvedMethod = targetMethod.GeneratedValue; } else { // Get RTTI pointer var rttiPointer = LLVM.BuildInBoundsGEP(builder, thisObject.Value, indices, string.Empty); rttiPointer = LLVM.BuildLoad(builder, rttiPointer, string.Empty); // Cast to expected RTTI type rttiPointer = LLVM.BuildPointerCast(builder, rttiPointer, LLVM.TypeOf(@class.GeneratedRuntimeTypeInfoGlobal), string.Empty); if (targetMethod.MethodReference.DeclaringType.Resolve().IsInterface) { // Interface call // Get method stored in IMT slot indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int) RuntimeTypeInfoFields.InterfaceMethodTable, false), // Access IMT LLVM.ConstInt(int32Type, (ulong) targetMethod.VirtualSlot, false), // Access specific IMT slot }; var imtEntry = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); var methodPointer = LLVM.BuildLoad(builder, imtEntry, string.Empty); // TODO: Compare method ID and iterate in the linked list until the correct match is found // If no match is found, it's likely due to covariance/contravariance, so we will need a fallback var methodId = GetMethodId(targetMethod.MethodReference); // Resolve interface call resolvedMethod = LLVM.BuildCall(builder, resolveInterfaceCallFunction, new[] { LLVM.ConstInt(int32Type, methodId, false), methodPointer, }, string.Empty); resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty); } else { // Virtual table call // Get method stored in vtable slot indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int) RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32Type, (ulong) targetMethod.VirtualSlot, false), // Access specific vtable slot }; var vtable = LLVM.BuildInBoundsGEP(builder, rttiPointer, indices, string.Empty); resolvedMethod = LLVM.BuildLoad(builder, vtable, string.Empty); resolvedMethod = LLVM.BuildPointerCast(builder, resolvedMethod, LLVM.PointerType(targetMethod.FunctionType, 0), string.Empty); } } } else { // Normal call // Callvirt on non-virtual function is only done to force "this" NULL check // However, that's probably a part of the .NET spec that we want to skip for performance reasons, // so maybe we should keep this as is? resolvedMethod = targetMethod.GeneratedValue; } return resolvedMethod; }
private void EmitStind(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode) { var value = stack.Pop(); var address = stack.Pop(); // Determine type Type type; switch (opcode) { case Code.Stind_I: type = intPtr; break; case Code.Stind_I1: type = int8; break; case Code.Stind_I2: type = int16; break; case Code.Stind_I4: type = int32; break; case Code.Stind_I8: type = int64; break; case Code.Stind_R4: type = @float; break; case Code.Stind_R8: type = @double; break; case Code.Stind_Ref: type = value.Type; break; default: throw new ArgumentException("opcode"); } if (CharUsesUTF8) { if (opcode == Code.Stind_I2 && address.Type.TypeReferenceCecil.FullName == typeof(char*).FullName) { type = int8; } } // Convert to local type var sourceValue = ConvertFromStackToLocal(type, value); // Store value at address var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(LLVM.TypeOf(sourceValue), 0), string.Empty); StoreValue(type.StackType, sourceValue, pointerCast, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; }
/// <summary> /// Update branching before emitting instruction. /// </summary> /// <param name="functionContext"></param> /// <param name="instruction"></param> private void UpdateBranching(FunctionCompilerContext functionContext, Instruction instruction) { var previousBasicBlock = functionContext.BasicBlock; var stack = functionContext.Stack; var basicBlocks = functionContext.BasicBlocks; var forwardStacks = functionContext.ForwardStacks; functionContext.BasicBlock = basicBlocks[instruction.Offset]; var forwardStack = forwardStacks[instruction.Offset]; if (functionContext.FlowingNextInstructionMode == FlowingNextInstructionMode.Implicit) { // Add a jump from previous block to new block LLVM.BuildBr(builder, functionContext.BasicBlock); } if (functionContext.FlowingNextInstructionMode != FlowingNextInstructionMode.None) { // If flowing either automatically or explicitely, // flow stack and build PHI nodes MergeStack(stack, previousBasicBlock, ref forwardStack, functionContext.BasicBlock); forwardStacks[instruction.Offset] = forwardStack; } // Clear stack stack.Clear(); // Try to restore stack from previously reached forward jump if (forwardStack != null) { // Restoring stack as it was during one of previous forward jump stack.AddRange(forwardStack); } else { // TODO: Actually, need to restore stack from one of previous forward jump instruction, if any // (if only backward jumps, spec says it should be empty to avoid multi-pass IL scanning, // but that's something we could also support later -- Mono doesn't but MS.NET does) } // Position builder to write at beginning of new block LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock); }
private void EmitNewobj(FunctionCompilerContext functionContext, Type type, Function ctor) { var stack = functionContext.Stack; // Make sure .cctor has been called EnsureClassInitialized(functionContext, GetClass(type)); // String is a special case: we redirect to matching CreateString function, // and InternalAllocateStr will do the actual allocation if (type.TypeReferenceCecil.FullName == typeof(string).FullName) { var targetMethod = type.Class.Functions.First(x => x.MethodReference.Name == "CreateString" && CompareParameterTypes(x.ParameterTypes, ctor.ParameterTypes)); // Insert a null "this" pointer (note: CreateString would probably be better as static to avoid that) functionContext.Stack.Insert(functionContext.Stack.Count - (targetMethod.ParameterTypes.Length - 1), new StackValue(type.StackType, type, LLVM.ConstNull(type.DefaultTypeLLVM))); EmitCall(functionContext, targetMethod.Signature, targetMethod.GeneratedValue); return; } var allocatedObject = AllocateObject(type); // Add it to stack, right before arguments var ctorNumParams = ctor.ParameterTypes.Length; stack.Insert(stack.Count - ctorNumParams + 1, new StackValue(StackValueType.Object, type, allocatedObject)); // Invoke ctor EmitCall(functionContext, ctor.Signature, ctor.GeneratedValue); if (type.StackType != StackValueType.Object && type.StackType != StackValueType.Value) { allocatedObject = LLVM.BuildLoad(builder, allocatedObject, string.Empty); } // Add created object on the stack stack.Add(new StackValue(type.StackType, type, allocatedObject)); }
private void EmitInstruction(FunctionCompilerContext functionContext, Instruction instruction) { var methodReference = functionContext.Function.MethodReference; var stack = functionContext.Stack; var args = functionContext.Arguments; var locals = functionContext.Locals; var functionGlobal = functionContext.Function.GeneratedValue; var exceptionHandlers = functionContext.ExceptionHandlers; var opcode = instruction.OpCode.Code; switch (opcode) { case Code.Nop: { // TODO: Insert nop? Debugger step? // Check if there is a custom action Action<List<StackValue>> instructionAction; if (InstructionActions.TryGetValue(instruction, out instructionAction)) { instructionAction(stack); } break; } case Code.Pop: { // Pop and discard last stack value stack.Pop(); break; } case Code.Dup: { // Readd last stack value var lastStackValue = stack[stack.Count - 1]; stack.Add(new StackValue(lastStackValue.StackType, lastStackValue.Type, lastStackValue.Value)); break; } case Code.Ret: { EmitRet(stack, methodReference); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Call: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); EmitCall(functionContext, new FunctionSignature(targetMethod.ReturnType, targetMethod.ParameterTypes), targetMethod.GeneratedValue); break; } case Code.Calli: { var callSite = (CallSite)instruction.Operand; // TODO: Unify with CreateFunction code var returnType = GetType(ResolveGenericsVisitor.Process(methodReference, callSite.ReturnType)).DefaultType; var parameterTypesLLVM = callSite.Parameters.Select(x => GetType(ResolveGenericsVisitor.Process(methodReference, x.ParameterType)).DefaultType).ToArray(); // Generate function type var functionType = LLVM.FunctionType(returnType, parameterTypesLLVM, false); var methodPtr = stack[stack.Count - parameterTypesLLVM.Length - 1]; var castedMethodPtr = LLVM.BuildPointerCast(builder, methodPtr.Value, LLVM.PointerType(functionType, 0), string.Empty); var signature = CreateFunctionSignature(methodReference, callSite); EmitCall(functionContext, signature, castedMethodPtr); break; } case Code.Callvirt: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); var thisObject = stack[stack.Count - targetMethod.ParameterTypes.Length]; var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject); stack[stack.Count - targetMethod.ParameterTypes.Length] = thisObject; // Emit call EmitCall(functionContext, targetMethod.Signature, resolvedMethod); break; } case Code.Constrained: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); functionContext.ConstrainedClass = GetClass(typeReference); break; } #region Obj opcodes (Initobj, Newobj, Stobj, Ldobj, etc...) case Code.Initobj: { var address = stack.Pop(); var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var type = GetType(typeReference); EmitInitobj(address, type); break; } case Code.Newobj: { var ctorReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var ctor = GetFunction(ctorReference); var type = GetType(ctorReference.DeclaringType); EmitNewobj(functionContext, type, ctor); break; } case Code.Stobj: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand)); EmitStobj(stack, type, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Ldobj: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand)); EmitLdobj(stack, type, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } #endregion case Code.Localloc: { var numElements = stack.Pop(); ValueRef numElementsCasted; if (numElements.StackType == StackValueType.NativeInt) { numElementsCasted = LLVM.BuildPtrToInt(builder, numElements.Value, int32Type, string.Empty); } else { numElementsCasted = LLVM.BuildIntCast(builder, numElements.Value, int32Type, string.Empty); } var alloca = LLVM.BuildArrayAlloca(builder, LLVM.Int8TypeInContext(context), numElementsCasted, string.Empty); alloca = LLVM.BuildPointerCast(builder, alloca, intPtr.DataType, string.Empty); stack.Add(new StackValue(StackValueType.NativeInt, intPtr, alloca)); break; } case Code.Castclass: case Code.Isinst: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var @class = GetClass(typeReference); var obj = stack.Pop(); // Get RTTI pointer var indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (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.ObjectType, 0); ValueRef castedPointerObject; // Prepare basic blocks (for PHI instruction) var typeNotMatchBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); var typeCheckDoneBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); BasicBlockRef typeCheckBlock; if (@class.Type.TypeReference.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(isInstInterfaceFunction, 0)); var isInstInterfaceResult = LLVM.BuildCall(builder, isInstInterfaceFunction, new[] { LLVM.BuildPointerCast(builder, rttiPointer, inlineRuntimeTypeInfoType, string.Empty), LLVM.BuildPointerCast(builder, @class.GeneratedRuntimeTypeInfoGlobal, 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(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.SuperTypeCount, false), // Super type count }; typeCheckBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); 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(int32Type, (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(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (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(int32Type, (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.GeneratedRuntimeTypeInfoGlobal, intPtrType), 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, throwExceptionFunction, new[] { LLVM.BuildPointerCast(builder, invalidCastException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); } else { LLVM.BuildBr(builder, typeCheckDoneBlock); } // Start new typeCheckDoneBlock LLVM.PositionBuilderAtEnd(builder, typeCheckDoneBlock); // Put back with appropriate type at end of stack ValueRef mergedVariable; if (opcode == Code.Castclass) { mergedVariable = castedPointerObject; } 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)); break; } case Code.Ldtoken: { var token = instruction.Operand; Class runtimeHandleClass; if (token is TypeReference) { runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeTypeHandle).FullName)); } else if (token is FieldReference) { runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeFieldHandle).FullName)); } else if (token is MethodReference) { runtimeHandleClass = GetClass(corlib.MainModule.GetType(typeof(RuntimeMethodHandle).FullName)); } else { throw new NotSupportedException("Invalid ldtoken operand."); } // TODO: Actually transform type to RTTI token. stack.Add(new StackValue(StackValueType.Value, runtimeHandleClass.Type, LLVM.ConstNull(runtimeHandleClass.Type.DataType))); break; } case Code.Ldftn: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, targetMethod.GeneratedValue, intPtrType, string.Empty))); break; } case Code.Ldvirtftn: { var targetMethodReference = ResolveGenericsVisitor.Process(methodReference, (MethodReference)instruction.Operand); var targetMethod = GetFunction(targetMethodReference); var thisObject = stack.Pop(); var resolvedMethod = ResolveVirtualMethod(functionContext, ref targetMethod, ref thisObject); stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, resolvedMethod, intPtrType, string.Empty))); break; } #region Box/Unbox opcodes case Code.Box: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var @class = GetClass(typeReference); // Only value types need to be boxed if (@class.Type.TypeReference.IsValueType) { var valueType = stack.Pop(); var allocatedObject = BoxValueType(@class, valueType); // Add created object on the stack stack.Add(new StackValue(StackValueType.Object, @class.Type, allocatedObject)); } break; } case Code.Unbox_Any: { var typeReference = ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand); var @class = GetClass(typeReference); if (typeReference.IsValueType) { var obj = stack.Pop(); // TODO: check type? var objCast = LLVM.BuildPointerCast(builder, obj.Value, LLVM.PointerType(@class.Type.ObjectType, 0), string.Empty); var dataPointer = GetDataPointer(objCast); var data = LLVM.BuildLoad(builder, dataPointer, string.Empty); stack.Add(new StackValue(@class.Type.StackType, @class.Type, data)); } else { // Should act as "castclass" on reference types goto case Code.Castclass; } break; } #endregion #region Array opcodes (Newarr, Ldlen, Stelem_Ref, etc...) case Code.Newarr: { var elementType = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand)); EmitNewarr(stack, elementType); break; } case Code.Ldlen: { EmitLdlen(stack); break; } case Code.Ldelema: { var type = GetType(ResolveGenericsVisitor.Process(methodReference, (TypeReference)instruction.Operand)); EmitLdelema(stack, type); break; } case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Any: case Code.Ldelem_Ref: { // TODO: Properly use opcode for type conversion EmitLdelem(stack); break; } case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Any: case Code.Stelem_Ref: { // TODO: Properly use opcode for type conversion EmitStelem(stack); break; } #endregion #region Argument opcodes (Ldarg, Ldarga, Starg, etc...) // Ldarg case Code.Ldarg_0: case Code.Ldarg_1: case Code.Ldarg_2: case Code.Ldarg_3: { var value = opcode - Code.Ldarg_0; EmitLdarg(stack, args, value); break; } case Code.Ldarg_S: case Code.Ldarg: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitLdarg(stack, args, value); break; } case Code.Ldarga: case Code.Ldarga_S: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitLdarga(stack, args, value); break; } case Code.Starg: case Code.Starg_S: { var value = ((ParameterDefinition)instruction.Operand).Index + (functionContext.Method.HasThis ? 1 : 0); EmitStarg(stack, args, value); break; } #endregion #region Load opcodes (Ldc, Ldstr, Ldloc, etc...) // Ldc_I4 case Code.Ldc_I4_M1: case Code.Ldc_I4_0: case Code.Ldc_I4_1: case Code.Ldc_I4_2: case Code.Ldc_I4_3: case Code.Ldc_I4_4: case Code.Ldc_I4_5: case Code.Ldc_I4_6: case Code.Ldc_I4_7: case Code.Ldc_I4_8: { var value = opcode - Code.Ldc_I4_0; EmitI4(stack, value); break; } case Code.Ldc_I4_S: { var value = (sbyte)instruction.Operand; EmitI4(stack, value); break; } case Code.Ldc_I4: { var value = (int)instruction.Operand; EmitI4(stack, value); break; } // Ldc_I8 case Code.Ldc_I8: { var value = (long)instruction.Operand; EmitI8(stack, value); break; } case Code.Ldc_R4: { var value = (float)instruction.Operand; EmitR4(stack, value); break; } case Code.Ldc_R8: { var value = (double)instruction.Operand; EmitR8(stack, value); break; } case Code.Ldstr: { var operand = (string)instruction.Operand; EmitLdstr(stack, operand); break; } case Code.Ldnull: { EmitLdnull(stack); break; } // Ldloc case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: { var localIndex = opcode - Code.Ldloc_0; EmitLdloc(stack, locals, localIndex); break; } case Code.Ldloc: case Code.Ldloc_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitLdloc(stack, locals, localIndex); break; } case Code.Ldloca: case Code.Ldloca_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitLdloca(stack, locals, localIndex); break; } case Code.Ldfld: case Code.Ldflda: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.Fields[fieldReference.Resolve()]; if (opcode == Code.Ldflda) { EmitLdflda(stack, field); } else { EmitLdfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; } break; } case Code.Ldsfld: case Code.Ldsflda: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.Fields[fieldReference.Resolve()]; if (opcode == Code.Ldsflda) { EmitLdsflda(stack, field); } else { EmitLdsfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; } break; } #endregion #region Indirect opcodes (Stind, Ldind, etc...) case Code.Stind_I: case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: { var value = stack.Pop(); var address = stack.Pop(); // Determine type Type type; switch (opcode) { case Code.Stind_I: type = intPtr; break; case Code.Stind_I1: type = int8; break; case Code.Stind_I2: type = int16; break; case Code.Stind_I4: type = int32; break; case Code.Stind_I8: type = int64; break; case Code.Stind_R4: type = @float; break; case Code.Stind_R8: type = @double; break; case Code.Stind_Ref: type = value.Type; break; default: throw new ArgumentException("opcode"); } // Convert to local type var sourceValue = ConvertFromStackToLocal(type, value); // Store value at address var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultType, 0), string.Empty); var storeInst = LLVM.BuildStore(builder, sourceValue, pointerCast); SetInstructionFlags(storeInst, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Ldind_I: case Code.Ldind_I1: case Code.Ldind_I2: case Code.Ldind_I4: case Code.Ldind_I8: case Code.Ldind_U1: case Code.Ldind_U2: case Code.Ldind_U4: case Code.Ldind_R4: case Code.Ldind_R8: case Code.Ldind_Ref: { var address = stack.Pop(); // Determine type Type type; switch (opcode) { case Code.Ldind_I: type = intPtr; break; case Code.Ldind_I1: type = int8; break; case Code.Ldind_I2: type = int16; break; case Code.Ldind_I4: type = int32; break; case Code.Ldind_I8: type = int64; break; case Code.Ldind_U1: type = int8; break; case Code.Ldind_U2: type = int16; break; case Code.Ldind_U4: type = int32; break; case Code.Ldind_R4: type = @float; break; case Code.Ldind_R8: type = @double; break; case Code.Ldind_Ref: type = GetType(((ByReferenceType)address.Type.TypeReference).ElementType); break; default: throw new ArgumentException("opcode"); } // Load value at address var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultType, 0), string.Empty); var loadInst = LLVM.BuildLoad(builder, pointerCast, string.Empty); SetInstructionFlags(loadInst, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; // Convert to stack type var value = ConvertFromLocalToStack(type, loadInst); // Add to stack stack.Add(new StackValue(type.StackType, type, value)); break; } #endregion #region Store opcodes (Stloc, etc...) // Stloc case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: { var localIndex = opcode - Code.Stloc_0; EmitStloc(stack, locals, localIndex); break; } case Code.Stloc: case Code.Stloc_S: { var localIndex = ((VariableDefinition)instruction.Operand).Index; EmitStloc(stack, locals, localIndex); break; } case Code.Stfld: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.Fields[fieldReference.Resolve()]; EmitStfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } case Code.Stsfld: { var fieldReference = (FieldReference)instruction.Operand; // Resolve class and field var @class = GetClass(ResolveGenericsVisitor.Process(methodReference, fieldReference.DeclaringType)); var field = @class.Fields[fieldReference.Resolve()]; EmitStsfld(stack, field, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; break; } #endregion #region Branching (Brtrue, Brfalse, Switch, etc...) case Code.Br: case Code.Br_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBr(functionContext.BasicBlocks[targetInstruction.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Brfalse: case Code.Brfalse_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBrfalse(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } case Code.Brtrue: case Code.Brtrue_S: { var targetInstruction = (Instruction)instruction.Operand; EmitBrtrue(stack, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } case Code.Switch: { var targets = (Instruction[])instruction.Operand; var operand = stack.Pop(); var @switch = LLVM.BuildSwitch(builder, operand.Value, functionContext.BasicBlocks[instruction.Next.Offset], (uint)targets.Length); for (int i = 0; i < targets.Length; ++i) { var target = targets[i]; LLVM.AddCase(@switch, LLVM.ConstInt(int32Type, (ulong)i, false), functionContext.BasicBlocks[target.Offset]); } functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } #endregion #region Conditional branching (Beq, Bgt, etc...) case Code.Beq: case Code.Beq_S: case Code.Bge: case Code.Bge_S: case Code.Bgt: case Code.Bgt_S: case Code.Ble: case Code.Ble_S: case Code.Blt: case Code.Blt_S: case Code.Bne_Un: case Code.Bne_Un_S: case Code.Bge_Un: case Code.Bge_Un_S: case Code.Bgt_Un: case Code.Bgt_Un_S: case Code.Ble_Un: case Code.Ble_Un_S: case Code.Blt_Un: case Code.Blt_Un_S: { var targetInstruction = (Instruction)instruction.Operand; var operand2 = stack.Pop(); var operand1 = stack.Pop(); var value1 = operand1.Value; var value2 = operand2.Value; if ((operand1.StackType == StackValueType.NativeInt && operand2.StackType != StackValueType.NativeInt) || (operand1.StackType != StackValueType.NativeInt && operand2.StackType == StackValueType.NativeInt)) throw new NotImplementedException("Comparison between native int and int types."); if (operand1.StackType == StackValueType.NativeInt && LLVM.TypeOf(value1) != LLVM.TypeOf(value2)) { // NativeInt types should have same types to be comparable value2 = LLVM.BuildPointerCast(builder, value2, LLVM.TypeOf(value1), string.Empty); } // Different object types: cast everything to object if (operand1.StackType == StackValueType.Object && operand2.StackType == StackValueType.Object && operand1.Type != operand2.Type) { value1 = LLVM.BuildPointerCast(builder, value1, @object.DefaultType, string.Empty); value2 = LLVM.BuildPointerCast(builder, value2, @object.DefaultType, string.Empty); } if (operand1.StackType != operand2.StackType || LLVM.TypeOf(value1) != LLVM.TypeOf(value2)) throw new InvalidOperationException(string.Format("Comparison between operands of different types, {0} and {1}.", operand1.Type, operand2.Type)); ValueRef compareResult; if (operand1.StackType == StackValueType.Float) { RealPredicate predicate; switch (opcode) { case Code.Beq: case Code.Beq_S: predicate = RealPredicate.RealOEQ; break; case Code.Bge: case Code.Bge_S: predicate = RealPredicate.RealOGE; break; case Code.Bgt: case Code.Bgt_S: predicate = RealPredicate.RealOGT; break; case Code.Ble: case Code.Ble_S: predicate = RealPredicate.RealOLE; break; case Code.Blt: case Code.Blt_S: predicate = RealPredicate.RealOLT; break; case Code.Bne_Un: case Code.Bne_Un_S: predicate = RealPredicate.RealUNE; break; case Code.Bge_Un: case Code.Bge_Un_S: predicate = RealPredicate.RealUGE; break; case Code.Bgt_Un: case Code.Bgt_Un_S: predicate = RealPredicate.RealUGT; break; case Code.Ble_Un: case Code.Ble_Un_S: predicate = RealPredicate.RealULE; break; case Code.Blt_Un: case Code.Blt_Un_S: predicate = RealPredicate.RealULT; break; default: throw new NotSupportedException(); } compareResult = LLVM.BuildFCmp(builder, predicate, value1, value2, string.Empty); } else { IntPredicate predicate; switch (opcode) { case Code.Beq: case Code.Beq_S: predicate = IntPredicate.IntEQ; break; case Code.Bge: case Code.Bge_S: predicate = IntPredicate.IntSGE; break; case Code.Bgt: case Code.Bgt_S: predicate = IntPredicate.IntSGT; break; case Code.Ble: case Code.Ble_S: predicate = IntPredicate.IntSLE; break; case Code.Blt: case Code.Blt_S: predicate = IntPredicate.IntSLT; break; case Code.Bne_Un: case Code.Bne_Un_S: predicate = IntPredicate.IntNE; break; case Code.Bge_Un: case Code.Bge_Un_S: predicate = IntPredicate.IntUGE; break; case Code.Bgt_Un: case Code.Bgt_Un_S: predicate = IntPredicate.IntUGT; break; case Code.Ble_Un: case Code.Ble_Un_S: predicate = IntPredicate.IntULE; break; case Code.Blt_Un: case Code.Blt_Un_S: predicate = IntPredicate.IntULT; break; default: throw new NotSupportedException(); } compareResult = LLVM.BuildICmp(builder, predicate, value1, value2, string.Empty); } // Branch depending on previous test LLVM.BuildCondBr(builder, compareResult, functionContext.BasicBlocks[targetInstruction.Offset], functionContext.BasicBlocks[instruction.Next.Offset]); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Explicit; break; } #endregion #region Comparison opcodes (Ceq, Cgt, etc...) case Code.Ceq: case Code.Cgt: case Code.Cgt_Un: case Code.Clt: case Code.Clt_Un: { var operand2 = stack.Pop(); var operand1 = stack.Pop(); var value1 = operand1.Value; var value2 = operand2.Value; // Downcast objects to typeof(object) so that they are comparables if (operand1.StackType == StackValueType.Object) value1 = ConvertFromStackToLocal(@object, operand1); if (operand2.StackType == StackValueType.Object) value2 = ConvertFromStackToLocal(@object, operand2); if ((operand1.StackType == StackValueType.NativeInt && operand2.StackType != StackValueType.NativeInt) || (operand1.StackType != StackValueType.NativeInt && operand2.StackType == StackValueType.NativeInt)) throw new NotImplementedException("Comparison between native int and int types."); if (operand1.StackType == StackValueType.NativeInt && LLVM.TypeOf(value1) != LLVM.TypeOf(value2)) { // NativeInt types should have same types to be comparable value2 = LLVM.BuildPointerCast(builder, value2, LLVM.TypeOf(value1), string.Empty); } // Different object types: cast everything to object if (operand1.StackType == StackValueType.Object && operand2.StackType == StackValueType.Object && operand1.Type != operand2.Type) { value1 = LLVM.BuildPointerCast(builder, value1, @object.DefaultType, string.Empty); value2 = LLVM.BuildPointerCast(builder, value2, @object.DefaultType, string.Empty); } if (operand1.StackType != operand2.StackType || LLVM.TypeOf(value1) != LLVM.TypeOf(value2)) throw new InvalidOperationException("Comparison between operands of different types."); ValueRef compareResult; if (operand1.StackType == StackValueType.Float) { RealPredicate predicate; switch (opcode) { case Code.Ceq: predicate = RealPredicate.RealOEQ; break; case Code.Cgt: predicate = RealPredicate.RealOGT; break; case Code.Cgt_Un: predicate = RealPredicate.RealUGT; break; case Code.Clt: predicate = RealPredicate.RealOLT; break; case Code.Clt_Un: predicate = RealPredicate.RealULT; break; default: throw new NotSupportedException(); } compareResult = LLVM.BuildFCmp(builder, predicate, value1, value2, string.Empty); } else { IntPredicate predicate; switch (opcode) { case Code.Ceq: predicate = IntPredicate.IntEQ; break; case Code.Cgt: predicate = IntPredicate.IntSGT; break; case Code.Cgt_Un: predicate = IntPredicate.IntUGT; break; case Code.Clt: predicate = IntPredicate.IntSLT; break; case Code.Clt_Un: predicate = IntPredicate.IntULT; break; default: throw new NotSupportedException(); } compareResult = LLVM.BuildICmp(builder, predicate, value1, value2, string.Empty); } // Extends to int32 compareResult = LLVM.BuildZExt(builder, compareResult, int32Type, string.Empty); // Push result back on the stack stack.Add(new StackValue(StackValueType.Int32, int32, compareResult)); break; } #endregion #region Conversion opcodes (Conv_U, Conv_I, etc...) case Code.Conv_U: case Code.Conv_I: case Code.Conv_U1: case Code.Conv_I1: case Code.Conv_U2: case Code.Conv_I2: case Code.Conv_U4: case Code.Conv_I4: case Code.Conv_U8: case Code.Conv_I8: case Code.Conv_R4: case Code.Conv_R8: case Code.Conv_R_Un: case Code.Conv_Ovf_U: case Code.Conv_Ovf_I: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_I1: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_I8: { var value = stack.Pop(); uint intermediateWidth; System.Type intermediateRealType; bool isSigned; bool isOverflow = false; switch (opcode) { case Code.Conv_U: isSigned = false; intermediateWidth = (uint)intPtrSize; break; case Code.Conv_I: isSigned = true; intermediateWidth = (uint)intPtrSize; break; case Code.Conv_U1: isSigned = false; intermediateWidth = 8; break; case Code.Conv_I1: isSigned = true; intermediateWidth = 8; break; case Code.Conv_U2: isSigned = false; intermediateWidth = 16; break; case Code.Conv_I2: isSigned = true; intermediateWidth = 16; break; case Code.Conv_U4: isSigned = false; intermediateWidth = 32; break; case Code.Conv_I4: isSigned = true; intermediateWidth = 32; break; case Code.Conv_U8: isSigned = false; intermediateWidth = 64; break; case Code.Conv_I8: isSigned = true; intermediateWidth = 64; break; case Code.Conv_Ovf_U: isOverflow = true; goto case Code.Conv_U; case Code.Conv_Ovf_I: isOverflow = true; goto case Code.Conv_I; case Code.Conv_Ovf_U1: isOverflow = true; goto case Code.Conv_U1; case Code.Conv_Ovf_I1: isOverflow = true; goto case Code.Conv_I1; case Code.Conv_Ovf_U2: isOverflow = true; goto case Code.Conv_U2; case Code.Conv_Ovf_I2: isOverflow = true; goto case Code.Conv_I2; case Code.Conv_Ovf_U4: isOverflow = true; goto case Code.Conv_U4; case Code.Conv_Ovf_I4: isOverflow = true; goto case Code.Conv_I4; case Code.Conv_Ovf_U8: isOverflow = true; goto case Code.Conv_U8; case Code.Conv_Ovf_I8: isOverflow = true; goto case Code.Conv_I8; case Code.Conv_R4: case Code.Conv_R8: var inputTypeFullName = value.Type.TypeReference.FullName; isSigned = inputTypeFullName == typeof(int).FullName || inputTypeFullName == typeof(short).FullName || inputTypeFullName == typeof(sbyte).FullName || inputTypeFullName == typeof(IntPtr).FullName; intermediateWidth = 0; // unknown yet, depends on input break; case Code.Conv_R_Un: // TODO: Not sure if this is exactly what Conv_R_Un should do... isSigned = false; intermediateWidth = 0; break; default: throw new InvalidOperationException(); } var currentValue = value.Value; if (value.StackType == StackValueType.NativeInt) { // Convert to integer currentValue = LLVM.BuildPtrToInt(builder, currentValue, nativeIntType, string.Empty); } else if (value.StackType == StackValueType.Reference || value.StackType == StackValueType.Object) { if (opcode != Code.Conv_U8 && opcode != Code.Conv_U && opcode != Code.Conv_I8 && opcode != Code.Conv_I) throw new InvalidOperationException(); // Convert to integer currentValue = LLVM.BuildPtrToInt(builder, currentValue, nativeIntType, string.Empty); } else if (value.StackType == StackValueType.Float) { if (opcode == Code.Conv_R4 || opcode == Code.Conv_R8) { // Special case: float to float, avoid usual case that goes through an intermediary integer. var outputType = opcode == Code.Conv_R8 ? @double : @float; currentValue = LLVM.BuildFPCast(builder, currentValue, outputType.DataType, string.Empty); stack.Add(new StackValue(StackValueType.Float, outputType, currentValue)); break; } // TODO: Float conversions currentValue = isSigned ? LLVM.BuildFPToSI(builder, currentValue, LLVM.IntTypeInContext(context, intermediateWidth), string.Empty) : LLVM.BuildFPToUI(builder, currentValue, LLVM.IntTypeInContext(context, intermediateWidth), string.Empty); } var inputType = LLVM.TypeOf(currentValue); var inputWidth = LLVM.GetIntTypeWidth(inputType); // Auto-adapt intermediate width for floats if (opcode == Code.Conv_R4 || opcode == Code.Conv_R8 || opcode == Code.Conv_R_Un) { intermediateWidth = inputWidth; } var smallestWidth = Math.Min(intermediateWidth, inputWidth); var smallestType = LLVM.IntTypeInContext(context, smallestWidth); var outputWidth = Math.Max(intermediateWidth, 32); // Truncate (if necessary) if (smallestWidth < inputWidth) currentValue = LLVM.BuildTrunc(builder, currentValue, smallestType, string.Empty); if (isOverflow) { // TODO: Compare currentValue with pre-trunc value? } // Reextend to appropriate type (if necessary) if (outputWidth > smallestWidth) { var outputIntType = LLVM.IntTypeInContext(context, outputWidth); if (isSigned) currentValue = LLVM.BuildSExt(builder, currentValue, outputIntType, string.Empty); else currentValue = LLVM.BuildZExt(builder, currentValue, outputIntType, string.Empty); } // Add constant integer value to stack switch (opcode) { case Code.Conv_U: case Code.Conv_I: case Code.Conv_Ovf_U: case Code.Conv_Ovf_I: // Convert to native int (if necessary) currentValue = LLVM.BuildIntToPtr(builder, currentValue, intPtrType, string.Empty); stack.Add(new StackValue(StackValueType.NativeInt, intPtr, currentValue)); break; case Code.Conv_U1: case Code.Conv_I1: case Code.Conv_U2: case Code.Conv_I2: case Code.Conv_U4: case Code.Conv_I4: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_I1: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_I4: stack.Add(new StackValue(StackValueType.Int32, int32, currentValue)); break; case Code.Conv_U8: case Code.Conv_I8: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_I8: stack.Add(new StackValue(StackValueType.Int64, int64, currentValue)); break; case Code.Conv_R4: case Code.Conv_R8: case Code.Conv_R_Un: var outputType = opcode == Code.Conv_R8 || opcode == Code.Conv_R_Un ? @double : @float; if (isSigned) currentValue = LLVM.BuildSIToFP(builder, currentValue, outputType.DataType, string.Empty); else currentValue = LLVM.BuildUIToFP(builder, currentValue, outputType.DataType, string.Empty); stack.Add(new StackValue(StackValueType.Float, outputType, currentValue)); break; default: throw new InvalidOperationException(); } break; } #endregion #region Unary operation opcodes (Neg, Not, etc...) case Code.Neg: case Code.Not: { var operand1 = stack.Pop(); var value1 = operand1.Value; // Check stack type (and convert if necessary) switch (operand1.StackType) { case StackValueType.Float: if (opcode == Code.Not) throw new InvalidOperationException("Not opcode doesn't work with float"); break; case StackValueType.NativeInt: value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty); break; case StackValueType.Int32: case StackValueType.Int64: break; default: throw new InvalidOperationException(string.Format("Opcode {0} not supported with stack type {1}", opcode, operand1.StackType)); } // Perform neg or not operation switch (opcode) { case Code.Neg: if (operand1.StackType == StackValueType.Float) value1 = LLVM.BuildFNeg(builder, value1, string.Empty); else value1 = LLVM.BuildNeg(builder, value1, string.Empty); break; case Code.Not: value1 = LLVM.BuildNot(builder, value1, string.Empty); break; } if (operand1.StackType == StackValueType.NativeInt) value1 = LLVM.BuildIntToPtr(builder, value1, intPtrType, string.Empty); // Add back to stack (with same type as before) stack.Add(new StackValue(operand1.StackType, operand1.Type, value1)); break; } #endregion #region Binary operation opcodes (Add, Sub, etc...) case Code.Add: case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.Sub: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Mul: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: case Code.Div: case Code.Div_Un: case Code.Rem: case Code.Rem_Un: case Code.Shl: case Code.Shr: case Code.Shr_Un: case Code.Xor: case Code.Or: case Code.And: { var operand2 = stack.Pop(); var operand1 = stack.Pop(); var value1 = operand1.Value; var value2 = operand2.Value; StackValueType outputStackType; bool isShiftOperation = false; bool isIntegerOperation = false; // Detect shift and integer operations switch (opcode) { case Code.Shl: case Code.Shr: case Code.Shr_Un: isShiftOperation = true; break; case Code.Xor: case Code.Or: case Code.And: case Code.Div_Un: case Code.Not: isIntegerOperation = true; break; } if (isShiftOperation) // Shift operations are specials { switch (operand2.StackType) { case StackValueType.Int32: case StackValueType.NativeInt: value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty); break; default: goto InvalidBinaryOperation; } // Check first operand, and convert second operand to match first one switch (operand1.StackType) { case StackValueType.Int32: value2 = LLVM.BuildIntCast(builder, value2, int32Type, string.Empty); break; case StackValueType.Int64: value2 = LLVM.BuildIntCast(builder, value2, int64Type, string.Empty); break; case StackValueType.NativeInt: value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty); value2 = LLVM.BuildIntCast(builder, value2, nativeIntType, string.Empty); break; default: goto InvalidBinaryOperation; } // Output type is determined by first operand outputStackType = operand1.StackType; } else if (operand1.StackType == operand2.StackType) // Diagonal { // Check type switch (operand1.StackType) { case StackValueType.Int32: case StackValueType.Int64: case StackValueType.Float: outputStackType = operand1.StackType; break; case StackValueType.NativeInt: value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty); value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty); outputStackType = operand1.StackType; break; case StackValueType.Reference: if (opcode != Code.Sub && opcode != Code.Sub_Ovf_Un) goto InvalidBinaryOperation; value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty); value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty); outputStackType = StackValueType.NativeInt; break; default: throw new InvalidOperationException(string.Format("Binary operations are not allowed on {0}.", operand1.StackType)); } } else if (operand1.StackType == StackValueType.NativeInt && operand2.StackType == StackValueType.Int32) { value1 = LLVM.BuildPtrToInt(builder, value1, nativeIntType, string.Empty); outputStackType = StackValueType.NativeInt; } else if (operand1.StackType == StackValueType.Int32 && operand2.StackType == StackValueType.NativeInt) { value2 = LLVM.BuildPtrToInt(builder, value2, nativeIntType, string.Empty); outputStackType = StackValueType.NativeInt; } else if (!isIntegerOperation && (operand1.StackType == StackValueType.Reference || operand2.StackType == StackValueType.Reference)) // ref + [i32, nativeint] or [i32, nativeint] + ref { StackValue operandRef, operandInt; ValueRef valueRef, valueInt; if (operand2.StackType == StackValueType.Reference) { operandRef = operand2; operandInt = operand1; valueRef = value2; valueInt = value1; } else { operandRef = operand1; operandInt = operand2; valueRef = value1; valueInt = value2; } switch (operandInt.StackType) { case StackValueType.Int32: break; case StackValueType.NativeInt: valueInt = LLVM.BuildPtrToInt(builder, valueInt, nativeIntType, string.Empty); break; default: goto InvalidBinaryOperation; } switch (opcode) { case Code.Add: case Code.Add_Ovf_Un: break; case Code.Sub: case Code.Sub_Ovf: if (operand2.StackType == StackValueType.Reference) goto InvalidBinaryOperation; valueInt = LLVM.BuildNeg(builder, valueInt, string.Empty); break; default: goto InvalidBinaryOperation; } // If necessary, cast to i8* var valueRefType = LLVM.TypeOf(valueRef); if (valueRefType != intPtrType) valueRef = LLVM.BuildPointerCast(builder, valueRef, intPtrType, string.Empty); valueRef = LLVM.BuildGEP(builder, valueRef, new[] { valueInt }, string.Empty); // Cast back to original type if (valueRefType != intPtrType) valueRef = LLVM.BuildPointerCast(builder, valueRef, valueRefType, string.Empty); stack.Add(new StackValue(StackValueType.Reference, operandRef.Type, valueRef)); // Early exit break; } else { goto InvalidBinaryOperation; } ValueRef result; // Perform binary operation if (operand1.StackType == StackValueType.Float) { switch (opcode) { case Code.Add: result = LLVM.BuildFAdd(builder, value1, value2, string.Empty); break; case Code.Sub: result = LLVM.BuildFSub(builder, value1, value2, string.Empty); break; case Code.Mul: result = LLVM.BuildFMul(builder, value1, value2, string.Empty); break; case Code.Div: result = LLVM.BuildFDiv(builder, value1, value2, string.Empty); break; case Code.Rem: result = LLVM.BuildFRem(builder, value1, value2, string.Empty); break; default: goto InvalidBinaryOperation; } } else { switch (opcode) { case Code.Add: result = LLVM.BuildAdd(builder, value1, value2, string.Empty); break; case Code.Sub: result = LLVM.BuildSub(builder, value1, value2, string.Empty); break; case Code.Mul: result = LLVM.BuildMul(builder, value1, value2, string.Empty); break; case Code.Div: result = LLVM.BuildSDiv(builder, value1, value2, string.Empty); break; case Code.Div_Un: result = LLVM.BuildUDiv(builder, value1, value2, string.Empty); break; case Code.Rem: result = LLVM.BuildSRem(builder, value1, value2, string.Empty); break; case Code.Rem_Un: result = LLVM.BuildURem(builder, value1, value2, string.Empty); break; case Code.Shl: result = LLVM.BuildShl(builder, value1, value2, string.Empty); break; case Code.Shr: result = LLVM.BuildAShr(builder, value1, value2, string.Empty); break; case Code.Shr_Un: result = LLVM.BuildLShr(builder, value1, value2, string.Empty); break; case Code.And: result = LLVM.BuildAnd(builder, value1, value2, string.Empty); break; case Code.Or: result = LLVM.BuildOr(builder, value1, value2, string.Empty); break; case Code.Xor: result = LLVM.BuildXor(builder, value1, value2, string.Empty); break; case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: { Intrinsics intrinsicId; switch (opcode) { case Code.Add_Ovf: intrinsicId = Intrinsics.sadd_with_overflow; break; case Code.Add_Ovf_Un: intrinsicId = Intrinsics.uadd_with_overflow; break; case Code.Sub_Ovf: intrinsicId = Intrinsics.ssub_with_overflow; break; case Code.Sub_Ovf_Un: intrinsicId = Intrinsics.usub_with_overflow; break; case Code.Mul_Ovf: intrinsicId = Intrinsics.smul_with_overflow; break; case Code.Mul_Ovf_Un: intrinsicId = Intrinsics.umul_with_overflow; break; default: throw new ArgumentOutOfRangeException(); } var intrinsic = LLVM.IntrinsicGetDeclaration(module, (uint)intrinsicId, new[] { LLVM.TypeOf(value1) }); var overflowResult = LLVM.BuildCall(builder, intrinsic, new[] {value1, value2}, string.Empty); var hasOverflow = LLVM.BuildExtractValue(builder, overflowResult, 1, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); var overflowBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "overflow"); LLVM.BuildCondBr(builder, hasOverflow, overflowBlock, nextBlock); LLVM.PositionBuilderAtEnd(builder, overflowBlock); // Create OverflowException object var overflowExceptionClass = GetClass(corlib.MainModule.GetType(typeof(OverflowException).FullName)); EmitNewobj(functionContext, overflowExceptionClass.Type, overflowExceptionClass.Functions.Single(x => x.MethodReference.Name == ".ctor" && x.MethodReference.Parameters.Count == 0)); var overflowException = stack.Pop(); GenerateInvoke(functionContext, throwExceptionFunction, new[] { LLVM.BuildPointerCast(builder, overflowException.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); functionContext.BasicBlock = nextBlock; LLVM.PositionBuilderAtEnd(builder, nextBlock); result = LLVM.BuildExtractValue(builder, overflowResult, 0, string.Empty); break; } default: goto InvalidBinaryOperation; } } Type outputType; switch (outputStackType) { case StackValueType.Int32: case StackValueType.Int64: case StackValueType.Float: // No output conversion required, as it could only have been from same input types (non-shift) or operand 1 (shift) outputType = operand1.Type; break; case StackValueType.NativeInt: outputType = intPtr; result = LLVM.BuildIntToPtr(builder, result, intPtrType, string.Empty); break; case StackValueType.Reference: result = LLVM.BuildIntToPtr(builder, result, intPtrType, string.Empty); // Get type from one of its operand (if output is reference type, one of the two operand must be too) if (operand1.StackType == StackValueType.Reference) outputType = operand1.Type; else if (operand2.StackType == StackValueType.Reference) outputType = operand2.Type; else goto InvalidBinaryOperation; break; default: goto InvalidBinaryOperation; } stack.Add(new StackValue(outputStackType, outputType, result)); break; InvalidBinaryOperation: throw new InvalidOperationException(string.Format("Binary operation {0} between {1} and {2} is not supported.", opcode, operand1.StackType, operand2.StackType)); } #endregion #region Exception handling opcodes (Leave, Endfinally, etc...) case Code.Throw: { var exceptionObject = stack.Pop(); // Throw exception // TODO: Update callstack GenerateInvoke(functionContext, throwExceptionFunction, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Rethrow: { // Find exception that was on stack at beginning of this catch clause var currentCatchClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset); if (currentCatchClause == null || currentCatchClause.Source.HandlerType != ExceptionHandlerType.Catch) throw new InvalidOperationException("Can't find catch clause matching this rethrow instruction."); var catchClauseStack = functionContext.ForwardStacks[currentCatchClause.Source.HandlerStart.Offset]; var exceptionObject = catchClauseStack[0]; // Rethrow exception GenerateInvoke(functionContext, throwExceptionFunction, new ValueRef[] { LLVM.BuildPointerCast(builder, exceptionObject.Value, LLVM.TypeOf(LLVM.GetParam(throwExceptionFunction, 0)), string.Empty) }); LLVM.BuildUnreachable(builder); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Leave: case Code.Leave_S: { // Evaluation stack is cleared stack.Clear(); // Default target (if we jump inside the exception clause) var targetInstruction = (Instruction)instruction.Operand; GenerateLeave(functionContext.ActiveTryHandlers, targetInstruction, functionContext.EndfinallyJumpTarget, functionContext.BasicBlocks); functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } case Code.Endfinally: { var currentFinallyClause = GetCurrentExceptionHandler(exceptionHandlers, instruction.Offset); if (currentFinallyClause == null || currentFinallyClause.Source.HandlerType != ExceptionHandlerType.Finally) throw new InvalidOperationException("Can't find finally clause matching this endfinally instruction."); // Basic block to continue exception handling (if endfinally.jumptarget is -1, but we simply set it on undefined cases) var activeTryHandlers = functionContext.ActiveTryHandlers; var nextActiveTryHandler = activeTryHandlers.Count > 0 ? activeTryHandlers[activeTryHandlers.Count - 1].CatchDispatch : functionContext.ResumeExceptionBlock; // Generate dispatch code (with a switch/case) var @switch = LLVM.BuildSwitch(builder, LLVM.BuildLoad(builder, functionContext.EndfinallyJumpTarget, string.Empty), nextActiveTryHandler, (uint)currentFinallyClause.LeaveTargets.Count); for (int index = 0; index < currentFinallyClause.LeaveTargets.Count; index++) { var leaveTarget = currentFinallyClause.LeaveTargets[index]; LLVM.AddCase(@switch, LLVM.ConstInt(int32Type, (ulong)index, false), functionContext.BasicBlocks[leaveTarget.Offset]); } // Default is not to flow to next instruction, previous Leave instructions already tagged what was necessary functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.None; break; } #endregion #region Instruction flags (Unaligned, Volatile) case Code.Volatile: functionContext.InstructionFlags |= InstructionFlags.Volatile; break; case Code.Unaligned: functionContext.InstructionFlags |= InstructionFlags.Unaligned; break; #endregion default: throw new NotImplementedException(string.Format("Opcode {0} not implemented.", instruction.OpCode)); } }
private void EmitLdind(FunctionCompilerContext functionContext, FunctionStack stack, Code opcode) { var address = stack.Pop(); // Determine type Type type; switch (opcode) { case Code.Ldind_I: type = intPtr; break; case Code.Ldind_I1: type = int8; break; case Code.Ldind_I2: type = int16; break; case Code.Ldind_I4: type = int32; break; case Code.Ldind_I8: type = int64; break; case Code.Ldind_U1: type = int8; break; case Code.Ldind_U2: type = int16; break; case Code.Ldind_U4: type = int32; break; case Code.Ldind_R4: type = @float; break; case Code.Ldind_R8: type = @double; break; case Code.Ldind_Ref: type = GetType(((ByReferenceType)address.Type.TypeReferenceCecil).ElementType, TypeState.StackComplete); break; default: throw new ArgumentException("opcode"); } if (CharUsesUTF8) { if (opcode == Code.Ldind_I2 && address.Type.TypeReferenceCecil.FullName == typeof(char*).FullName) { type = int8; } } // Load value at address var pointerCast = LLVM.BuildPointerCast(builder, address.Value, LLVM.PointerType(type.DefaultTypeLLVM, 0), string.Empty); var loadInst = LoadValue(type.StackType, pointerCast, functionContext.InstructionFlags); functionContext.InstructionFlags = InstructionFlags.None; // Convert to stack type var value = ConvertFromLocalToStack(type, loadInst); // Add to stack stack.Add(new StackValue(type.StackType, type, value)); }