') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); JIT: Enable inlining of runtime async methods without awaits by jakobbotsch · Pull Request #124183 · dotnet/runtime · GitHub
Skip to content

JIT: Enable inlining of runtime async methods without awaits - #124183

Merged
jakobbotsch merged 6 commits into
dotnet:mainfrom
jakobbotsch:async-inlining
Feb 10, 2026
Merged

JIT: Enable inlining of runtime async methods without awaits#124183
jakobbotsch merged 6 commits into
dotnet:mainfrom
jakobbotsch:async-inlining

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

While inlining runtime async methods in general is tricky, it is relatively simple to allow inlining async methods that do not have any awaits in them, and I suspect this is a large fraction of them.

While inlining runtime async methods in general is tricky, it is
relatively simple to allow inlining async methods that do not have any
awaits in them, and I suspect this is a large fraction of them.
CopilotAI review requested due to automatic review settings February 9, 2026 16:47
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 9, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Enables inlining of “runtime async” methods in the JIT when the async method body has no awaits, by rejecting inline candidates that require async call setup while adjusting async-context save/restore and EH sizing behavior.

Changes:

  • Add a new fatal inline observation for detecting awaits during inlinee import.
  • Abort inlining when async call setup is encountered while importing an inlinee.
  • Adjust EH table sizing and async-context save/restore behavior to support the new inlining scenario.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
src/coreclr/jit/inline.defAdds callee “await” observation; removes callsite continuation-handling observation.
src/coreclr/jit/importercalls.cppMarks inlinees as non-inlineable when async call setup is detected; removes prior callsite continuation-handling inline ban.
src/coreclr/jit/fgbasic.cppAccounts for an extra EH clause when async context save/restore is enabled.
src/coreclr/jit/async.cppTweaks async-context logic for inlining mode (resumed computation, arg injection, return merging).

Comment threadsrc/coreclr/jit/async.cpp
Comment threadsrc/coreclr/jit/async.cpp
Comment threadsrc/coreclr/jit/fgbasic.cpp
Comment threadsrc/coreclr/jit/importercalls.cpp
@jakobbotsch
jakobbotsch marked this pull request as ready for review February 10, 2026 15:52
CopilotAI review requested due to automatic review settings February 10, 2026 15:52
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

cc @dotnet/jit-contrib PTAL @AndyAyersMS

One thing this doesn't handle is multi-level inlinees where we eventually end up with no awaits. Not sure if that generalization would be simple, however.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/jit/fgbasic.cpp

@AndyAyersMSAndyAyersMS 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.

Didn't see anything other than what copilot saw.

CopilotAI review requested due to automatic review settings February 10, 2026 16:44

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@jakobbotsch
jakobbotsch merged commit 0b91113 into dotnet:mainFeb 10, 2026
124 of 132 checks passed
@jakobbotsch
jakobbotsch deleted the async-inlining branch February 10, 2026 22:22
@drieseng

Copy link
Copy Markdown
Contributor

@jakobbotsch, why do you think a "large fraction" of async methods do not have an await? Such methods would result in a CS1998 warning: "This async method lacks 'await' operators and will run synchronously."

Do you want to optimise for "bad" code? If the cost of this optimization is negligible, then this doesn't matter of course.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

@jakobbotsch, why do you think a "large fraction" of async methods do not have an await? Such methods would result in a CS1998 warning: "This async method lacks 'await' operators and will run synchronously."

Do you want to optimise for "bad" code? If the cost of this optimization is negligible, then this doesn't matter of course.

I suspect that a large fraction of async functions that we would inline do not have await. Generally successful inlinees are smaller and more likely to be leaf methods than other methods. But yes, @stephentoub pointed CS1998 out too (although we are removing this warning) and it is very possible it means my intuition about inlinees does not carry over to async methods.

@drieseng

Copy link
Copy Markdown
Contributor

What is the rationale behind removing CS1998? Just curious.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

What is the rationale behind removing CS1998? Just curious.

You can see the related discussion in dotnet/roslyn#77001. From a runtime async perspective it is no longer more expensive to have these methods be async, and in fact will often be a deoptimization to not make them async since the alternatives like Task.FromResult can be more expensive than runtime async. Also see #115771.

@stephentoub

Copy link
Copy Markdown
Member

What is the rationale behind removing CS1998? Just curious.

You can see the related discussion in dotnet/roslyn#77001. From a runtime async perspective it is no longer more expensive to have these methods be async, and in fact will often be a deoptimization to not make them async since the alternatives like Task.FromResult can be more expensive than runtime async. Also see #115771.

From a language perspective, the cure was worse than the disease. Even before runtime async, folks would get the warning and then bend over backwards to try to avoid it, often making things words, when the right answer was just generally just to use async.

iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…124183)
While inlining runtime async methods in general is tricky, it is
relatively simple to allow inlining async methods that do not have any
awaits in them, and I suspect this is a large fraction of them.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 14, 2026
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 SuperPMIruntime-async

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jakobbotsch@drieseng@stephentoub@AndyAyersMS