using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Exercises the Microsoft NativeAOT runtime contracts that the bare-metal PAL // must support. Compilation alone is never a guest runtime verification. internal static class Program { [DllImport("xfe_pal", EntryPoint = "xfe_runtime_probe_report")] private static extern void Report(int stage, long value); private sealed class Root(int value) { internal int Value = value; } private sealed class DependentValue(object owner) { internal object Owner = owner; ~DependentValue() => Interlocked.Increment(ref finalized); } [ThreadStatic] private static int threadToken; private static readonly object Gate = new(); private static int finalized, stopWorker, iterations, workerResult, zeroDivisor; private static bool workerReady; private static int Main() { var table = new ConditionalWeakTable(); var key = new object(); table.Add(key, new Root(42)); byte[] bytes = new byte[32768]; bytes[^1] = 211; threadToken = 7; GC.Collect(); if (table.GetValue(key, _ => new Root(-1)).Value != 42 || bytes[^1] != 211 || threadToken != 7) return 1; Report(1, GC.GetTotalMemory(false)); int exceptionMask = 0; try { try { Throw(); } catch (InvalidOperationException) { exceptionMask |= 1; } try { _ = ReadNull(null); } catch (NullReferenceException) { exceptionMask |= 2; } try { _ = Divide(70, Volatile.Read(ref zeroDivisor)); } catch (DivideByZeroException) { exceptionMask |= 4; } try { _ = Divide(int.MinValue, Volatile.Read(ref zeroDivisor) - 1); } catch (OverflowException) { exceptionMask |= 8; } try { _ = DivideLong(long.MinValue, Volatile.Read(ref zeroDivisor) - 1); } catch (OverflowException) { exceptionMask |= 16; } } finally { Report(2, exceptionMask == 31 ? 1 : 0); } if (exceptionMask != 31) return 2; var cycleTable = new ConditionalWeakTable(); CreateUnrootedCycle(cycleTable, out WeakReference weakKey, out WeakReference weakValue); for (int i = 0; i < 4; i++) { GC.Collect(); GC.WaitForPendingFinalizers(); } if (weakKey.IsAlive || weakValue.IsAlive || Volatile.Read(ref finalized) != 1) return 3; GC.KeepAlive(cycleTable); Report(3, 1); var worker = new Thread(Worker); worker.Start(); lock (Gate) while (!workerReady) if (!Monitor.Wait(Gate, 5000)) return 4; if (Volatile.Read(ref workerResult) != 0) return 4; Report(4, 1); for (int i = 0; i < 8; i++) { _ = new byte[64 * 1024]; GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); if (Volatile.Read(ref workerResult) != 0 || bytes[^1] != 211 || table.GetValue(key, _ => new Root(-1)).Value != 42) return 5; Thread.Yield(); } if (Volatile.Read(ref iterations) < 8 || threadToken != 7) return 5; Report(5, 1); Volatile.Write(ref stopWorker, 1); if (!worker.Join(5000) || Volatile.Read(ref workerResult) != 1 || threadToken != 7) return 6; Report(6, 1); GC.KeepAlive(key); GC.KeepAlive(bytes); return 0; } [MethodImpl(MethodImplOptions.NoInlining)] private static void Throw() => throw new InvalidOperationException("NativeAOT EH probe"); [MethodImpl(MethodImplOptions.NoInlining)] private static int ReadNull(Root? value) => value!.Value; [MethodImpl(MethodImplOptions.NoInlining)] private static int Divide(int numerator, int denominator) => numerator / denominator; [MethodImpl(MethodImplOptions.NoInlining)] private static long DivideLong(long numerator, long denominator) => numerator / denominator; [MethodImpl(MethodImplOptions.NoInlining)] private static void CreateUnrootedCycle(ConditionalWeakTable table, out WeakReference keyReference, out WeakReference valueReference) { var key = new object(); var value = new DependentValue(key); table.Add(key, value); keyReference = new WeakReference(key); valueReference = new WeakReference(value); } [MethodImpl(MethodImplOptions.NoInlining)] private static void Worker() { try { threadToken = 91; var root = new Root(91); var retained = new byte[65536]; retained[0] = 17; retained[^1] = 193; lock (Gate) { workerReady = true; Monitor.PulseAll(Gate); } while (Volatile.Read(ref stopWorker) == 0) { var allocation = new byte[4096]; allocation[^1] = 113; if (root.Value != 91 || retained[0] != 17 || retained[^1] != 193 || allocation[^1] != 113 || threadToken != 91) { Volatile.Write(ref workerResult, -1); return; } Interlocked.Increment(ref iterations); Thread.SpinWait(64); } try { Throw(); } catch (InvalidOperationException) { Volatile.Write(ref workerResult, 1); } GC.KeepAlive(root); GC.KeepAlive(retained); } catch (Exception) { Volatile.Write(ref workerResult, -2); } finally { if (threadToken != 91) Volatile.Write(ref workerResult, -3); } } }