diff options
-rw-r--r-- | src/vm/i386/stublinkerx86.cpp | 30 | ||||
-rw-r--r-- | tests/src/CoreMangLib/system/span/SlowTailCallArgs.cs | 107 | ||||
-rw-r--r-- | tests/src/CoreMangLib/system/span/SlowTailCallArgs.csproj | 1 |
3 files changed, 131 insertions, 7 deletions
diff --git a/src/vm/i386/stublinkerx86.cpp b/src/vm/i386/stublinkerx86.cpp index 7674464cfd..28a012e178 100644 --- a/src/vm/i386/stublinkerx86.cpp +++ b/src/vm/i386/stublinkerx86.cpp @@ -5930,11 +5930,35 @@ static void AppendGCLayout(ULONGARRAY &gcLayout, size_t baseOffset, BOOL fIsType { FindByRefPointerOffsetsInByRefLikeObject( pMT, - baseOffset, + 0 /* baseOffset */, [&](size_t pointerOffset) { - _ASSERTE(gcLayout.Count() == 0 || pointerOffset > (gcLayout[gcLayout.Count() - 1] & ~(ULONG)1)); - *gcLayout.AppendThrowing() = (ULONG)(pointerOffset | 1); // "| 1" to mark it as an interior pointer + // 'gcLayout' requires stack offsets relative to the top of the stack to be recorded, such that subtracting + // the offset from the stack top yields the address of the field, given that subtracting 'baseOffset' from + // the stack top yields the address of the first field in this struct. See TailCallFrame::GcScanRoots() for + // how these offsets are used to calculate stack addresses for fields. + _ASSERTE(pointerOffset < baseOffset); + size_t stackOffsetFromTop = baseOffset - pointerOffset; + _ASSERTE(FitsInU4(stackOffsetFromTop)); + + // Offsets in 'gcLayout' are expected to be in increasing order + int gcLayoutInsertIndex = gcLayout.Count(); + _ASSERTE(gcLayoutInsertIndex >= 0); + for (; gcLayoutInsertIndex != 0; --gcLayoutInsertIndex) + { + ULONG prevStackOffsetFromTop = gcLayout[gcLayoutInsertIndex - 1] & ~(ULONG)1; + if (stackOffsetFromTop > prevStackOffsetFromTop) + { + break; + } + if (stackOffsetFromTop == prevStackOffsetFromTop) + { + return; + } + } + + _ASSERTE(gcLayout.Count() == 0 || stackOffsetFromTop > (gcLayout[gcLayout.Count() - 1] & ~(ULONG)1)); + *gcLayout.InsertThrowing(gcLayoutInsertIndex) = (ULONG)(stackOffsetFromTop | 1); // "| 1" to mark it as an interior pointer }); } diff --git a/tests/src/CoreMangLib/system/span/SlowTailCallArgs.cs b/tests/src/CoreMangLib/system/span/SlowTailCallArgs.cs index 1d85cf883f..da878272e6 100644 --- a/tests/src/CoreMangLib/system/span/SlowTailCallArgs.cs +++ b/tests/src/CoreMangLib/system/span/SlowTailCallArgs.cs @@ -5,11 +5,33 @@ using System; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.InteropServices; internal static class Program { private static int Main() { + bool allPassed = true; + bool passed; + + Console.Write(" SpanTest: "); + passed = SpanTest.Run(); + Console.WriteLine(passed ? "pass" : "fail"); + allPassed &= passed; + + Console.Write(" ByRefLikeTest: "); + passed = ByRefLikeTest.Run(); + Console.WriteLine(passed ? "pass" : "fail"); + allPassed &= passed; + + return allPassed ? 100 : 1; + } +} + +internal static class SpanTest +{ + public static bool Run() + { DynamicMethod dm = new DynamicMethod("TailCaller", typeof(void), new Type[] { typeof(Span<int>) }); ILGenerator il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); @@ -18,7 +40,7 @@ internal static class Program il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Tailcall); - il.Emit(OpCodes.Call, typeof(Program).GetMethod("TailCallee", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit(OpCodes.Call, typeof(SpanTest).GetMethod("TailCallee", BindingFlags.Static | BindingFlags.NonPublic)); il.Emit(OpCodes.Ret); var tailCaller = (ActionOfSpanOfInt)dm.CreateDelegate(typeof(ActionOfSpanOfInt)); @@ -34,10 +56,9 @@ internal static class Program } catch (ArgumentException) { - return 1; // fail + return false; // fail } - - return 100; // pass + return true; } private static void TailCallee(Span<int> a, Span<int> b, Span<int> c, Span<int> d, Span<int> e) @@ -51,3 +72,81 @@ internal static class Program private delegate void ActionOfSpanOfInt(Span<int> x); } + +internal static class ByRefLikeTest +{ + public static bool Run() + { + DynamicMethod dm = new DynamicMethod("TailCaller", typeof(void), new Type[] { typeof(TestByRefLike) }); + ILGenerator il = dm.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Tailcall); + il.Emit(OpCodes.Call, typeof(ByRefLikeTest).GetMethod("TailCallee", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit(OpCodes.Ret); + + var tailCaller = (ActionOfTestByRefLike)dm.CreateDelegate(typeof(ActionOfTestByRefLike)); + + try + { + for (int i = 0; i < 1000; ++i) + { + GC.KeepAlive(new object()); + tailCaller(new TestByRefLike(new int[] { 42 })); + GC.KeepAlive(new object()); + } + } + catch (ArgumentException) + { + return false; // fail + } + return true; + } + + private static void TailCallee(TestByRefLike a, TestByRefLike b, TestByRefLike c, TestByRefLike d, TestByRefLike e) + { + GC.Collect(); + for (int i = 0; i < 10000; i++) + GC.KeepAlive(new object()); + if (a.span[0] != 42 || b.span[0] != 42 || c.span[0] != 42 || d.span[0] != 42 || e.span[0] != 42 || + a.span2[0] != 42 || b.span2[0] != 42 || c.span2[0] != 42 || d.span2[0] != 42 || e.span2[0] != 42) + { + throw new ArgumentException(); + } + } + + private delegate void ActionOfTestByRefLike(TestByRefLike x); + + [StructLayout(LayoutKind.Explicit)] + private ref struct TestByRefLike + { + [FieldOffset(8 * 0)] + private object obj; + [FieldOffset(8 * 0)] + private object obj2; + [FieldOffset(8 * 1)] + public Span<int> span; + [FieldOffset(8 * 1)] + public Span<int> span2; + [FieldOffset(8 * 3)] + private object obj3; + + public TestByRefLike(int[] values) + { + obj = null; + if (obj != null) + obj = null; + obj2 = null; + if (obj2 != null) + obj2 = null; + obj3 = null; + if (obj3 != null) + obj3 = null; + span = new Span<int>(values); + span2 = span; + } + } +} diff --git a/tests/src/CoreMangLib/system/span/SlowTailCallArgs.csproj b/tests/src/CoreMangLib/system/span/SlowTailCallArgs.csproj index 6afa0ee0e6..8acdab76c2 100644 --- a/tests/src/CoreMangLib/system/span/SlowTailCallArgs.csproj +++ b/tests/src/CoreMangLib/system/span/SlowTailCallArgs.csproj @@ -9,6 +9,7 @@ <OutputType>Exe</OutputType> <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> + <LangVersion>latest</LangVersion> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CLRTestKind>BuildAndRun</CLRTestKind> <CLRTestPriority>1</CLRTestPriority> |