From 31fd405aa8d13fc8975689aa598e84f43b23ebba Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 17 Jul 2026 11:00:02 -0700 Subject: [PATCH 1/2] Fix codegen assert reloading a spilled signed small cast source A cast whose operand is used from memory (`castIsLoad`) computed its load type from `src->TypeGet()`. For an LSRA spill temp holding a signed small load (e.g. a sign-extended `sbyte`) that feeds a zero-extending cast to `ushort`, that reported the small signed type, asserting in `GenIntCastDesc` and -- absent asserts -- emitting a byte `movzx` that drops the sign bits the 4-byte temp actually holds. Reload a spill temp as its actual type instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegenlinear.cpp | 6 +- .../JitBlue/Runtime_124508/Runtime_124508.cs | 279 ++++++++++++++++++ .../Runtime_124508/Runtime_124508.csproj | 12 + 3 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 49f67585225024..04310d7e350f42 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -2555,7 +2555,11 @@ CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) if (castIsLoad) { - const var_types srcLoadType = src->TypeGet(); + // A spill temp holds the full actual-type value, already extended per the source's own + // signedness, so it must be reloaded as that actual type rather than as a small memory + // operand -- otherwise a signed small source (e.g. TYP_BYTE) reloaded under a zero-extending + // cast would drop the sign bits it actually holds. + const var_types srcLoadType = src->isUsedFromSpillTemp() ? srcType : src->TypeGet(); switch (m_extendKind) { diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs new file mode 100644 index 00000000000000..fc3ccfeb96fc4a --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Generated by Fuzzlyn v3.3 on 2026-02-15 18:01:33 +// Seed: 12192374036514626124 +// +// A signed small load (sbyte[] element, materialized via movsx) that feeds a +// zero-extending cast to ushort could be spilled by LSRA under high register +// pressure (x86, FullOpts). GenIntCastDesc reloaded the spill temp as the small +// signed type, which asserted (and would have emitted a 1-byte movzx that drops +// the sign bits the temp actually holds) instead of reloading the full 4-byte +// actual type. Compiling M3 exercises the shape (in the switch's case 4). + +using System.Numerics; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Xunit; + +public class Program +{ + public static IRuntime s_rt; + public static sbyte[] s_1; + public static long s_3; + public static byte s_4; + public static Vector256 s_5; + public static ulong s_8; + public static byte s_12; + public static int s_13; + public static uint s_15; + public static uint s_16; + public static Vector256[] s_17; + public static ulong[] s_18; + public static Vector128 s_23 = Vector128.Create(0, 1U, 0, 0); + + [Fact] + public static void TestEntryPoint() + { + s_rt = new Runtime(); + try + { + M3(); + } + catch { } + } + + public static ushort M3() + { + Vector128[] var1 = default(Vector128[]); + uint var5 = default(uint); + Vector256 var7 = default(Vector256); + Vector128 var9 = default(Vector128); + uint var15 = default(uint); + Vector var18 = default(Vector); + sbyte[] var19 = default(sbyte[]); + byte var3 = 0; + Vector[] var4 = new Vector[] + { + Vector.Create(0) + }; + s_rt.WriteLine(var5); + try + { + throw new System.Exception(); + } + catch (System.NotSupportedException) + { + var1 = new Vector128[] + { + Vector128.Create(0) + }; + } + catch (System.NotImplementedException) + { + s_rt.WriteLine(var7); + } + catch (System.InvalidOperationException) + { + ulong var8 = s_8; + s_rt.WriteLine(var9); + long var10 = 0; + s_rt.WriteLine(var8); + s_rt.WriteLine(var10); + } + catch (System.ArgumentOutOfRangeException) + { + uint[][][] var11 = new uint[][][] + { + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 }, + new uint[] { 0 } + }, + new uint[][] + { + new uint[] { 0 }, + new uint[] { 0 } + } + }; + s_rt.WriteLine(var11[0][0][0]); + } + catch (System.ArgumentNullException) + { + short var12 = 0; + Vector256 var13 = s_5; + s_rt.WriteLine(var12); + s_rt.WriteLine(var13); + } + catch (System.Exception) + { + s_1[0] >>= (int)Bmi1.BitFieldExtract(0, 0, var3); + Vector128.Create(s_3); + } + + s_rt.WriteLine(var3); + ushort[] var16 = new ushort[] + { + 0 + }; + var vr4 = var1[0]; + Sse2.CompareLessThan(vr4, Vector128.Create((short)Bmi2.ParallelBitExtract(var15, var15))); + s_rt.WriteLine(var16[0]); + var1[0] = var1[0]; + switch (s_13--) + { + case 0: + { + s_16 += M5(); + Vector.Create(s_12); + s_rt.WriteLine(var18); + s_rt.WriteLine(var19[0]); + var vr1 = (byte)M11(); + Vector128.Create(vr1); + } + + break; + case 1: + { + Vector256[] var20 = s_17; + ulong[] var21 = s_18; + s_rt.WriteLine(var20[0]); + s_rt.WriteLine(var21[0]); + } + + break; + case -1: + { + sbyte var22 = s_1[0]++; + Vector128[,, ] var25 = new Vector128[,, ] + { + { + { + Vector128.Create(0, 0, 0, 0, 0, 0, 0, 0) + } + } + }; + sbyte var26 = var22; + s_17[0] = s_17[0]; + var vr3 = s_4++; + var vr0 = M5(); + M9(vr0, (byte)Bmi2.ParallelBitExtract(s_15, (uint)Sse2.MoveMask(Vector128.CreateScalar((byte)0))), s_8--); + s_rt.WriteLine(var25[0, 0, 0]); + s_rt.WriteLine(var26); + s_rt.WriteLine(var22); + } + + break; + case 3: + { + } + + break; + case 4: + { + s_17[0] = s_17[0]; + Vector128 var33 = Vector128.Create(0, 0, 0, 0); + bool var34 = -1 <= M11(); + var1[0] = Vector128.CreateScalar((short)(s_1[0] << (0 >> (ushort)M9(0, 0, 0)))); + var1 = new Vector128[] + { + Vector128.Create(0) + }; + s_rt.WriteLine(var33); + s_rt.WriteLine(var34); + } + + break; + case 5: + { + } + + break; + case 6: + { + } + + break; + case 7: + { + } + + break; + default: + { + } + + break; + } + + var vr6 = Vector128.CreateScalar(0); + var vr5 = (uint)Sse41.Extract(vr6, 0); + return (ushort)Bmi2.ZeroHighBits(vr5, 0); + } + + public static ushort M5() + { + return default(ushort); + } + + public static long M9(ushort arg0, byte arg1, ulong arg2) + { + return s_3; + } + + public static long M11() + { + var vr2 = Vector256.Create(-2); + Avx512F.VL.ConvertToVector128Int16(vr2); + return 7708512004982082393L; + } +} + +public interface IRuntime +{ + void WriteLine(T value); +} + +public class Runtime : IRuntime +{ + public void WriteLine(T value) => System.Console.WriteLine(value); +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj new file mode 100644 index 00000000000000..5d107ffa5e2f80 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj @@ -0,0 +1,12 @@ + + + True + + true + + + + + + + From 2662bd82fd5b0af38280bad985fd1e000a34b337 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 17 Jul 2026 12:36:14 -0700 Subject: [PATCH 2/2] Convert regression test to merged format Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../JitBlue/Runtime_124508/Runtime_124508.cs | 4 +++- .../JitBlue/Runtime_124508/Runtime_124508.csproj | 12 ------------ src/tests/JIT/Regression/Regression_ro_1.csproj | 1 + 3 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs index fc3ccfeb96fc4a..e5f91f11e5e8c1 100644 --- a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs +++ b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.cs @@ -11,12 +11,14 @@ // the sign bits the temp actually holds) instead of reloading the full 4-byte // actual type. Compiling M3 exercises the shape (in the switch's case 4). +namespace Runtime_124508; + using System.Numerics; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using Xunit; -public class Program +public class Runtime_124508 { public static IRuntime s_rt; public static sbyte[] s_1; diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj deleted file mode 100644 index 5d107ffa5e2f80..00000000000000 --- a/src/tests/JIT/Regression/JitBlue/Runtime_124508/Runtime_124508.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - True - - true - - - - - - - diff --git a/src/tests/JIT/Regression/Regression_ro_1.csproj b/src/tests/JIT/Regression/Regression_ro_1.csproj index b332ffd68aeac6..6ce2fb8e85386e 100644 --- a/src/tests/JIT/Regression/Regression_ro_1.csproj +++ b/src/tests/JIT/Regression/Regression_ro_1.csproj @@ -78,6 +78,7 @@ +