diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index d8751ac07c686a..07f367c2af583f 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -3838,6 +3838,20 @@ bool ObjectAllocator::CheckCanClone(CloneInfo* info) JITDUMP("** Seeing if we can clone to guarantee non-escape under V%02u\n", info->m_local); BasicBlock* const allocBlock = info->m_allocBlock; + // Cloning redirects the allocation block's sole outgoing edge to the fast path, + // so the allocation block must be a block kind that has a single target. + // + // If the allocation block ends in a conditional (say the def of the enumerator var + // is in the allocation block, and is followed by a GDV guard), we can't simply + // redirect, so bail out. + // + if (!allocBlock->HasTarget()) + { + JITDUMP("allocation block " FMT_BB " is a %s, and so has no unique target edge\n", allocBlock->bbNum, + bbKindNames[allocBlock->GetKind()]); + return false; + } + // The allocation site must not be in a loop (stack allocation limitation) // // Note if we can prove non-escape but can't stack allocate, we might be diff --git a/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.cs b/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.cs new file mode 100644 index 00000000000000..623576533ee30c --- /dev/null +++ b/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Xml; +using System.Xml.Serialization; +using Xunit; +using TestLibrary; + +public class Runtime_131713 +{ + [ActiveIssue("needs triage", TestRuntimes.Mono)] + [Fact] + public static void Test() + { + // The bug needs the serializer's methods to reach tier-1 with PGO data, so give + // the background compilations time to land in between calls. + for (int i = 0; i < 100; i++) + { + Serialize(); + Thread.Sleep(15); + } + } + + private static void Serialize() + { + Foo model = new() + { + Foos = + [ + new Foo + { + Bars = [new Bar()] + } + ] + }; + + XmlSerializer serializer = new(typeof(Foo)); + using StringWriter stringWriter = new(); + using XmlWriter xmlWriter = XmlWriter.Create(stringWriter); + serializer.Serialize(xmlWriter, model); + } +} + +public class Foo +{ + public CustomList Bars { get; set; } = []; + + public CustomList Foos { get; set; } = []; +} + +public class Bar +{ +} + +public class CustomList : IEnumerable +{ + private readonly List _innerList = []; + + public void Add(T item) => _innerList.Add(item); + + public IEnumerator GetEnumerator() => _innerList.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => _innerList.GetEnumerator(); +} diff --git a/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.csproj b/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.csproj new file mode 100644 index 00000000000000..f963cbbb01f43a --- /dev/null +++ b/src/tests/JIT/opt/ObjectStackAllocation/Runtime_131713.csproj @@ -0,0 +1,20 @@ + + + 1 + + true + None + True + + + + + + + + + + + +