Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/coreclr/ildasm/dasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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))
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/ildasm/dis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
}
1 change: 1 addition & 0 deletions src/coreclr/ildasm/dis.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ char* DumpUnicodeString(void* GUICookie,
void TokenSigInit(IMDInternalImport *pImport);
void TokenSigDelete();
bool IsSpecialNumber(const char*);
void NormalizeFloatString(char*);


//---------------- see DMAN.CPP--------------------------------------------------
Expand Down
28 changes: 28 additions & 0 deletions src/tests/ilasm/FloatSpecialValues/FloatSpecialValues.il
Original file line number Diff line number Diff line change
@@ -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
}
}
117 changes: 117 additions & 0 deletions src/tests/ilasm/FloatSpecialValues/FloatSpecialValuesTests.cs
Original file line number Diff line number Diff line change
@@ -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() {}
}
5 changes: 5 additions & 0 deletions src/tests/ilasm/ilasm_tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="PortablePdb\IlasmPortablePdbTesterCommon.cs" />
<Compile Include="PortablePdb\IlasmPortablePdbTesterTypes.cs" />
<Compile Include="MethodImplOptions\MethodImplOptionsTests.cs" />
<Compile Include="FloatSpecialValues\FloatSpecialValuesTests.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="PortablePdb\Resources\**" />
Expand Down Expand Up @@ -68,6 +69,10 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Link>%(Filename).il</Link>
</None>
<None Include="FloatSpecialValues/*.il">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Link>%(Filename).il</Link>
</None>
</ItemGroup>
<Import Project="$(TestSourceDir)MergedTestRunner.targets" />
</Project>
Loading