diff --git a/src/coreclr/ildasm/dasm.cpp b/src/coreclr/ildasm/dasm.cpp index 45f0070ffc390d..d83c1822db472a 100644 --- a/src/coreclr/ildasm/dasm.cpp +++ b/src/coreclr/ildasm/dasm.cpp @@ -2593,6 +2593,7 @@ void DumpDefaultValue(mdToken tok, __inout __nullterminated char* szString, void char szf[32]; sprintf_s(szf, 32, "%#.8g", (double)MDDV.m_fltValue); float df = (float)atof(szf); + NormalizeFloatString(szf); // Must compare as underlying bytes, not floating point otherwise optimizer will // try to enregister and compare 80-bit precision number with 32-bit precision number!!!! if((*(ULONG*)&df == MDDV.m_ulValue)&&!IsSpecialNumber(szf)) @@ -2608,6 +2609,7 @@ void DumpDefaultValue(mdToken tok, __inout __nullterminated char* szString, void sprintf_s(szf, 32, "%#.17g", MDDV.m_dblValue); double df = strtod(szf, &pch); //atof(szf); szf[31]=0; + NormalizeFloatString(szf); // Must compare as underlying bytes, not floating point otherwise optimizer will // try to enregister and compare 80-bit precision number with 64-bit precision number!!!! if((*(ULONGLONG*)&df == MDDV.m_ullValue)&&!IsSpecialNumber(szf)) diff --git a/src/coreclr/ildasm/dis.cpp b/src/coreclr/ildasm/dis.cpp index 8a70fc714433c2..5b784e4f9424cf 100644 --- a/src/coreclr/ildasm/dis.cpp +++ b/src/coreclr/ildasm/dis.cpp @@ -2767,3 +2767,25 @@ bool IsSpecialNumber(const char* szf) || (strstr(szf, "nan") != NULL) || (strstr(szf, "NAN") != NULL) || (strstr(szf, "inf") != NULL) || (strstr(szf, "INF") != NULL); } + +void NormalizeFloatString(char* szf) +{ + // Normalize NaN representations to remove platform-specific suffixes + // Windows can produce "-nan(ind)" while Linux produces "-nan" + // This function removes the "(ind)" suffix to ensure cross-platform consistency + + char* nan_pos = strstr(szf, "nan("); + if (nan_pos != NULL) + { + // "nan" is 3 characters, so truncate at position 3 from the match + nan_pos[3] = '\0'; + } + + // Also handle uppercase NAN + char* NAN_pos = strstr(szf, "NAN("); + if (NAN_pos != NULL) + { + // "NAN" is 3 characters, so truncate at position 3 from the match + NAN_pos[3] = '\0'; + } +} diff --git a/src/coreclr/ildasm/dis.h b/src/coreclr/ildasm/dis.h index 1b60192a60b82e..1f75e5f8cd7434 100644 --- a/src/coreclr/ildasm/dis.h +++ b/src/coreclr/ildasm/dis.h @@ -83,6 +83,7 @@ char* DumpUnicodeString(void* GUICookie, void TokenSigInit(IMDInternalImport *pImport); void TokenSigDelete(); bool IsSpecialNumber(const char*); +void NormalizeFloatString(char*); //---------------- see DMAN.CPP-------------------------------------------------- diff --git a/src/tests/ilasm/FloatSpecialValues/FloatSpecialValues.il b/src/tests/ilasm/FloatSpecialValues/FloatSpecialValues.il new file mode 100644 index 00000000000000..1cc689c0716b3a --- /dev/null +++ b/src/tests/ilasm/FloatSpecialValues/FloatSpecialValues.il @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { } +.assembly FloatSpecialValues { } + +.class public auto ansi beforefieldinit TestFloatSpecialValues + extends [System.Runtime]System.Object +{ + // Test NaN values - these should output with normalized "// -nan" comment + .field public static literal float64 NaNValue = float64(0xFFF8000000000000) + + // Test infinity values for completeness + .field public static literal float64 PositiveInf = float64(0x7FF0000000000000) + .field public static literal float64 NegativeInf = float64(0xFFF0000000000000) + + // Test float32 NaN as well + .field public static literal float32 NaNValue32 = float32(0xFFC00000) + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} diff --git a/src/tests/ilasm/FloatSpecialValues/FloatSpecialValuesTests.cs b/src/tests/ilasm/FloatSpecialValues/FloatSpecialValuesTests.cs new file mode 100644 index 00000000000000..c57f50528f5cbc --- /dev/null +++ b/src/tests/ilasm/FloatSpecialValues/FloatSpecialValuesTests.cs @@ -0,0 +1,117 @@ +// 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.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using Xunit; + +public class FloatSpecialValuesTests : IDisposable +{ + private string _ilasmFile; + private string _ildasmFile; + + public FloatSpecialValuesTests() + { + string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT"); + if (string.IsNullOrWhiteSpace(coreRoot)) + { + Console.WriteLine("Environment variable is not set: 'CORE_ROOT'"); + throw new InvalidOperationException("Environment variable is not set: 'CORE_ROOT'"); + } + if (!Directory.Exists(coreRoot)) + { + Console.WriteLine($"Did not find CORE_ROOT directory: {coreRoot}"); + throw new InvalidOperationException("Did not find CORE_ROOT directory"); + } + + var nativeExeExtensions = new string[] { string.Empty, ".exe" }; + bool found = false; + string ilasmFile = null; + string ildasmFile = null; + foreach (string nativeExeExtension in nativeExeExtensions) + { + ilasmFile = Path.Combine(coreRoot, $"ilasm{nativeExeExtension}"); + ildasmFile = Path.Combine(coreRoot, $"ildasm{nativeExeExtension}"); + if (File.Exists(ilasmFile) && File.Exists(ildasmFile)) + { + found = true; + break; + } + } + if (!found) + { + Console.WriteLine($"Did not find ilasm or ildasm in CORE_ROOT directory: {coreRoot}"); + throw new InvalidOperationException("Did not find ilasm or ildasm in CORE_ROOT directory"); + } + + _ilasmFile = ilasmFile; + _ildasmFile = ildasmFile; + } + + [Fact] + public void NaNOutputIsNormalizedAcrossPlatforms() + { + Console.WriteLine("NaNOutputIsNormalizedAcrossPlatforms"); + + string ilFileName = "FloatSpecialValues.il"; + string disasmIlFileName; + ProcessStartInfo ilasmPsi, ildasmPsi; + GetIlasmProcessStartInfos(_ilasmFile, _ildasmFile, ilFileName, out disasmIlFileName, out ilasmPsi, out ildasmPsi); + + Process ilasmProcess = Process.Start(ilasmPsi); + ilasmProcess.WaitForExit(); + Assert.Equal(0, ilasmProcess.ExitCode); + + Process ildasmProcess = Process.Start(ildasmPsi); + ildasmProcess.WaitForExit(); + Assert.Equal(0, ildasmProcess.ExitCode); + + string disasmIl = File.ReadAllText(disasmIlFileName); + + // Verify that NaN values use the normalized format without platform-specific suffixes + // The hex representation should be followed by "// -nan" without "(ind)" suffix + var nanFloat64Regex = new Regex(@"float64\(0xFFF8000000000000\)\s+//\s+-nan\s", RegexOptions.Compiled); + Assert.True(nanFloat64Regex.IsMatch(disasmIl), + "NaN float64 should be output as '// -nan' without platform-specific suffixes like '(ind)'"); + + // Verify the output does NOT contain the Windows-specific "(ind)" suffix + Assert.DoesNotContain("-nan(ind)", disasmIl); + Assert.DoesNotContain("-nan(", disasmIl); + } + + private static void GetIlasmProcessStartInfos( + string ilasmFile, + string ildasmFile, + string ilFileName, + out string disasmIlFileName, + out ProcessStartInfo ilasmPsi, + out ProcessStartInfo ildasmPsi) + { + if (!File.Exists(ilFileName)) + { + throw new FileNotFoundException( + $"Did not find '{ilFileName}' in working directory '{Environment.CurrentDirectory}'"); + } + + string currentDirectory = Environment.CurrentDirectory; + + ilasmPsi = new ProcessStartInfo(); + ilasmPsi.UseShellExecute = false; + ilasmPsi.WorkingDirectory = currentDirectory; + ilasmPsi.FileName = ilasmFile; + string asmDllFileName = $"{Path.GetFileNameWithoutExtension(ilFileName)}.dll"; + ilasmPsi.Arguments = + $"-nologo -dll -optimize -output={asmDllFileName} {ilFileName}"; + + ildasmPsi = new ProcessStartInfo(); + ildasmPsi.UseShellExecute = false; + ildasmPsi.WorkingDirectory = currentDirectory; + ildasmPsi.FileName = ildasmFile; + disasmIlFileName = $"{Path.GetFileNameWithoutExtension(ilFileName)}_dis{Path.GetExtension(ilFileName)}"; + ildasmPsi.Arguments = $"-out={disasmIlFileName} {asmDllFileName}"; + } + + public void Dispose() {} +} diff --git a/src/tests/ilasm/ilasm_tests.csproj b/src/tests/ilasm/ilasm_tests.csproj index 6e4d9a17c241af..b08c49dd105509 100644 --- a/src/tests/ilasm/ilasm_tests.csproj +++ b/src/tests/ilasm/ilasm_tests.csproj @@ -13,6 +13,7 @@ + @@ -68,6 +69,10 @@ Always %(Filename).il + + Always + %(Filename).il +