private static bool DoStubCall(int callerIndex, bool staticMethod, bool onValueType, GenericShape typeGenericShape, GenericShape methodGenericShape) { string callerNameSeed = Config.InstantiatingStubPrefix + "Caller" + callerIndex; // Use a consistent seed value here so that the various various of unboxing/instantiating stubs are generated with the same arg shape string callerName = callerNameSeed + (staticMethod ? "Static" : "Instance") + (onValueType ? "Class" : "ValueType") + typeGenericShape.ToString() + methodGenericShape.ToString(); Random rand = new Random(GetSeed(callerNameSeed)); List <TypeEx> pms; do { pms = RandomParameters(s_allTypes, rand); } while (pms.Count > 16); Type delegateType = GetDelegateType(pms, typeof(int)); Callee callee = new Callee(callerName + "Callee", pms);// CreateCallee(Config.PInvokeePrefix + calleeIndex, s_allTypes); callee.Emit(); Delegate calleeDelegate = callee.Method.CreateDelegate(delegateType); int newStubCount = Interlocked.Increment(ref s_stubTypesCreated); if ((s_stubTypesModule == null) || (newStubCount % 1000) == 0) { AssemblyBuilder stubsAssembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ABIStress_Stubs" + newStubCount), AssemblyBuilderAccess.RunAndCollect); s_stubTypesModule = stubsAssembly.DefineDynamicModule("ABIStress_Stubs" + newStubCount); } // This code is based on DelegateHelpers.cs in System.Linq.Expressions.Compiler TypeBuilder tb = s_stubTypesModule.DefineType( $"{callerName}_GenericTarget", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AutoClass, onValueType ? typeof(object) : typeof(ValueType)); GenericTypeParameterBuilder[] typeParamsType = null; if (typeGenericShape != GenericShape.NotGeneric) { typeParamsType = tb.DefineGenericParameters(new string[] { "T" }); } FieldInfo fieldDeclaration = tb.DefineField("MagicValue", typeof(int), FieldAttributes.Public); Type typeofInstantiatedType; FieldInfo fieldInfoMagicValueField; if (typeGenericShape == GenericShape.NotGeneric) { typeofInstantiatedType = tb; fieldInfoMagicValueField = fieldDeclaration; } else { typeofInstantiatedType = tb.MakeGenericType(typeParamsType[0]); fieldInfoMagicValueField = TypeBuilder.GetField(typeofInstantiatedType, fieldDeclaration); } ConstructorBuilder cb = tb.DefineConstructor( MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(int) }); cb.SetImplementationFlags(MethodImplAttributes.Managed); ILGenerator g = cb.GetILGenerator(); g.Emit(OpCodes.Ldarg, 0); g.Emit(OpCodes.Call, typeof(object).GetConstructor(Array.Empty <Type>())); g.Emit(OpCodes.Ldarg, 0); g.Emit(OpCodes.Ldarg_1); g.Emit(OpCodes.Stfld, fieldInfoMagicValueField); g.Emit(OpCodes.Ret); MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig; if (staticMethod) { methodAttributes |= MethodAttributes.Static; } MethodBuilder mbInstance = tb.DefineMethod( "Method", methodAttributes, callee.Method.ReturnType, callee.Parameters.Select(t => t.Type).ToArray()); GenericTypeParameterBuilder[] typeParamsMethod = null; if (methodGenericShape != GenericShape.NotGeneric) { typeParamsMethod = mbInstance.DefineGenericParameters(new string[] { "T" }); } mbInstance.SetImplementationFlags(MethodImplAttributes.Managed); int magicNumberEmbeddedInObject = rand.Next(); GCHandle gchCallee = GCHandle.Alloc(callee.Method.CreateDelegate(delegateType)); IntPtr gchCalleeIntPtr = GCHandle.ToIntPtr(gchCallee); g = mbInstance.GetILGenerator(); if (!staticMethod) { // Verify random number made it intact, and this parameter was handled correctly g.Emit(OpCodes.Ldarg_0); g.Emit(OpCodes.Ldfld, fieldInfoMagicValueField); g.Emit(OpCodes.Ldc_I4, magicNumberEmbeddedInObject); g.Emit(OpCodes.Call, s_compareNumbers); } // Verify generic args are as expected EmitTypeHandleCheck(g, typeGenericShape, typeParamsType, "type"); EmitTypeHandleCheck(g, methodGenericShape, typeParamsMethod, "method"); // Make the call to callee LocalBuilder gcHandleLocal = g.DeclareLocal(typeof(GCHandle)); // Load GCHandle of callee delegate g.Emit(OpCodes.Ldc_I8, (long)gchCalleeIntPtr); g.Emit(OpCodes.Conv_I); g.Emit(OpCodes.Call, s_gcHandleFromIntPtr); g.Emit(OpCodes.Stloc, gcHandleLocal); // Resolve to target g.Emit(OpCodes.Ldloca, gcHandleLocal); g.Emit(OpCodes.Call, s_gcHandle_getTarget); // Cast to delegate type g.Emit(OpCodes.Castclass, delegateType); // Load all args int argOffset = 1; if (staticMethod) { argOffset = 0; } for (int i = 0; i < pms.Count; i++) { g.Emit(OpCodes.Ldarg, argOffset + i); } // Call delegate invoke method g.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke")); // ret g.Emit(OpCodes.Ret); Type calleeTypeOpen = tb.CreateType(); Type calleeType; switch (typeGenericShape) { case GenericShape.NotGeneric: calleeType = calleeTypeOpen; break; case GenericShape.GenericOverReferenceType: calleeType = calleeTypeOpen.MakeGenericType(typeof(object)); break; case GenericShape.GenericOverValueType: calleeType = calleeTypeOpen.MakeGenericType(typeof(int)); break; default: throw new Exception("Unknown case"); } MethodInfo targetMethodOpen = calleeType.GetMethod("Method"); MethodInfo targetMethod; switch (methodGenericShape) { case GenericShape.NotGeneric: targetMethod = targetMethodOpen; break; case GenericShape.GenericOverReferenceType: targetMethod = targetMethodOpen.MakeGenericMethod(typeof(object)); break; case GenericShape.GenericOverValueType: targetMethod = targetMethodOpen.MakeGenericMethod(typeof(int)); break; default: throw new Exception("Unknown case"); } Delegate targetMethodToCallDel; if (staticMethod) { targetMethodToCallDel = targetMethod.CreateDelegate(delegateType); } else { targetMethodToCallDel = targetMethod.CreateDelegate(delegateType, Activator.CreateInstance(calleeType, magicNumberEmbeddedInObject)); } GCHandle gchTargetMethod = GCHandle.Alloc(targetMethodToCallDel); // CALLER Dynamic method DynamicMethod caller = new DynamicMethod( callerName, typeof(int), pms.Select(t => t.Type).ToArray(), typeof(Program).Module); g = caller.GetILGenerator(); // Create the args to pass to the callee from the caller. List <Value> args = GenCallerToCalleeArgs(pms, callee.Parameters, rand); if (Config.Verbose) { EmitDumpValues("Caller's incoming args", g, pms.Select((p, i) => new ArgValue(p, i))); } if (Config.Verbose) { EmitDumpValues($"Caller's args to {callerName} call", g, args); } gcHandleLocal = g.DeclareLocal(typeof(GCHandle)); g.Emit(OpCodes.Ldc_I8, (long)GCHandle.ToIntPtr(gchTargetMethod)); g.Emit(OpCodes.Conv_I); g.Emit(OpCodes.Call, s_gcHandleFromIntPtr); g.Emit(OpCodes.Stloc, gcHandleLocal); // Resolve to target g.Emit(OpCodes.Ldloca, gcHandleLocal); g.Emit(OpCodes.Call, s_gcHandle_getTarget); // Cast to delegate type g.Emit(OpCodes.Castclass, delegateType); foreach (Value v in args) { v.Emit(g); } // Call delegate invoke method g.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke")); // ret g.Emit(OpCodes.Ret); (object callerResult, object calleeResult) = InvokeCallerCallee(caller, pms, callee.Method, args, rand); gchCallee.Free(); gchTargetMethod.Free(); if (callerResult.Equals(calleeResult)) { return(true); } Console.WriteLine("Mismatch in stub call: expected {0}, got {1}", calleeResult, callerResult); Console.WriteLine(callerName); WriteSignature(caller); WriteSignature(callee.Method); return(false); }
private static bool DoTailCall(int callerIndex) { // We pregenerate tail callee parameter lists because we want to be able to select // a callee with less arg stack space than this caller. if (s_tailCallees == null) { s_tailCallees = Enumerable.Range(0, Config.NumCallees) .Select(i => CreateCallee(Config.TailCalleePrefix + i, s_tailCalleeCandidateArgTypes)) .ToList(); } string callerName = Config.TailCallerPrefix + callerIndex; Random rand = new Random(GetSeed(callerName)); List <TypeEx> callerParams; List <Callee> callable; do { callerParams = RandomParameters(s_tailCalleeCandidateArgTypes, rand); int argStackSizeApprox = s_abi.ApproximateArgStackAreaSize(callerParams); callable = s_tailCallees.Where(t => t.ArgStackSizeApprox < argStackSizeApprox).ToList(); } while (callable.Count <= 0); int calleeIndex = rand.Next(callable.Count); Callee callee = callable[calleeIndex]; callee.Emit(); DynamicMethod caller = new DynamicMethod( callerName, typeof(int), callerParams.Select(t => t.Type).ToArray(), typeof(Program).Module); ILGenerator g = caller.GetILGenerator(); // Create the args to pass to the callee from the caller. List <Value> args = GenCallerToCalleeArgs(callerParams, callee.Parameters, rand); if (Config.Verbose) { EmitDumpValues("Caller's incoming args", g, callerParams.Select((p, i) => new ArgValue(p, i))); EmitDumpValues("Caller's args to tailcall", g, args); } foreach (Value v in args) { v.Emit(g); } g.Emit(OpCodes.Tailcall); g.EmitCall(OpCodes.Call, callee.Method, null); g.Emit(OpCodes.Ret); (object callerResult, object calleeResult) = InvokeCallerCallee(caller, callerParams, callee.Method, args, rand); if (callerResult.Equals(calleeResult)) { return(true); } Console.WriteLine("Mismatch in tailcall: expected {0}, got {1}", calleeResult, callerResult); WriteSignature(caller); WriteSignature(callee.Method); return(false); }