From 678b988a9b0464d9aa045813f7cafb070c569422 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 12:52:33 +0000 Subject: [PATCH 1/4] Initial plan From 07f7c2fc6530e62fda79339dbe5122b13b9f0511 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 13:00:37 +0000 Subject: [PATCH 2/4] JIT: bail on flipping post-layout JTRUE when reversal needs new IR Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5d85b59c-0590-49b1-9854-94014903fa1a Co-authored-by: jakobbotsch <7887810+jakobbotsch@users.noreply.github.com> --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/gentree.cpp | 33 +++++++++++++++++++++++++++------ src/coreclr/jit/optimizer.cpp | 23 +++++++++++------------ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 74ebf2548df805..9f9d002777bb30 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3813,6 +3813,7 @@ class Compiler bool gtComplexityExceeds(GenTree* tree, unsigned limit, TFunc getComplexity); GenTree* gtReverseCond(GenTree* tree); + bool gtTryReverseCond(GenTree* tree); static bool gtHasRef(GenTree* tree, unsigned lclNum); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index f9365400bd7f09..19df7942f1760d 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -3710,10 +3710,16 @@ genTreeOps GenTree::SwapRelop(genTreeOps relop) /***************************************************************************** * - * Reverse the meaning of the given test condition. + * Try to reverse the meaning of the given test condition in-place, + * without introducing any new IR nodes. + * + * Returns true if the condition was reversed in-place. + * Returns false if reversing the condition would require introducing a new + * node (e.g. wrapping the tree in a "tree == 0" comparison). In that case, + * the tree is not modified. */ -GenTree* Compiler::gtReverseCond(GenTree* tree) +bool Compiler::gtTryReverseCond(GenTree* tree) { if (tree->OperIsCompare()) { @@ -3727,23 +3733,38 @@ GenTree* Compiler::gtReverseCond(GenTree* tree) { tree->gtFlags ^= GTF_RELOP_NAN_UN; } + return true; } - else if (tree->OperIs(GT_JCC, GT_SETCC)) + if (tree->OperIs(GT_JCC, GT_SETCC)) { GenTreeCC* cc = tree->AsCC(); cc->gtCondition = GenCondition::Reverse(cc->gtCondition); + return true; } - else if (tree->OperIs(GT_JCMP, GT_JTEST)) + if (tree->OperIs(GT_JCMP, GT_JTEST)) { GenTreeOpCC* opCC = tree->AsOpCC(); opCC->gtCondition = GenCondition::Reverse(opCC->gtCondition); + return true; } - else if (tree->IsIntegralConst()) + if (tree->IsIntegralConst()) { GenTreeIntConCommon* con = tree->AsIntConCommon(); con->SetIntegralValue(con->IsIntegralConst(0) ? 1 : 0); + return true; } - else + + return false; +} + +/***************************************************************************** + * + * Reverse the meaning of the given test condition. + */ + +GenTree* Compiler::gtReverseCond(GenTree* tree) +{ + if (!gtTryReverseCond(tree)) { tree = gtNewOperNode(GT_EQ, TYP_INT, tree, gtNewZeroConNode(TYP_INT)); } diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 636d588bb82d2a..a2e1f073021b08 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -2246,22 +2246,21 @@ PhaseStatus Compiler::optOptimizePostLayout() GenTree* const test = block->lastNode(); assert(test->OperIsConditionalJump()); + // Try to reverse the condition in-place. We are running after LSRA, so we cannot + // introduce new IR nodes here. If the condition cannot be reversed in-place, bail + // on flipping for this block. + // + // For GT_JTRUE the operand may have a GT_COPY/GT_RELOAD inserted by LSRA on top of + // the actual condition node, so skip those to find the underlying condition. + GenTree* cond = test; if (test->OperIs(GT_JTRUE)) { - // Flip GT_JTRUE node's conditional operand, and handle any new nodes this may introduce - GenTree* const cond = test->gtGetOp1(); - GenTree* const newCond = gtReverseCond(cond); - if (cond != newCond) - { - LIR::AsRange(block).InsertAfter(cond, newCond); - test->AsUnOp()->gtOp1 = newCond; - } + cond = test->gtGetOp1()->gtSkipReloadOrCopy(); } - else + + if (!gtTryReverseCond(cond)) { - // gtReverseCond can handle other conditional jumps without introducing a new node - GenTree* const cond = gtReverseCond(test); - assert(cond == test); + continue; } FlowEdge* const oldTrueEdge = block->GetTrueEdge(); From fa6d9077cc172338c5e98548cfaf5fe3aeb59782 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 13:10:23 +0000 Subject: [PATCH 3/4] Update gtReverseCond/gtTryReverseCond function headers to new style Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/c3a02af3-6597-451c-ad3c-267603ddb8ba Co-authored-by: jakobbotsch <7887810+jakobbotsch@users.noreply.github.com> --- src/coreclr/jit/gentree.cpp | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 19df7942f1760d..ceeebbdf9d3543 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -3708,17 +3708,18 @@ genTreeOps GenTree::SwapRelop(genTreeOps relop) return swapOps[relop - GT_EQ]; } -/***************************************************************************** - * - * Try to reverse the meaning of the given test condition in-place, - * without introducing any new IR nodes. - * - * Returns true if the condition was reversed in-place. - * Returns false if reversing the condition would require introducing a new - * node (e.g. wrapping the tree in a "tree == 0" comparison). In that case, - * the tree is not modified. - */ - +//------------------------------------------------------------------------ +// gtTryReverseCond: Try to reverse the meaning of the given test condition +// in-place, without introducing any new IR nodes. +// +// Arguments: +// tree - The condition tree to reverse +// +// Return Value: +// True if the condition was reversed in-place. False if reversing the +// condition would require introducing a new node (e.g. wrapping the tree +// in a "tree == 0" comparison); in that case, the tree is not modified. +// bool Compiler::gtTryReverseCond(GenTree* tree) { if (tree->OperIsCompare()) @@ -3757,11 +3758,18 @@ bool Compiler::gtTryReverseCond(GenTree* tree) return false; } -/***************************************************************************** - * - * Reverse the meaning of the given test condition. - */ - +//------------------------------------------------------------------------ +// gtReverseCond: Reverse the meaning of the given test condition. +// +// Arguments: +// tree - The condition tree to reverse +// +// Return Value: +// The reversed condition tree. This is normally the same node as the +// input tree, with its operator (or condition) reversed in-place. If the +// tree cannot be reversed in-place, a new GT_EQ node comparing the tree +// to zero is returned instead. +// GenTree* Compiler::gtReverseCond(GenTree* tree) { if (!gtTryReverseCond(tree)) From 2183a1edeb3e7a54c31786dac2d67e0842b69fbe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:51:44 +0000 Subject: [PATCH 4/4] Add JIT regression test for Runtime_127745 (optOptimizePostLayout LIR assert) Co-authored-by: dhartglassMSFT <248563697+dhartglassMSFT@users.noreply.github.com> --- .../JitBlue/Runtime_127745/Runtime_127745.cs | 99 +++++++++++++++++++ .../JIT/Regression/Regression_ro_2.csproj | 1 + 2 files changed, 100 insertions(+) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_127745/Runtime_127745.cs diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_127745/Runtime_127745.cs b/src/tests/JIT/Regression/JitBlue/Runtime_127745/Runtime_127745.cs new file mode 100644 index 00000000000000..ecef6e4c390d57 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_127745/Runtime_127745.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Regression test: optOptimizePostLayout could assert "found use of a node that +// is not in the LIR sequence" on arm32 when LSRA inserted a RELOAD on top of +// a SETCC result under a JTRUE. gtReverseCond then returned a newly-allocated +// EQ(reload, 0) tree whose CNS_INT 0 child was never threaded into LIR. + +namespace Runtime_127745; + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_127745 +{ + public static IRuntime127745 s_rt; + public static short[][][] s_15; + public static uint[,] s_34; + public static byte[] s_53; + public static int s_62; + public static long[][] s_90; + public static int s_108; + public static int[] s_110; + public static long s_114; + public static ushort s_130; + + [Fact] + public static void TestEntryPoint() + { + // Calling M96 forces the JIT to compile it. The regression was a + // compile-time assertion rather than wrong output. The static fields are + // all null, so execution throws NullReferenceException immediately. + try + { + RunM96(); + } + catch (NullReferenceException) + { + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void RunM96() + { + M96(s_90); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ref byte M96(long[][] arg0) + { + byte lvar0 = default(byte); + do + { + s_rt.WriteLine("c_677", 0); + } + while (++lvar0 < 252); + try + { + var vr2 = new short[] { 1 }; + M100(ref arg0[0][0], ref arg0[0][0], vr2); + } + finally + { + arg0[0] = arg0[0]; + } + + var vr1 = s_15[0][0][0]; + if ((1 <= s_114) | (0 > M97(vr1))) + { + arg0[0][0] >>= 1; + } + else + { + s_62 = s_110[0]; + } + + s_rt.WriteLine("c_715", arg0[0][0]); + s_rt.WriteLine("c_716", s_108); + return ref s_53[0]; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort M97(short arg0) + { + bool vr0 = 0 > s_34[0, 0]; + return s_130; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void M100(ref long arg0, ref long arg1, short[] arg2) + { + } +} + +public interface IRuntime127745 +{ + void WriteLine(string site, T value); +} diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj index d4d6bd2ba5d72e..fcdc87296505ae 100644 --- a/src/tests/JIT/Regression/Regression_ro_2.csproj +++ b/src/tests/JIT/Regression/Regression_ro_2.csproj @@ -102,6 +102,7 @@ +