Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add long/ulong->float cast helpers - #114597

Merged
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt
May 22, 2025
Merged

Add long/ulong->float cast helpers#114597
tannergooding merged 8 commits into
dotnet:mainfrom
saucecontrol:lng2flt

Conversation

@saucecontrol

@saucecontrolsaucecontrol commented Apr 12, 2025

Copy link
Copy Markdown
Member

Resolves#106646

We currently have a few inconsistencies in the way long/ulong to float conversions are done in JIT.

Problem 1

On 64-bit platforms, long->float conversions have always been done directly with a native CPU instruction, while on 32-bit, long->float has always been morphed to long->double->float so that it could use CORINFO_HELP_LNG2DBL.

This can lead to different conversion results when the long->double conversion rounds in a different direction than long->float would. Ex: sharplab.

Similarly, ulong->float may yield a different result than ulong->double->float.

Problem 2

In CIL, there is no way to represent a conversion directly from unsigned to float or double. The IL instruction conv.r.un specifies unsigned conversion to IL type F which is of indeterminate precision. Consequently, managed language compilers emit a pair of instructions for these casts: conv.r.un; conv.r4 or conv.r.un; conv.r8.

The JIT importer has always treated conv.r.un as unsigned->double, so in the case of conv.r.un; conv.r8, the second (double->double) cast is skipped, and the intention of the managed compiler is preserved. conv.r.un; conv.r4, on the other hand, imports as unsigned->double->float.

As stated above, this could yield a different result than intended for ulong.

Problem 3

When Arm64 support was added to JIT, as an optimization to take advantage of the fact Arm64 has a direct ulong->float conversion instruction, recognition of the ulong->double->float pattern was added to JIT, and the intermediate cast was removed. This led to Arm64 having different cast behavior than all other platforms.

#84384 extended the same optimization to x64 in .NET 8, using AVX-512 instructions. This led to further fragmentation of behavior since the presence or absence of AVX-512 could change results.

#111595 unified the behavior on x64 by emulating the direct ulong->float cast using SSE instructions.

However, this still leaves problem 1 (32-bit still goes through CORINFO_HELP_ULNG2DBL) and introduces another: The intermediate double cast may have actually been intentional, and the current optimization removes it without knowing. i.e. conv.r.un; conv.r4 and conv.r.un; conv.r8; conv.r4 are treated the same.

The solution

This PR adds JIT helpers to perform long->float and ulong->float casts directly for 32-bit platforms.

It also modifies JIT to specifically look for the conv.r.un; conv.r4 sequence and import it as unsigned->float, and then calls the new helpers as appropriate.

Next steps

Because this requires a JIT-EE GUID change, I have broken the solution into two parts.

This part solves problems 1 and 2 by making sure all platforms can consistently recognize and perform the long/ulong->float casts directly.

I will address problem 3 in a followup PR, along with some more cleanup, optimization, and added tests. The current bad optimization also catches some redundant casts that will need to be handled differently, and it will be easier to see the impact of all those changes if the SPMI asmdiff jobs work.

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 12, 2025
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Apr 12, 2025
@AaronRobinsonMSFTAaronRobinsonMSFT added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 12, 2025
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

/cc @dotnet/jit-contrib

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@saucecontrol
saucecontrol marked this pull request as ready for review April 13, 2025 01:11
Comment threadsrc/coreclr/inc/readytorun.h Outdated
@saucecontrol

saucecontrol commented Apr 13, 2025

Copy link
Copy Markdown
MemberAuthor

jit-format is failing due to changes from #114525. cc @kunalspathak

(fixed in #114603)

@jkotas
jkotas requested a review from a teamApril 22, 2025 01:27
@jkotas

Copy link
Copy Markdown
Member

The VM side of the changes LGTM

@BruceForstall

Copy link
Copy Markdown
Contributor

@saucecontrol Please merge and resolve conflicts.

Comment threadsrc/coreclr/vm/jithelpers.cpp

@tannergoodingtannergooding left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC. @dotnet/jit-contrib, @jakobbotsch for secondary review

@EgorBoEgorBo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's just in case kick off a few outerloops

@EgorBo

Copy link
Copy Markdown
Member

/azp run runtime-coreclr jitstress-isas-x86, runtime-coreclr jitstress-isas-arm, Fuzzlyn, runtime-coreclr jitstress-isas-avx512

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 4 pipeline(s).

@tannergooding

Copy link
Copy Markdown
Member

Failures are unrelated. Logged issues for the new ones Fuzzlyn discovered

@tannergooding
tannergooding merged commit f10d4c3 into dotnet:mainMay 22, 2025
@saucecontrol
saucecontrol deleted the lng2flt branch May 22, 2025 15:59
SimaTian pushed a commit that referenced this pull request May 27, 2025
* add long/ulong->float cast helpers
* fix const folding
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 22, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: Model Int64/UInt64 -> Single casts without intermediate cast to Double

7 participants

@saucecontrol@AaronRobinsonMSFT@jkotas@BruceForstall@EgorBo@tannergooding@am11