public Instruction(InstructionActions action, double value) { Action = action; Value = value; }
private MethodBody GenerateDelegateMethod(MethodDefinition method, Class declaringClass) { // Delegate type var delegateType = corlib.MainModule.GetType(typeof(Delegate).FullName); // Delegate fields var targetField = delegateType.Fields.First(x => x.Name == "_target"); var methodPtrField = delegateType.Fields.First(x => x.Name == "_methodPtr"); var methodPtrAuxField = delegateType.Fields.First(x => x.Name == "_methodPtrAux"); var body = new MethodBody(method); var il = body.GetILProcessor(); if (method.Name == ".ctor") { // Mark //GenerateMulticastInvokeThunk(declaringClass); // Two main cases: // - Instance method: // this._methodPtr = fnptr; // this._target = target; // Result: this._methodPtr(this._target, arg1, ..) will directly work // - Static method: // this._target = this; // this._methodPtrAux = fnptr; // this._methodPtr = (delegate, arg1, ...) => { delegate->_methodPtrAux(arg1, ...); } // Result: this._methodPtr(this._target, arg1, ...) will call thunk, // which will call fnptr (from this._target._methodPtr) without the first argument var target = Instruction.Create(OpCodes.Ldarg_0); // if (target == null) // { il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[0])); il.Append(Instruction.Create(OpCodes.Brtrue, target)); // Generate thunk (for now, done using direct LLVM, not sure weither LLVM or IL is better) // this._methodPtr = (delegate, arg1, ...) => { delegate->_methodPtrAux(arg1, ...); } var invokeMethodHelper = GenerateStaticInvokeThunk(declaringClass); // Fake Nop to push this thunk on stack (TODO: Better way to do this? i.e. store it in some static field?) il.Append(Instruction.Create(OpCodes.Ldarg_0)); var loadFunctionPointerInstruction = Instruction.Create(OpCodes.Nop); InstructionActions.Add(loadFunctionPointerInstruction, (stack) => { // Push the generated method pointer on the stack stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, invokeMethodHelper, intPtrLLVM, string.Empty))); }); il.Append(loadFunctionPointerInstruction); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrField)); // this._methodPtrAux = method; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[1])); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrAuxField)); // this._target = this; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Stfld, targetField)); // return; // } il.Append(Instruction.Create(OpCodes.Ret)); // this._target = target; il.Append(target); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[0])); il.Append(Instruction.Create(OpCodes.Stfld, targetField)); // this._methodPtr = method; il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldarg, method.Parameters[1])); il.Append(Instruction.Create(OpCodes.Stfld, methodPtrField)); // return; il.Append(Instruction.Create(OpCodes.Ret)); } else if (method.Name == "GetMulticastDispatchMethod") { var invokeMethodHelper = GenerateMulticastInvokeThunk(declaringClass); var loadFunctionPointerInstruction = Instruction.Create(OpCodes.Nop); InstructionActions.Add(loadFunctionPointerInstruction, (stack) => { // Push the generated method pointer on the stack stack.Add(new StackValue(StackValueType.NativeInt, intPtr, LLVM.BuildPointerCast(builder, invokeMethodHelper, intPtrLLVM, string.Empty))); }); il.Append(loadFunctionPointerInstruction); il.Append(Instruction.Create(OpCodes.Ret)); } else if (method.Name == "Invoke") { // For now, generate IL // Note that we could probably optimize at callsite too, // but probably not necessary if LLVM and sealed class are optimized/inlined well enough // ldarg_0 // ldfld _target il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldfld, targetField)); var callsite = new CallSite(method.ReturnType); callsite.Parameters.Add(new ParameterDefinition(targetField.FieldType)); foreach (var parameter in method.Parameters) { callsite.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, parameter.ParameterType)); // ldarg il.Append(Instruction.Create(OpCodes.Ldarg, parameter)); } // ldarg_0 // ldfld _methodPtr il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldfld, methodPtrField)); // calli il.Append(Instruction.Create(OpCodes.Calli, callsite)); // ret il.Append(Instruction.Create(OpCodes.Ret)); } else { LLVM.BuildUnreachable(builder); return(null); } return(body); }