summaryrefslogtreecommitdiff
path: root/tests/src/JIT/Stress/ABI/PInvokes.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/JIT/Stress/ABI/PInvokes.cs')
-rw-r--r--tests/src/JIT/Stress/ABI/PInvokes.cs113
1 files changed, 113 insertions, 0 deletions
diff --git a/tests/src/JIT/Stress/ABI/PInvokes.cs b/tests/src/JIT/Stress/ABI/PInvokes.cs
new file mode 100644
index 0000000000..0198dbf82d
--- /dev/null
+++ b/tests/src/JIT/Stress/ABI/PInvokes.cs
@@ -0,0 +1,113 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+
+namespace ABIStress
+{
+ internal partial class Program
+ {
+ private static readonly List<DynamicMethod> s_keepRooted = new List<DynamicMethod>();
+ private static readonly Dictionary<int, Callee> s_pinvokees = new Dictionary<int, Callee>();
+ private static bool DoPInvokes(int callerIndex)
+ {
+ string callerName = Config.PInvokerPrefix + callerIndex;
+ Random rand = new Random(GetSeed(callerName));
+ List<TypeEx> pms = RandomParameters(s_allTypes, rand);
+
+ int calleeIndex = rand.Next(0, Config.NumCallees);
+ Callee callee;
+ if (!s_pinvokees.TryGetValue(calleeIndex, out callee))
+ {
+ callee = CreateCallee(Config.PInvokeePrefix + calleeIndex, s_pinvokeeCandidateArgTypes);
+ callee.Emit();
+ callee.EmitPInvokeDelegateTypes();
+ s_pinvokees.Add(calleeIndex, callee);
+ }
+
+ DynamicMethod caller = new DynamicMethod(
+ callerName, typeof(int[]), pms.Select(t => t.Type).ToArray(), typeof(Program).Module);
+
+ // We need to keep callers rooted due to a stale cache bug in the runtime related to calli.
+ s_keepRooted.Add(caller);
+
+ ILGenerator 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)));
+
+ // Create array to store results in
+ LocalBuilder resultsArrLocal = g.DeclareLocal(typeof(int[]));
+ g.Emit(OpCodes.Ldc_I4, callee.PInvokeDelegateTypes.Count);
+ g.Emit(OpCodes.Newarr, typeof(int));
+ g.Emit(OpCodes.Stloc, resultsArrLocal);
+
+ // Emit pinvoke calls for each calling convention. Keep delegates rooted in a list.
+ LocalBuilder resultLocal = g.DeclareLocal(typeof(int));
+ List<Delegate> delegates = new List<Delegate>();
+ int resultIndex = 0;
+ foreach (var (cc, delegateType) in callee.PInvokeDelegateTypes)
+ {
+ Delegate dlg = callee.Method.CreateDelegate(delegateType);
+ delegates.Add(dlg);
+
+ if (Config.Verbose)
+ EmitDumpValues($"Caller's args to {cc} calli", g, args);
+
+ foreach (Value v in args)
+ v.Emit(g);
+
+ IntPtr ptr = Marshal.GetFunctionPointerForDelegate(dlg);
+ g.Emit(OpCodes.Ldc_I8, (long)ptr);
+ g.Emit(OpCodes.Conv_I);
+ g.EmitCalli(OpCodes.Calli, cc, typeof(int), callee.Parameters.Select(p => p.Type).ToArray());
+ g.Emit(OpCodes.Stloc, resultLocal);
+
+ g.Emit(OpCodes.Ldloc, resultsArrLocal);
+ g.Emit(OpCodes.Ldc_I4, resultIndex); // where to store result
+ g.Emit(OpCodes.Ldloc, resultLocal); // result
+ g.Emit(OpCodes.Stelem_I4);
+ resultIndex++;
+ }
+
+ g.Emit(OpCodes.Ldloc, resultsArrLocal);
+ g.Emit(OpCodes.Ret);
+
+ (object callerResult, object calleeResult) =
+ InvokeCallerCallee(caller, pms, callee.Method, args, rand);
+
+ // The pointers used in the calli instructions are only valid while the delegates are alive,
+ // so keep these alive until we're done executing.
+ GC.KeepAlive(delegates);
+
+ int[] results = (int[])callerResult;
+
+ bool allCorrect = true;
+ for (int i = 0; i < results.Length; i++)
+ {
+ if (results[i] == (int)calleeResult)
+ continue;
+
+ allCorrect = false;
+ string callType = callee.PInvokeDelegateTypes.ElementAt(i).Key.ToString();
+ Console.WriteLine("Mismatch in {0}: expected {1}, got {2}", callType, calleeResult, results[i]);
+ }
+
+ if (!allCorrect)
+ {
+ WriteSignature(caller);
+ WriteSignature(callee.Method);
+ }
+
+ return allCorrect;
+ }
+ }
+}