From 8550be3afdea349364cc3aaa5d1edd0a20a604d8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 21 Sep 2026 16:35:32 -0700 Subject: [PATCH 1/3] JIT: Guard comma throw propagation types Comma throw propagation could create invalid struct-typed zero constants. Centralize the BashToZeroConst type constraint and skip propagation when a zero constant cannot represent the parent type. Fixes #133862 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d1dcb1f4-83e0-44fa-8060-109bd4fa2957 --- src/coreclr/jit/compiler.hpp | 15 ++++++++-- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/morph.cpp | 12 ++++++-- .../Runtime_133862/Runtime_133862.cs | 29 +++++++++++++++++++ .../Runtime_133862/Runtime_133862.csproj | 12 ++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs create mode 100644 src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index f531edf3ce4c7b..4b872124946b10 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -2173,6 +2173,17 @@ void GenTree::BashToConst(T value, var_types type /* = TYP_UNDEF */) } } +//------------------------------------------------------------------------ +// CanBashToZeroConst: Check whether a node can be bashed to a zero constant of "type". +// +// Arguments: +// type - Type the bashed node will have. +// +inline bool GenTree::CanBashToZeroConst(var_types type) +{ + return varTypeIsFloating(type) || varTypeIsIntegral(type) || varTypeIsGC(type); +} + //------------------------------------------------------------------------ // BashToZeroConst: Bash the node to a constant representing "zero" of "type". // @@ -2182,14 +2193,14 @@ void GenTree::BashToConst(T value, var_types type /* = TYP_UNDEF */) // inline void GenTree::BashToZeroConst(var_types type) { + assert(CanBashToZeroConst(type)); + if (varTypeIsFloating(type)) { BashToConst(0.0, type); } else { - assert(varTypeIsIntegral(type) || varTypeIsGC(type)); - // "genActualType" so that we do not create CNS_INT(small type). BashToConst(0, genActualType(type)); } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 60477341da53e0..5dd2518775a53b 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2139,6 +2139,7 @@ struct GenTree template void BashToConst(T value, var_types type = TYP_UNDEF); + static bool CanBashToZeroConst(var_types type); void BashToZeroConst(var_types type); GenTreeLclVar* BashToLclVar(Compiler* comp, unsigned lclNum); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ed49402b9db54a..be41bc437d1826 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11160,10 +11160,16 @@ GenTree* Compiler::fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, } // Fix up the COMMA's type if needed. - if (genActualType(parent) != genActualType(commaThrow)) + var_types parentType = genActualType(parent); + if (parentType != genActualType(commaThrow)) { - commaThrow->gtGetOp2()->BashToZeroConst(genActualType(parent)); - commaThrow->ChangeType(genActualType(parent)); + if (!GenTree::CanBashToZeroConst(parentType)) + { + return nullptr; + } + + commaThrow->gtGetOp2()->BashToZeroConst(parentType); + commaThrow->ChangeType(parentType); } return commaThrow; diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs new file mode 100644 index 00000000000000..270948fdb2cb56 --- /dev/null +++ b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs @@ -0,0 +1,29 @@ +// 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_133862 +{ + private struct S + { + public int A; + public int B; + public int C; + public int D; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Index(long value) => checked((int)value); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static S Load(S[] values) => values[Index(long.MaxValue)]; + + [Fact] + public static void TestEntryPoint() + { + Assert.Throws(() => Load(new S[4])); + } +} diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj new file mode 100644 index 00000000000000..bf39be5a4b6f7e --- /dev/null +++ b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj @@ -0,0 +1,12 @@ + + + 1 + True + + true + + + + + + From fb870b5c75080f3877af0fa708b41d7e398cfc82 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 24 Sep 2026 16:09:15 -0700 Subject: [PATCH 2/3] Address comma throw propagation review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/compiler.hpp | 15 ++------------- src/coreclr/jit/gentree.h | 1 - src/coreclr/jit/morph.cpp | 4 ++-- .../Regression_2/Runtime_133862/Runtime_133862.cs | 2 +- .../Runtime_133862/Runtime_133862.csproj | 4 ---- 5 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 4b872124946b10..f531edf3ce4c7b 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -2173,17 +2173,6 @@ void GenTree::BashToConst(T value, var_types type /* = TYP_UNDEF */) } } -//------------------------------------------------------------------------ -// CanBashToZeroConst: Check whether a node can be bashed to a zero constant of "type". -// -// Arguments: -// type - Type the bashed node will have. -// -inline bool GenTree::CanBashToZeroConst(var_types type) -{ - return varTypeIsFloating(type) || varTypeIsIntegral(type) || varTypeIsGC(type); -} - //------------------------------------------------------------------------ // BashToZeroConst: Bash the node to a constant representing "zero" of "type". // @@ -2193,14 +2182,14 @@ inline bool GenTree::CanBashToZeroConst(var_types type) // inline void GenTree::BashToZeroConst(var_types type) { - assert(CanBashToZeroConst(type)); - if (varTypeIsFloating(type)) { BashToConst(0.0, type); } else { + assert(varTypeIsIntegral(type) || varTypeIsGC(type)); + // "genActualType" so that we do not create CNS_INT(small type). BashToConst(0, genActualType(type)); } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5dd2518775a53b..60477341da53e0 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2139,7 +2139,6 @@ struct GenTree template void BashToConst(T value, var_types type = TYP_UNDEF); - static bool CanBashToZeroConst(var_types type); void BashToZeroConst(var_types type); GenTreeLclVar* BashToLclVar(Compiler* comp, unsigned lclNum); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index be41bc437d1826..29e8e04f49e922 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11163,12 +11163,12 @@ GenTree* Compiler::fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, var_types parentType = genActualType(parent); if (parentType != genActualType(commaThrow)) { - if (!GenTree::CanBashToZeroConst(parentType)) + if (parentType == TYP_STRUCT) { return nullptr; } - commaThrow->gtGetOp2()->BashToZeroConst(parentType); + commaThrow->gtOp2 = gtNewZeroConNode(parentType); commaThrow->ChangeType(parentType); } diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs index 270948fdb2cb56..1b267778a55007 100644 --- a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs +++ b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs @@ -18,7 +18,7 @@ private struct S [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Index(long value) => checked((int)value); - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] private static S Load(S[] values) => values[Index(long.MaxValue)]; [Fact] diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj index bf39be5a4b6f7e..de6d5e08882e86 100644 --- a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj +++ b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj @@ -1,12 +1,8 @@ - 1 True - - true - From 02994e42d18233c7416410db056856339a1964c1 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 24 Sep 2026 16:46:26 -0700 Subject: [PATCH 3/3] Use merged runner for comma throw regression Mark the replacement zero node as morphed before returning the propagated comma. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/morph.cpp | 5 ++++- .../JIT/Regression_2/Runtime_133862/Runtime_133862.csproj | 8 -------- .../Runtime_133862 => Regression_o_2}/Runtime_133862.cs | 0 3 files changed, 4 insertions(+), 9 deletions(-) delete mode 100644 src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj rename src/tests/JIT/{Regression_2/Runtime_133862 => Regression_o_2}/Runtime_133862.cs (100%) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 29e8e04f49e922..260cc7d918249c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11168,7 +11168,10 @@ GenTree* Compiler::fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, return nullptr; } - commaThrow->gtOp2 = gtNewZeroConNode(parentType); + GenTree* zero = gtNewZeroConNode(parentType); + zero->SetMorphed(this); + + commaThrow->gtOp2 = zero; commaThrow->ChangeType(parentType); } diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj b/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj deleted file mode 100644 index de6d5e08882e86..00000000000000 --- a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - True - - - - - diff --git a/src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs b/src/tests/JIT/Regression_o_2/Runtime_133862.cs similarity index 100% rename from src/tests/JIT/Regression_2/Runtime_133862/Runtime_133862.cs rename to src/tests/JIT/Regression_o_2/Runtime_133862.cs