From bfa801814ab007f161dd21561b83ea99ed1a1c28 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 28 Apr 2026 20:41:16 +0200 Subject: [PATCH] JIT: fix Vector{128,256}.ConvertToInt32 use-before-def in gtNewSimdCvtNode When `op1` is not invariant or a local, `fgMakeMultiUse(&op1)` rewrites `op1` into `COMMA(STORE temp, LCL_VAR temp)` and returns a fresh load of `temp`. The STORE is the only place `temp` is written, so it must evaluate before any later read of `temp`. The non-AVX-512 branch passed the clean clone as the first argument of `AND_NOT` and the COMMA-wrapped tree as the second. `AND_NOT(a, b)` decomposes into `AND(a, NOT(b))`, so `a` evaluates first - meaning the `LCL_VAR temp` read happened before the STORE inside `b`, producing garbage for the non-NaN'd input. The bug was latent: prior to #127124 / #127402 the inner `IsNaN(op1)` expanded into real per-element compares that kept enough materialization around to mask the bad ordering. With SIMD32/64 constant propagation, `CompareNotEqual(temp, temp)` value-numbers as AllBitsSet and the entire right subtree collapses to constants, leaving only the broken left-side read - which is what the `Vector512Tests.ConvertToInt32Test` failure on non-AVX-512 hosts (libraries-jitstress-random, nativeaot-outerloop, iossimulator) was actually exercising. Fix: pass `op1` (the COMMA, evaluated first) as `AND_NOT`'s first argument and use the side-effect-free `op1Clone1` for the IsNaN check. Verified by repro on a non-AVX-512 host (DOTNET_EnableAVX512=0): Vector512.ConvertToInt32(Vector512.Create(float.MinValue)) now returns Vector512.Create(int.MinValue) as expected. SPMI benchmarks.run replay clean. Fixes #127440. Supersedes #127499 (which gated unreachable code in `impSpecialIntrinsic` - `NI_Vector512_ConvertToInt32` is already filtered upstream by `lookupId` on non-AVX-512 hosts, so that gate did not actually fix the failure). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/gentree.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a0a8e62c337a40..63490c07ca3a8a 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -23197,8 +23197,8 @@ GenTree* Compiler::gtNewSimdCvtNode( // mask1 contains the output either 0xFFFFFFFF or 0. // FixupVal zeros out any NaN values in the input by ANDing input with mask1. GenTree* op1Clone1 = fgMakeMultiUse(&op1); - GenTree* mask1 = gtNewSimdIsNaNNode(type, op1, simdSourceBaseType, simdSize); - fixupVal = gtNewSimdBinOpNode(GT_AND_NOT, type, op1Clone1, mask1, simdSourceBaseType, simdSize); + GenTree* mask1 = gtNewSimdIsNaNNode(type, op1Clone1, simdSourceBaseType, simdSize); + fixupVal = gtNewSimdBinOpNode(GT_AND_NOT, type, op1, mask1, simdSourceBaseType, simdSize); } if (varTypeIsSigned(simdTargetBaseType))