diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 11743cf7d3e5e..e2c4e797a3c9d 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -2404,6 +2404,14 @@ void ReplaceVisitor::ReplaceLocal(GenTree** use, GenTree* user) { lcl->gtFlags |= GTF_VAR_DEATH; CheckForwardSubForLastUse(lclNum); + + // Relying on the values in the struct local after this struct use + // would effectively introduce another use of the struct, so + // indicate that no replacements are up to date. + for (Replacement& rep : replacements) + { + SetNeedsWriteBack(rep); + } } return; } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.cs b/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.cs new file mode 100644 index 0000000000000..0756a0de981d6 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_88616 +{ + [Fact] + public static int TestEntryPoint() + { + S foo; + foo.A = 10; + foo.B = 20; + foo.C = 30; + foo.D = 40; + foo.E = 50; + foo.A += foo.A += foo.A += foo.A += foo.A; + foo.B += foo.B += foo.B += foo.B += foo.B; + foo.C += foo.C += foo.C += foo.C += foo.C; + foo.D += foo.D += foo.D += foo.D += foo.D; + foo.E += foo.E += foo.E += foo.E += foo.E; + // 'foo' is a last use here (it is fully promoted so all of its state + // is stored in separate field locals), so physical promotions marks + // this occurence as GTF_VAR_DEATH. + Mutate(foo); + // However, we cannot use the fact that we wrote the fields back into + // the struct local above to skip those same write backs here; if we + // skip those write backs then we effectively introduce new uses of the + // struct local. + return Check(foo); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Mutate(S s) + { + s.A = -42; + Consume(ref s); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Consume(ref S s) + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Check(S s) + { + if (s.A != 50) + { + Console.WriteLine("FAIL: s.A == {0}", s.A); + return -1; + } + + return 100; + } + + private struct S + { + public long A, B, C, D, E; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.csproj new file mode 100644 index 0000000000000..1bb887ea34b0f --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_88616/Runtime_88616.csproj @@ -0,0 +1,9 @@ + + + True + None + + + + + \ No newline at end of file